2012-03-28 11 views
22

In sostanza, quello che vorrei fare è di avere:Scala: come posso ottenere una rappresentazione di una stringa con escape?

// in foo.scala 
val string = "this is a string\nover two lines" 
println(string) 
println(foo(string)) 

fare questo:

% scala foo.scala 
this is a string 
over two lines 
"this is a string\nover two lines" 

Fondamentalmente alla ricerca di un analogo di String#inspect di Ruby o Haskell di show :: String -> String.

risposta

18

Questa domanda è un po 'vecchio, ma mi sono imbattuto su di esso durante la ricerca di una soluzione e di me stesso era insoddisfatto delle altre risposte perché non sono sicure (sostituendo te stesso) o richiedono una libreria esterna.

Ho trovato un modo per ottenere la rappresentazione di una stringa con la scala standard library (> 2.10.0) che è sicura. Utilizza un piccolo trucco:

Attraverso il riflesso in fase di esecuzione è possibile ottenere facilmente una rappresentazione di un'espressione stringa letterale. L'albero di tale espressione viene restituito come (quasi) codice scala quando si chiama il metodo toString. Questo in particolare significa che il letterale è rappresentato come sarebbe nel codice, cioè sfuggito e citato in due.

def escape(raw: String): String = { 
    import scala.reflect.runtime.universe._ 
    Literal(Constant(raw)).toString 
} 

La funzione escape traduce quindi in code-rappresentazione desiderata della stringa prima fornita (compresi circostanti virgolette):

scala> "\bHallo" + '\n' + "\tWelt" 
res1: String = 
?Hallo 
     Welt 

scala> escape("\bHallo" + '\n' + "\tWelt") 
res2: String = "\bHallo\n\tWelt" 

Questa soluzione è certamente abusa l'API Reflection ma IMHO ancora più sicuro e più manutenibile rispetto alle altre soluzioni proposte.

+0

Sai come scansionare l'uso con la riflessione? – Renkai

+1

@Renkai non hai bisogno di riflessione per questo. È possibile utilizzare StringContext: 'StringContext.treatEscapes (res2)'. Lascia le virgolette anche se –

+1

'StringContext.treatEscapes 'fa il contrario: converte" \\ t "in" \ t ". –

2

Se compilo questi:

object s1 { 
val s1 = "this is a string\nover two lines" 
} 

object s2 { 
val s2 = """this is a string 
over two lines""" 
} 

non trovo una differenza nella stringa, quindi credo che: Non c'è alcuna possibilità, per scoprire, se ci fosse stato un "\ n" in la fonte.

Ma forse ti ho sbagliato e ti piacerebbe ottenere lo stesso risultato per entrambi?

"\"" + s.replaceAll ("\\n", "\\\\n").replaceAll ("\\t", "\\\\t") + "\"" 

La seconda possibilità è:

val mask = Array.fill (3)('"').mkString 
mask + s + mask 

res5: java.lang.String = 
"""a 
b""" 

prova:

scala> val s = "a\n\tb" 
s: java.lang.String = 
a 
    b 

scala>  "\"" + s.replaceAll ("\\n", "\\\\n").replaceAll ("\\t", "\\\\t") + "\"" 
res7: java.lang.String = "a\n\tb" 

scala> mask + s + mask 
res8: java.lang.String = 
"""a 
    b""" 
+0

(U) Sto cercando una funzione che sfugge una stringa, in cui sfuggire una stringa coinvolge trasformare tutti i "\ n" a "\\ n", "\ t" a "\ \ t ", ecc. e possibilmente racchiudendo il risultato tra virgolette (a seconda della lingua). – rampion

+0

Nella compilazione, almeno il \ n viene trasformato in uno 0A (se ti capita di guardare attraverso un hexeditor). Scommetto che vedrai un equivalente per \ t - come se avessi usato una scheda con il tasto Tab-Key. Okay - hai bisogno di un '' 'prima e dietro lo String; lo aggiungerò alla mia risposta. –

+0

' mask' può essere scritto come '" \ "" * 3' – sschaef

19

Sono abbastanza sicuro che questo non è disponibile nelle librerie standard sia per Scala o Java, ma è in Apache Commons Lang:

scala> import org.apache.commons.lang.StringEscapeUtils.escapeJava 
import org.apache.commons.lang.StringEscapeUtils.escapeJava 

scala> escapeJava("this is a string\nover two lines") 
res1: java.lang.String = this is a string\nover two lines 

Si potrebbe facilmente aggiungere le virgolette alla stringa di escape se lo si desidera, naturalmente.

2

Si potrebbe costruire una propria funzione abbastanza facilmente, se non si desidera utilizzare la libreria Apache:

scala> var str = "this is a string\b with some \n escapes \t so we can \r \f \' \" see how they work \\"; 
str: java.lang.String = 
this is a string? with some 
escapes  so we can 
' " see how they work \ 

scala> print(str.replace("\\","\\\\").replace("\n","\\n").replace("\b","\\b").replace("\r","\\r").replace("\t","\\t").replace("\'","\\'").replace("\f","\\f").replace("\"","\\\"")); 
this is a string\b with some \n escapes \t so we can \r \f \' \" see how they work \\ 
+0

non dimenticare l'ascii non stampabile, e unicode ... – rampion

+0

Grazie - dimenticato di quelli, quelli sono più difficili da "rimpiazzare" indietro, anche. Vedrò se riesco a capirlo più tardi. –

+2

il mio punto di vista è che ci sono molti casi limite con questo, e preferisco usare una libreria ben testata di quella mia. – rampion

5

La soluzione scala.reflect funziona correttamente. Quando non vuoi entrare in quell'intera libreria, questo è quello che sembra fare sotto i baffi (Scala 2.11):

def quote (s: String): String = "\"" + escape(s) + "\"" 
def escape(s: String): String = s.flatMap(escapedChar) 

def escapedChar(ch: Char): String = ch match { 
    case '\b' => "\\b" 
    case '\t' => "\\t" 
    case '\n' => "\\n" 
    case '\f' => "\\f" 
    case '\r' => "\\r" 
    case '"' => "\\\"" 
    case '\'' => "\\\'" 
    case '\\' => "\\\\" 
    case _ => if (ch.isControl) "\\0" + Integer.toOctalString(ch.toInt) 
       else    String.valueOf(ch) 
} 

val string = "\"this\" is a string\nover two lines" 
println(quote(string)) // ok 
Problemi correlati