Scala: How can I get an escaped representation of

2019-03-09 18:53发布

问题:

Basically, what I'd like to do is have:

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

Do this:

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

Basically looking for an analog of ruby's String#inspect or haskell's show :: String -> String.

回答1:

This question is a bit old but I stumbled over it while searching for a solution myself and was dissatisfied with the other answers because they either are not safe (replacing stuff yourself) or require an external library.

I found a way to get the escaped representation of a string with the scala standard library (>2.10.0) which is safe. It uses a little trick:

Through runtime reflection you can can easily obtain a representation of a literal string expression. The tree of such an expression is returned as (almost) scala code when calling it's toString method. This especially means that the literal is represented the way it would be in code, i.e. escaped and double quoted.

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

The escape function therefore results in the desired code-representation of the provided raw string (including the surrounding double quotes):

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

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

This solution is admittedly abusing the reflection api but IMHO still safer and more maintainable than the other proposed solutions.



回答2:

I'm pretty sure this isn't available in the standard libraries for either Scala or Java, but it is 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

You could easily add the quotation marks to the escaped string if you wanted, of course.



回答3:

The scala.reflect solution actually works fine. When you do not want to pull in that whole library, this is what it seems to do under the hood (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


回答4:

If I compile these:

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

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

I don't find a difference in the String, so I guess: There is no possibility, to find out, whether there was was a "\n" in the source.

But maybe I got you wrong, and you would like to get the same result for both?

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

The second possibility is:

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"""


回答5:

You could build your own function pretty easily, if you don't want to use the apache library:

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 \\