在Haskell中解析CSV/TSV文件 - Unicode字符

Pol*_*old 5 csv haskell

我正在尝试使用Haskell中的cassava/Data.Csv解析制表符分隔的文件.但是,如果我的CSV文件中存在"奇怪的"(Unicode)字符,则会出现问题.我会得到一个parse error (endOfInput).

根据命令行工具"文件",我的文件有一个"UTF-8 Unicode文本"解码.我的Haskell代码如下所示:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}

import qualified Data.ByteString as C
import qualified System.IO.UTF8 as U
import qualified Data.ByteString.UTF8 as UB
import qualified Data.ByteString.Lazy.Char8 as DL
import qualified Codec.Binary.UTF8.String as US
import qualified Data.Text.Lazy.Encoding as EL
import qualified Data.ByteString.Lazy as L

import Data.Text.Encoding as E

-- Handle CSV / TSV files with ...
import Data.Csv
import qualified Data.Vector as V

import Data.Char -- ord

csvFile :: FilePath
csvFile = "myFile.txt"

-- Set delimiter to \t (tabulator)
myOptions = defaultDecodeOptions {
              decDelimiter = fromIntegral (ord '\t')
            }

main :: IO ()
main = do
  csvData <- L.readFile csvFile 
  case EL.decodeUtf8' csvData of 
   Left err -> print err
   Right dat ->
     case decodeWith myOptions NoHeader $ EL.encodeUtf8 dat of
       Left err -> putStrLn err
       Right v -> V.forM_ v $ \ (category :: String ,
                               user :: String ,
                               date :: String,
                               time :: String,
                               message :: String) -> do
         print message
Run Code Online (Sandbox Code Playgroud)

我尝试使用decodeUtf8 ',使用Data.Char中的谓词预处理(过滤)输入等等.但是endOfFile错误仍然存​​在.

我的CSV文件如下所示:

a   -   -   -   RT USE " Kenny" • Hahahahahahahahaha. #Emmen #Brandstapel
a   -   -   -   Uhm .. wat dan ook ????!!!! 
Run Code Online (Sandbox Code Playgroud)

或者更确切地说:

a\t-\t-\t-\tRT USE " Kenny" • Hahahahahahahahaha. #Emmen #Brandstapel
a\t-\t-\t-\tUhm .. wat dan ook ????!!!! 
Run Code Online (Sandbox Code Playgroud)

问题字符是和•(在我的完整文件中,还有更多相似的字符).我该怎么办,以便cassava/Data.Csv可以正确读取我的文件?

编辑: 我创建了以下预处理器,用于在用cassava解码之前转义我的文本(参见tibbe的回答).可能有更好的可能性,但到目前为止,工作正常!

import qualified Data.Text as T

preprocess :: T.Text -> T.Text
preprocess txt = cons '\"' $ T.snoc escaped '\"'
  where escaped = T.concatMap escaper txt

escaper :: Char -> T.Text
escaper c
  | c == '\t' = "\"\t\""
  | c == '\n' = "\"\n\""
  | c == '\"' = "\"\""
  | otherwise = T.singleton c
Run Code Online (Sandbox Code Playgroud)

tib*_*bbe 4

根据木薯文档:

\n\n
\n
    \n
  • 非转义字段可以包含除双引号、逗号、回车符和换行符之外的任何字符。

  • \n
  • 转义字段可以包含任何字符(但双引号需要转义)。

  • \n
\n
\n\n

由于第一个记录中的最后一个字段包含双引号,因此该字段需要用双引号转义,并且任何双引号都需要转义,如下所示:

\n\n
a   -   -   -   "RT USE "" Kenny"" \xe2\x80\xa2 Hahahahahahahahaha. #Emmen #Brandstapel"\n
Run Code Online (Sandbox Code Playgroud)\n\n

这段代码对我有用:

\n\n
import Data.ByteString.Lazy\nimport Data.Char\nimport Data.Csv\nimport Data.Text.Encoding\nimport Data.Vector\n\ntest :: Either String (Vector (String, String, String, String, String))\ntest = decodeWith\n    defaultDecodeOptions {decDelimiter = fromIntegral $ ord \'\\t\' }\n    NoHeader\n    (fromStrict $ encodeUtf8 "a\\t-\\t-\\t-\\t\\"RT USE \\"\\" Kenny\\"\\" \xe2\x80\xa2 Hahahahahahahahaha. #Emmen #Brandstapel\\"")\n
Run Code Online (Sandbox Code Playgroud)\n\n

(请注意,我必须确保使用encodeUtf8类型的文字Text,而不是ByteString直接使用文字。 sIsString的实例ByteString,用于将文字转换为 a ByteString,会截断每个 Unicode 代码点。)

\n