解码 Scala 问题中带有转义特殊字符的字符串



我有一个多行 JSON 文件,其中包含包含编码为十六进制的特殊字符的记录。下面是单个 JSON 记录的示例:

{x22valuex22:x22xC4xB1arines BintxC4xB1xC3xA7 RamuxC3xA7larx22}

这个记录应该是{"value":"ıarines Bintıç Ramuçlar"}的,例如'"字符被替换为相应的十六进制\x22,其他特殊的Unicode字符被替换为一个或两个十六进制(例如\xC3\xA7编码ç等(。

我需要在 Scala 中将类似的字符串转换为常规的 Unicode 字符串,因此在打印时,它产生的{"value":"ıarines Bintıç Ramuçlar"}没有十六进制。

在 Python 中,我可以用一行代码轻松解码这些记录:

>>> a = "{x22valuex22:x22xC4xB1arines BintxC4xB1xC3xA7 RamuxC3xA7larx22}"
>>> a.decode("utf-8")
u'{"value":"u0131arines Bintu0131xe7 Ramuxe7lar"}'
>>> print a.decode("utf-8")
{"value":"ıarines Bintıç Ramuçlar"}

但是在Scala中,我找不到解码它的方法。我尝试像这样转换它但没有成功:

scala> val a = """{x22valuex22:x22xC4xB1arines BintxC4xB1xC3xA7 RamuxC3xA7larx22}"""
scala> print(new String(a.getBytes(), "UTF-8"))
{x22valuex22:x22xC4xB1arines BintxC4xB1xC3xA7 RamuxC3xA7larx22}

我也尝试了URLDecoder,因为我在类似问题的解决方案中找到了(但使用URL(:

scala> val a = """{x22valuex22:x22xC4xB1arines BintxC4xB1xC3xA7 RamuxC3xA7larx22}"""
scala> print(java.net.URLDecoder.decode(a.replace("\x", "%"), "UTF-8"))
{"value":"ıarines Bintıç Ramuçlar"}

它为此示例生成了所需的结果,但对于通用文本字段似乎不安全,因为它旨在使用 URL,并且需要替换字符串中%的所有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, "")
}

每个x??编码一个字节,就像x22编码"x5C编码一样。但是在 UTF-8 中,某些字符使用多个字节进行编码,因此您需要将xC4xB1转换为ı符号,依此类推。

replaceAllIn真的很好,但它可能会吃掉你的斜杠。因此,如果不在替换的字符串中使用组(如1(,建议使用quoteReplacement来转义$符号。

/** "22" -> 34, "AA" -> -86  */
def hex2byte(hex: String) = Integer.parseInt(hex, 16).toByte
/** decode strings like x22 or xC4xB1xC3xA7 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)))

附言有谁知道java.util.regex.Matcher.quoteReplacementscala.util.matching.Regex.quoteReplacement之间的区别?

问题是编码确实特定于python(我认为(。像这样的东西可能会起作用:

val s = """{x22valuex22:x22xC4xB1arines BintxC4xB1xC3xA7 RamuxC3xA7larx22}"""
"""\x([A-F0-9]{2})""".r.replaceAllIn(s, (x: Regex.Match) => 
new String(BigInt(x.group(1), 16).toByteArray, "UTF-8")
)

相关内容

  • 没有找到相关文章

最新更新