Sté*_*ent 7 string unicode haskell utf-8
首先,如果术语"未转义的unicode"和"utf8整数"不正确,我深表歉意; 当我谈论编码时,我真的不知道我在说什么.
作为一个具体的例子,我想将字符串转换"\\u00b5ABC"为字符串"\181ABC"(\u00b5并\181对应于µ).用"字符串"我的意思是String或Text.
我知道如何通过使用曲折(也许是可笑的)方式实现这一目标:
import Data.Aeson (decode)
import Data.ByteString.Lazy (packChars)
import Data.Text (Text)
decode (packChars "\"\\u00b5ABC\"") :: Maybe Text
Run Code Online (Sandbox Code Playgroud)
我准备打赌存在一种更直接的方式......
根据@ Alec的评论,我提供了更多背景信息.在后台,有一个Javascript程序接收一个字符串,\\uxxxx 当这个unicode表示位于\u007F和之间时,\uFFFF用它们的unicode表示替换该字符串中的字符.
在Haskell方面,我收到这个新字符串,我想\\uxxxx用相应的utf8整数表示替换它.
这是一个使用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)
Run Code Online (Sandbox Code Playgroud)
现在,我们要解析转义字符。我们将使用一个匹配字符并返回一个字符的正则表达式,因此它是一个RE Char Char. 理想情况下我会这样写:
escaped :: RE Char Char
escaped = do
string "\\u"
digits <- replicateM 4 (psym isHexDigit)
return . chr . fst . head . readHex $ digits
Run Code Online (Sandbox Code Playgroud)
这head是安全的,因为我们确保readHex只会传递十六进制数字,因此会成功。我们几乎可以这样写,只不过它RE Char不是Monad. 使用新的 GHC,您可能可以打开ApplicativeDo并完成它,但无论如何,我们自己以应用风格编写并支持所有 GHC 也不错,所以让我们这样做:
escaped :: RE Char Char
escaped
= chr . fst . head . readHex
<$> (string "\\u"
*> replicateA 4 (psym isHexDigit)
)
Run Code Online (Sandbox Code Playgroud)
不管怎样,一旦我们有了一个用于解码单个转义字符的正则表达式,就很容易生成一个用于解码所有转义字符的正则表达式,并将未转义的字符传递给未被更改的:many (escaped <|> anySym)。由于这个正则表达式总是会成功,因此我们可以忽略Maybe对冲(=~)表达式是否匹配的赌注,并编写
decodeHex :: String -> String
decodeHex = fromJust . (=~ many (escaped <|> anySym))
Run Code Online (Sandbox Code Playgroud)
让我们在 ghci 中尝试一下:
> decodeHex "\\u00b5ABC"
"\181ABC"
> decodeHex "\\u00bABC"
"\186BC"
> decodeHex "\\udefg"
"\\udefg"
Run Code Online (Sandbox Code Playgroud)
像这样编写我们自己的解析器而不是依赖类似的东西的优点decode是我们可以准确地控制和信心正在完成哪些转换;例如,由于我们知道\u后面总是跟着四个十六进制数字,因此我们只能在发生这种情况时对其进行转换,以防包含原始的 JavaScript 文本之前的文本,\\udefg并且我们希望它出现在最终输出中,而不是\3567g; 我们不必担心它会试图逃避我们不希望它做的其他事情;而且我们也不必在传递字符串之前对其进行“额外转义”,就像在字符串周围添加额外的引号一样。当然,缺点是我们必须自己设计它,并且可能对其正确性缺乏信心,因为它还没有经过一千个用户的久经沙场!