Reputation: 89123
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
.
Upvotes: 26
Views: 21008
Reputation: 1240
Maybe there is a cleaner way to implement this in Scala 3, but this is what I've come up with following @martin-ring approach:
import scala.quoted.*
inline def escape(inline raw: String): String = ${escapeImpl('{raw})}
def escapeImpl(raw: Expr[String])(using Quotes): Expr[String] =
import quotes.reflect.*
Literal(StringConstant(raw.show)).asExprOf[String]
Upvotes: 2
Reputation: 67330
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
Upvotes: 12
Reputation: 5426
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.
Upvotes: 29
Reputation: 33175
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 \\
Upvotes: 2
Reputation: 36269
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"""
Upvotes: 2
Reputation: 139058
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.
Upvotes: 26