将未转义的unicode转换为utf8整数

时间:2017-02-02 21:57:18

标签: string haskell unicode utf-8

首先,如果条款" unescaped unicode"和" utf8整数"不正确;当我谈论编码时,我真的不知道我在谈论什么。

作为具体示例,我想将字符串"\\u00b5ABC"转换为字符串"\181ABC"\u00b5\181对应µ)。通过" string"我的意思是StringText

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

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

我准备打赌存在更直接的方式......

修改

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

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

1 个答案:

答案 0 :(得分:2)

这是一个使用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并不是那么糟糕,所以让我们#39; s那样做:

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

无论如何,一旦我们有一个用于解码单个转义字符的正则表达式,就可以很容易地生成一个正则表达式来解码所有转义字符并通过未更改的字符传递未转义字符:many (escaped <|> anySym)。由于此正则表达式将始终成功,我们可以忽略Maybe - (=~) - 对照表达式是否匹配的对冲,并写入

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

让我们在ghci中尝试:

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

像这样编写我们自己的解析器而不依赖于decode之类的东西的好处是,我们可以获得对正在进行的转换的控制和信心;例如,因为我们知道\u将始终跟随四个十六进制数字,我们只能在发生这种情况时对其进行转换,以防原始的Javascript前文本包含{{1}我们希望它出现在最终输出中,而不是\\udefg;而且我们不必担心它会试图逃避我们不希望它做的其他事情;而且我们没有必要&#34;额外逃脱&#34;在我们把它关掉之前我们的字符串,就像你在它周围添加额外的引号一样。当然,缺点是我们必须自己设计它,并且可能对它的正确性缺乏信心,因为它还没有被一千个用户强化!