【问题标题】:Convert unescaped unicode to utf8 integer将未转义的 unicode 转换为 utf8 整数
【发布时间】:2017-06-20 03:58:15
【问题描述】:

首先,如果术语“未转义的 unicode”和“utf8 整数”不正确,我深表歉意;当我在谈论编码时,我真的不知道我在说什么。

作为一个具体的例子,我想将字符串"\\u00b5ABC"转换为字符串"\181ABC"\u00b5\181对应µ)。 “字符串”是指StringText

我知道如何通过曲折(也许是可笑)的方式来实现这一点:

import Data.Aeson (decode)
import Data.ByteString.Lazy (packChars)
import Data.Text (Text)
decode (packChars "\"\\u00b5ABC\"") :: Maybe Text

我敢打赌,还有更直接的方法……

编辑

根据@Alec 的评论,我提供了更多背景信息。在后台,有一个 Javascript 程序接收一个字符串,并将该字符串中的字符替换为它们的 unicode 表示 \\uxxxx当这个 unicode 表示介于 \u007F\uFFFF 之间时。

在 Haskell 方面,我收到了这个新字符串,我想用它们对应的 utf8 整数表示来替换 \\uxxxx

【问题讨论】:

  • 我觉得您的问题有点未指定-您希望以\... 的形式打印哪些字符,以及您希望按字面意思打印哪些字符?除了这个问题,这是一个相当简单的问题。
  • 老实说,您的代码似乎是最好的方法。在您给出的示例中,如果您打开 -XOverloadedStrings,则可以省略对 packChars 的调用
  • @Alec 谢谢你的评论。 \u... 字符来自 Javascript 程序。据我了解,它会生成从\u007F\uFFFF 的这些字符。具体来说,我“接收”一个包含文字字符和 \u... 字符的字符串,我想替换 \u.... 字符。
  • @haoformayor 令人惊讶。这看起来像用锤子杀死一只苍蝇:)

标签: string haskell unicode utf-8


【解决方案1】:

这是一个使用regex-applicative 编写的漂亮的简单解析器。首先是一些不值得阅读的导入和其他废话:

import Data.Char
import Data.Maybe
import Numeric
import Text.Regex.Applicative

-- no idea why this isn't in Control.Applicative
replicateA :: Applicative f => Int -> f a -> f [a]
replicateA n act = sequenceA (replicate n act)

现在,我们要解析一个转义字符。我们将使用匹配字符并返回字符的正则表达式,因此它是RE Char Char。理想情况下,我会这样写:

escaped :: RE Char Char
escaped = do
    string "\\u"
    digits <- replicateM 4 (psym isHexDigit)
    return . chr . fst . head . readHex $ digits

head 是安全的,因为我们确保 readHex 只会传递十六进制数字,因此会成功。我们几乎可以这样写,只是RE Char 不是Monad。使用新的 GHC,您可能可以打开 ApplicativeDo 并完成它,但无论如何自己编写应用程序风格并支持所有 GHC 也不错,所以让我们这样做:

escaped :: RE Char Char
escaped
    =   chr . fst . head . readHex
    <$> (string "\\u"
     *>  replicateA 4 (psym isHexDigit)
        )

无论如何,一旦我们有了一个用于解码单个转义字符的正则表达式,就很容易生成一个正则表达式来解码所有转义字符并将未转义字符原样传递:many (escaped &lt;|&gt; anySym)。由于这个正则表达式总是会成功,我们可以忽略(=~)Maybe-ness 对冲它关于表达式是否匹配的赌注,并编写

decodeHex :: String -> String
decodeHex = fromJust . (=~ many (escaped <|> anySym))

让我们在 ghci 中尝试一下:

> decodeHex "\\u00b5ABC"
"\181ABC"
> decodeHex "\\u00bABC"
"\186BC"
> decodeHex "\\udefg"
"\\udefg"

像这样编写我们自己的解析器而不是依赖于decode 之类的东西的好处是我们可以控制并确定正在执行哪些转换;例如,因为我们知道\u 后面总是跟着四个 十六进制数字,所以我们只能在发生这种情况时对其进行转换,以防原始的、pre-Javascript 文本包含 \\udefg 并且我们想要出现在最终输出中,而不是\3567g;我们不必担心它会试图逃避我们不希望它做的其他事情;我们也不必在传递它之前“额外转义”我们的字符串,就像在它周围添加额外的引号一样。当然,缺点是我们必须自己设计它,并且可能对其正确性缺乏信心,因为它还没有经过一千个用户的实战!

【讨论】:

  • replicateA 存在于...在Control.Monad...中作为replicateM。但它的约束仍然是Applicative
  • @Alec 这太可怕了。看来,我们还有相当多的 AMP 影响需要清理。感谢您指出。
  • 谢谢丹尼尔。我会在这个周末或下周尝试。如果我不快速投票或接受答案,请不要担心。这听起来很有启发性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
  • 1970-01-01
  • 1970-01-01
  • 2021-01-28
  • 2021-12-26
  • 1970-01-01
相关资源
最近更新 更多