我选择了csv-enumerator包来解析大量的CSV文件任务。 输入文件确实包含标题,因此为行表示选择了地图类型。 MapRow是Data.Map ByteString ByteString的别名,具体而言。
问题在于map是由csv-enumerator代码深处的键(列名)自动排序的 - 在localy-scoped toMapCSV函数中使用Data.Map.fromList按键排序元素,所以简单的monkey-patch不是可能的。
我需要保留输出CSV的列顺序。 不幸的是,我目前的解决方案相当复杂,即。单独读取标题并重新排序一次读取MapRow:
import Data.CSV.Enumerator
import qualified Data.Map as M
import Data.Either
import Control.Monad
...
csvMode = CSVS { csvSep = ';'
, csvQuoteChar = Just '"'
, csvOutputQuoteChar = Just '"'
, csvOutputColSep = ';'
}
...
updateCSV :: FilePath -> IO (Either SomeException Int)
updateCSV fn = do
...
csv <- readCSVFile csvMode fn >>= return . (either (const []) (id :: [MapRow]->[MapRow]))
hdr <- readCSVFile csvMode fn >>= return . head .(either (const []) (id :: [Row]->[Row]))
...
-- reorder MapRow
outMapRow=M.fromAscList $ map (\k -> (k, (M.!) (head csv) k)) hdr
...
答案 0 :(得分:0)
由于没有比csv-enumerator更完整的CSV解析模块和用于行表示的Data.Map丢失列的原始顺序,我不得不使用关联列表并实现自定义函数以进行元素更新,在assoc之间进行转换。列表和普通列表等。幸运的是标准的Data.List(GHC.List)至少包含查找和zip / unzip函数。
第一行csv文件从IO操作中提取一次
标题&lt; - readCSVFile ...&gt;&gt; =返回。头......
使用foldCSVFile
解析csv文件的内容linesCount&lt; - foldCSVFile ...(funToIterIO ...)0
迭代函数使用从Row转换的关联列表[(Field,Field)]并在写入输出文件之前将其转换回来
让assocRow = zip header ...
让updatedARow =调整trans“列名”assocRow
- 写下更新的值
outputRow ... $ foldr((:)。snd)[] updatedARow
尽管需要编写更多代码并消耗更多时间,但所有工作都按预期工作:(