2012-03-28 11 views

Odpowiedz

18

to pytanie jest nieco stary, ale potknął się o niego podczas poszukiwania rozwiązania siebie i był niezadowolony z innych odpowiedzi, ponieważ nie są one bezpieczne (zastępując rzeczy samodzielnie) lub wymagają zewnętrznej biblioteki.

Znalazłem sposób, aby uzyskać reprezentację escaped ciągu znaków z biblioteką standardową scala (> 2.10.0), która jest bezpieczna. Wykorzystuje małą sztuczkę:

Poprzez odbicie w czasie wykonywania można łatwo uzyskać reprezentację dosłownego wyrażenia ciągu. Drzewo takiego wyrażenia jest zwracane jako (prawie) kod scala, gdy wywołuje się metodę toString. Oznacza to w szczególności, że literał jest reprezentowany tak, jak w kodzie, tzn. Jest unikany i podwójnie cytowany.

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

Funkcja escape powoduje zatem żądany kod reprezentacji dostarczonego surowca łańcucha (w tym otaczających cudzysłowach)

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

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

Rozwiązanie to jest wprawdzie nadużywanie API odbicia, ale IMHO bezpieczniejsza i łatwiejsze do utrzymania niż inne proponowane rozwiązania.

+0

Czy wiesz, jak korzystać z funkcji unescape przy odbiciu? – Renkai

+1

@Renkai nie potrzebujesz do tego refleksji. Możesz użyć StringContext: 'StringContext.treatEscapes (res2)'. Pozostawia podwójny cudzysłów chociaż –

+1

'StringContext.treatEscapes' działa odwrotnie: konwertuje" \\ t "na" \ t ". –

2

Gdybym skompilować te:

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

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

nie znajdę różnicę w ciągu, więc myślę: Nie ma możliwości, aby dowiedzieć się, czy nie było był w „\ n” źródło.

Ale może się myliłem i chciałbyś uzyskać taki sam wynik dla obu?

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

Drugą możliwością jest:

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

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

Test:

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) Poszukuję funkcji, która wymyka się z ciągu znaków, gdzie usunięcie ciągu znaków obejmuje przekształcenie wszystkich "\ n" na "\\ n", "\ t" na "\ \ t ", itp., i ewentualnie dołączając wynik do podwójnych cudzysłowów (w zależności od języka). – rampion

+0

Podczas kompilacji przynajmniej \ n jest przekształcane na 0A (jeśli przypadkiem obejrzysz hekaditora). Założę się, że zobaczysz odpowiednik \ t - taki sam, jak gdybyś użył karty z klawiszem Tab. Okay - potrzebujesz '" 'przed i za String, dodam to do mojej odpowiedzi –

+0

' maska' może być napisana jako '" \ "" * 3' – sschaef

19

Jestem całkiem pewien, że to nie jest dostępne w standardowych bibliotek dla obu Scala lub Java, ale jest w 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 

Możesz łatwo dodać cudzysłowy do łańcucha znaków, jeśli chcesz, oczywiście.

2

można zbudować własną funkcję dość łatwo, jeśli nie chcesz korzystać z biblioteki 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

nie zapomnij o niezdatnym do druku ascii, i unicode ... – rampion

+0

Dzięki - zapomniałem o tych, te są trudniejsze, aby po prostu "wymienić" z powrotem, również. Zobaczę, czy uda mi się to rozgryźć później. –

+2

Chodzi mi o to, że jest w tym wiele przypadków skrajnych i wolałbym używać dobrze przetestowanej biblioteki niż własnej. – rampion

5

Rozwiązanie scala.reflect faktycznie działa dobrze. Kiedy nie chcesz wciągnąć całej biblioteki, wydaje się, że robi to pod maską (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 
Powiązane problemy