在 Scala 问题中解码带有转义特殊字符的字符串
Decoding a String with escaped special characters in Scala issue
我有一个多行 JSON 文件,其中的记录包含编码为十六进制的特殊字符。这是单个 JSON 记录的示例:
{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}
这条记录应该是 {"value":"ıarines Bintıç Ramuçlar"}
,例如'"' 字符替换为相应的十六进制 \x22,其他特殊 Unicode 字符替换为一个或两个十六进制(例如 \xC3\xA7 编码 ç 等)
我需要在 Scala 中将类似的字符串转换为常规的 Unicode 字符串,因此在打印时它生成 {"value":"ıarines Bintıç Ramuçlar"}
没有十六进制。
在Python我可以用一行代码轻松解码这些记录:
>>> a = "{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"
>>> a.decode("utf-8")
u'{"value":"\u0131arines Bint\u0131\xe7 Ramu\xe7lar"}'
>>> print a.decode("utf-8")
{"value":"ıarines Bintıç Ramuçlar"}
但是在 Scala 中我找不到解码它的方法。我尝试像这样转换它但没有成功:
scala> val a = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
scala> print(new String(a.getBytes(), "UTF-8"))
{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}
我也尝试了 URL解码器,因为我在类似问题的解决方案中找到了它(但使用 URL):
scala> val a = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
scala> print(java.net.URLDecoder.decode(a.replace("\x", "%"), "UTF-8"))
{"value":"ıarines Bintıç Ramuçlar"}
它为这个例子产生了预期的结果,但对于通用文本字段似乎不安全,因为它设计用于 URLs 并且需要将所有 \x
替换为 %
字符串。
Scala 有没有更好的方法来处理这个问题?
我是 Scala 的新手,非常感谢您的帮助
更新:
我用 javax.xml.bind.DatatypeConverter.parseHexBinary
制作了一个自定义解决方案。它现在可以工作,但它看起来很麻烦而且一点也不优雅。我认为应该有更简单的方法来做到这一点。
代码如下:
import javax.xml.bind.DatatypeConverter
import scala.annotation.tailrec
import scala.util.matching.Regex
def decodeHexChars(string: String): String = {
val regexHex: Regex = """\A\[xX]([0-9a-fA-F]{1,2})(.*)""".r
def purgeBuffer(buffer: String, acc: List[Char]): List[Char] = {
if (buffer.isEmpty) acc
else new String(DatatypeConverter.parseHexBinary(buffer)).reverse.toList ::: acc
}
@tailrec
def traverse(s: String, acc: List[Char], buffer: String): String = s match {
case "" =>
val accUpdated = purgeBuffer(buffer, acc)
accUpdated.foldRight("")((str, b) => b + str)
case regexHex(chars, suffix) =>
traverse(suffix, acc, buffer + chars)
case _ =>
val accUpdated = purgeBuffer(buffer, acc)
traverse(s.tail, s.head :: accUpdated, "")
}
traverse(string, Nil, "")
}
问题是编码确实特定于 python(我认为)。这样的事情可能会奏效:
val s = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
"""\x([A-F0-9]{2})""".r.replaceAllIn(s, (x: Regex.Match) =>
new String(BigInt(x.group(1), 16).toByteArray, "UTF-8")
)
每个\x??
编码一个字节,就像\x22
编码"
和\x5C
编码\
。但是在UTF-8中有些字符是使用多个字节编码的,所以你需要将\xC4\xB1
转换为ı
符号等等。
replaceAllIn
确实不错,但它可能会吃掉你的斜杠。因此,如果您不在替换字符串中使用组(如 </code>),建议使用 <code>quoteReplacement
转义 \
和 $
符号。
/** "22" -> 34, "AA" -> -86 */
def hex2byte(hex: String) = Integer.parseInt(hex, 16).toByte
/** decode strings like \x22 or \xC4\xB1\xC3\xA7 to specified encoding */
def decodeHexadecimals(str: String, encoding: String="UTF-8") =
new String(str.split("""\x""").tail.map(hex2byte), encoding)
/** fix weird strings */
def replaceHexadecimals(str: String, encoding: String="UTF-8") =
"""(\x[\dA-F]{2})+""".r.replaceAllIn(str, m =>
util.matching.Regex.quoteReplacement(
decodeHexadecimals(m.group(0), encoding)))
P.S。有谁知道java.util.regex.Matcher.quoteReplacement
和scala.util.matching.Regex.quoteReplacement
的区别吗?
我有一个多行 JSON 文件,其中的记录包含编码为十六进制的特殊字符。这是单个 JSON 记录的示例:
{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}
这条记录应该是 {"value":"ıarines Bintıç Ramuçlar"}
,例如'"' 字符替换为相应的十六进制 \x22,其他特殊 Unicode 字符替换为一个或两个十六进制(例如 \xC3\xA7 编码 ç 等)
我需要在 Scala 中将类似的字符串转换为常规的 Unicode 字符串,因此在打印时它生成 {"value":"ıarines Bintıç Ramuçlar"}
没有十六进制。
在Python我可以用一行代码轻松解码这些记录:
>>> a = "{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"
>>> a.decode("utf-8")
u'{"value":"\u0131arines Bint\u0131\xe7 Ramu\xe7lar"}'
>>> print a.decode("utf-8")
{"value":"ıarines Bintıç Ramuçlar"}
但是在 Scala 中我找不到解码它的方法。我尝试像这样转换它但没有成功:
scala> val a = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
scala> print(new String(a.getBytes(), "UTF-8"))
{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}
我也尝试了 URL解码器,因为我在类似问题的解决方案中找到了它(但使用 URL):
scala> val a = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
scala> print(java.net.URLDecoder.decode(a.replace("\x", "%"), "UTF-8"))
{"value":"ıarines Bintıç Ramuçlar"}
它为这个例子产生了预期的结果,但对于通用文本字段似乎不安全,因为它设计用于 URLs 并且需要将所有 \x
替换为 %
字符串。
Scala 有没有更好的方法来处理这个问题?
我是 Scala 的新手,非常感谢您的帮助
更新:
我用 javax.xml.bind.DatatypeConverter.parseHexBinary
制作了一个自定义解决方案。它现在可以工作,但它看起来很麻烦而且一点也不优雅。我认为应该有更简单的方法来做到这一点。
代码如下:
import javax.xml.bind.DatatypeConverter
import scala.annotation.tailrec
import scala.util.matching.Regex
def decodeHexChars(string: String): String = {
val regexHex: Regex = """\A\[xX]([0-9a-fA-F]{1,2})(.*)""".r
def purgeBuffer(buffer: String, acc: List[Char]): List[Char] = {
if (buffer.isEmpty) acc
else new String(DatatypeConverter.parseHexBinary(buffer)).reverse.toList ::: acc
}
@tailrec
def traverse(s: String, acc: List[Char], buffer: String): String = s match {
case "" =>
val accUpdated = purgeBuffer(buffer, acc)
accUpdated.foldRight("")((str, b) => b + str)
case regexHex(chars, suffix) =>
traverse(suffix, acc, buffer + chars)
case _ =>
val accUpdated = purgeBuffer(buffer, acc)
traverse(s.tail, s.head :: accUpdated, "")
}
traverse(string, Nil, "")
}
问题是编码确实特定于 python(我认为)。这样的事情可能会奏效:
val s = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
"""\x([A-F0-9]{2})""".r.replaceAllIn(s, (x: Regex.Match) =>
new String(BigInt(x.group(1), 16).toByteArray, "UTF-8")
)
每个\x??
编码一个字节,就像\x22
编码"
和\x5C
编码\
。但是在UTF-8中有些字符是使用多个字节编码的,所以你需要将\xC4\xB1
转换为ı
符号等等。
replaceAllIn
确实不错,但它可能会吃掉你的斜杠。因此,如果您不在替换字符串中使用组(如 </code>),建议使用 <code>quoteReplacement
转义 \
和 $
符号。
/** "22" -> 34, "AA" -> -86 */
def hex2byte(hex: String) = Integer.parseInt(hex, 16).toByte
/** decode strings like \x22 or \xC4\xB1\xC3\xA7 to specified encoding */
def decodeHexadecimals(str: String, encoding: String="UTF-8") =
new String(str.split("""\x""").tail.map(hex2byte), encoding)
/** fix weird strings */
def replaceHexadecimals(str: String, encoding: String="UTF-8") =
"""(\x[\dA-F]{2})+""".r.replaceAllIn(str, m =>
util.matching.Regex.quoteReplacement(
decodeHexadecimals(m.group(0), encoding)))
P.S。有谁知道java.util.regex.Matcher.quoteReplacement
和scala.util.matching.Regex.quoteReplacement
的区别吗?