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

时间:2014-10-22 03:23:08

标签: csv haskell

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

根据命令行工具“file”,我的文件有一个“UTF-8 Unicode text”解码。我的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

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

我的CSV文件如下所示:

a   -   -   -   RT USE " Kenny" • Hahahahahahahahaha. #Emmen #Brandstapel
a   -   -   -   Uhm .. wat dan ook ????!!!! 

或更确切地说:

a\t-\t-\t-\tRT USE " Kenny" • Hahahahahahahahaha. #Emmen #Brandstapel
a\t-\t-\t-\tUhm .. wat dan ook ????!!!! 

问题字符是和•(在我的完整文件中,还有更多相似的字符)。我能做些什么,以便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

1 个答案:

答案 0 :(得分:4)

根据木薯文件:

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

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

  •   

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

a   -   -   -   "RT USE "" Kenny"" • Hahahahahahahahaha. #Emmen #Brandstapel"

此代码适用于我:

import Data.ByteString.Lazy
import Data.Char
import Data.Csv
import Data.Text.Encoding
import Data.Vector

test :: Either String (Vector (String, String, String, String, String))
test = decodeWith
    defaultDecodeOptions {decDelimiter = fromIntegral $ ord '\t' }
    NoHeader
    (fromStrict $ encodeUtf8 "a\t-\t-\t-\t\"RT USE \"\" Kenny\"\" • Hahahahahahahahaha. #Emmen #Brandstapel\"")

(请注意,我必须确保在encodeUtf8字面上使用Text,而不是直接使用ByteString字面值。IsString实例{ {1}} s,用于将文字转换为ByteString,用于截断每个Unicode代码点。)