【问题标题】:Haskell: How to get "\\0" into "\0"?Haskell:如何将“\\0”变成“\0”?
【发布时间】:2011-08-24 06:23:40
【问题描述】:

Haskell 有许多 string literals 使用 \ 转义序列。例如\n\t\NUL

如果我有字符串文字:

let s = "Newline: \\n Tab: \\t"

如何定义函数escape :: String -> String 将上述字符串转换为:

"Newline: \n Tab: \t"

与所有其他字符串文字转义序列相同。

我可以使用 Quasi Quoting 和 Template Haskell,但不知道如何使用它们来实现结果。有什么指点吗?


更新:我刚刚找到了包含在 Base 库中的 Text.ParserCombinators.ReadP 模块。它支持 Data.Char 中的 readLitChar :: ReadS Char 函数,可以满足我的要求,但我不知道如何使用 ReadP 模块。我尝试了以下方法并且有效:

escape2 [] = []
escape2 xs = case readLitChar xs of
    [] -> []
    [(a, b)] -> a : escape2 b

但这可能不是使用 ReadP 模块的正确方法。谁能提供一些指点?

另一个更新:谢谢大家。我的最终功能如下。我觉得还不错。

import Text.ParserCombinators.ReadP
import Text.Read.Lex

escape xs 
    | []      <- r = []
    | [(a,_)] <- r = a
    where r = readP_to_S (manyTill lexChar eof) xs 

【问题讨论】:

标签: string haskell escaping


【解决方案1】:

你不需要做任何事情。当你输入字符串文字时

let s = "Newline: \\n Tab: \\t"

您可以检查它是否是您想要的:

Prelude> putStrLn s
Newline: \n Tab: \t
Prelude> length s
19

如果你只是向 ghci 询问 s 的值,你会得到别的东西,

Prelude> s
"Newline: \\n Tab: \\t"

显然它在你背后做一些转义格式,它还显示引号。如果您致电showprint,您会得到其他答案:

Prelude> show s
"\"Newline: \\\\n Tab: \\\\t\""
Prelude> print s
"Newline: \\n Tab: \\t"

这是因为show 用于序列化值,所以当您show 一个字符串时,您不会得到原始字符串,而是会得到一个可以解析为原始字符串的序列化字符串。 show s 的结果实际上是由print s 显示的(print 定义为putStrLn . show)。当你只在 ghci 中 show s 时,你会得到一个更奇怪的答案;这里 ghci 正在格式化由show 序列化的字符。

tl;dr - 始终使用 putStrLn 来查看 ghci 中字符串的值。

编辑:我刚刚意识到也许你想转换文字值

Newline: \n Tab: \t

进入实际的控制序列。最简单的方法可能是将其放在引号中并使用read

Prelude> let s' = '"' : s ++ "\""
Prelude> read s' :: String
"Newline: \n Tab: \t"
Prelude> putStrLn (read s')
Newline: 
 Tab:   

编辑 2:使用 readLitChar 的示例,这与 Chris 的答案非常接近,除了 readLitChar

strParser :: ReadP String
strParser = do
  str <- many (readS_to_P readLitChar)
  eof
  return str

然后您使用readP_to_S 运行它,它会为您提供匹配解析的列表(不应该有多个匹配项,但是可能没有任何匹配项,因此您应该检查一个空列表。)

> putStrLn . fst . head $ readP_to_S strParser s
Newline:
Tab:    
>

【讨论】:

  • 是否在字符串周围添加一组引号并进行读取可靠?
  • @Snoqual:不完全是。考虑一个带有嵌入引号的字符串:"\""read 在这种情况下会抛出解析错误。您可以为引号字符添加转义(我会使用 split 包),或者,由于您使用的是字符串文字,因此测试它是否适用于每个字符串。
【解决方案2】:

询问 QQ 和 TH 表示您希望在编译时进行此转换。对于简单的 String -> Something 转换,您可以使用 GHC 中的 OverloadedString 文字工具。

EDIT 2:在 Text.Read.Lex 中使用暴露的字符词法分析器

module UnEscape where

import Data.String(IsString(fromString))
import Text.ParserCombinators.ReadP as P
import Text.Read.Lex as L

newtype UnEscape = UnEscape { unEscape :: String }

instance IsString UnEscape where
  fromString rawString = UnEscape lexed
    where lexer = do s <- P.many L.lexChar
                     eof
                     return s
          lexed = case P.readP_to_S lexer rawString of
                    ((answer,""):_) -> answer
                    _ -> error ("UnEscape could not process "++show rawString)

编辑 1:我现在有了一个更好的 UnEscape 实例,它使用 GHC 的读取:

instance IsString UnEscape where
  fromString rawString = UnEscape (read (quote rawString))
    where quote s = '"' : s ++ ['"']

例如:

module UnEscape where

import Data.String(IsString(fromString))

newtype UnEscape = UnEscape { unEscape :: String }

instance IsString UnEscape where
  fromString rawString = UnEscape (transform rawString)
    where transform [] = []
          transform ('\\':x:rest) = replace x : transform rest
          transform (y:rest) = y : transform rest
            -- also covers special case of backslash at end
          replace x = case x of
                        'n' -> '\n'
                        't' -> '\t'
                        unrecognized -> unrecognized

以上必须是与使用 unEscape 的模块分开的模块:

{-# LANGUAGE OverloadedStrings #-}
module Main where

import UnEscape(UnEscape(unEscape))

main = do
  let s = "Newline: \\n Tab: \\t"
      t = unEscape "Newline: \\n Tab: \\t"
  print s
  putStrLn s
  print t
  putStrLn t

这会产生

shell prompt$ ghci Main.hs 


GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
[1 of 2] Compiling UnEscape         ( UnEscape.hs, interpreted )
[2 of 2] Compiling Main             ( Main.hs, interpreted )
Ok, modules loaded: Main, UnEscape.
*Main> main
"Newline: \\n Tab: \\t"
Newline: \n Tab: \t
"Newline: \n Tab: \t"
Newline: 
 Tab:   

【讨论】:

  • 嗨,克里斯。谢谢。我希望我不必在自己的代码中重现所有转义序列的整个解析能力。
  • 我无法快速找到 GHC 公开其内部解析功能的简单地方。但是你可以在 GHC 中找到它并复制/粘贴它。
  • 我的意思是我不想通过实现自己或复制来复制该功能。我想使用内置功能的公开版本。
  • 我在上面编辑了我的答案:我现在使用 GHC 的读取实例作为字符串。享受吧。
  • 请注意,关于read 的常见警告(正如我在回答下面所指出的那样)仍然适用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-04
  • 1970-01-01
  • 2014-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多