阅读"尴尬"使用FSharp CsvParser的CSV文件

时间:2016-08-31 21:57:01

标签: f# f#-data

我有一个大文件(200K - 300K行文本)。 它几乎但不是一个CSV文件。

  1. 列标题位于第二行,其中有一行虚拟文本
    在那之前。

  2. 有些行散布着实际的数据行。他们有 逗号,但大多数列都是空白的。它们与我无关。

  3. 我需要有效地读取这个文件,并解析实际的行 有效,作为CSV数据。

    我的第一个想法是编写一个清除第一行的空白程序和空白行,只留下我想要的标题和详细信息 在CsvParser可以读取的CSV文件中。

    这很简单,只需从StreamReader读取ReadLine,我可以通过将其视为字符串来保持或忽略每一行。

    现在虽然我有一个新问题。

    我可以使用有效数据中的一列来忽略更多行。

    如果我使用CsvParser阅读已清理的文件,则很容易按该列过滤。

    但是,我真的不想浪费把我不需要的行写到Clean文件中。

    我希望能够在清理文件时检查该列。但是,在那一点上,我使用代表整行的字符串。要达到我想要的特定栏目并不容易。

    我不能分开','其他专栏文本中可能有逗号。 我最终编写了Csv解析逻辑,我首先使用的是CsvParser。

    理想情况下,我想读取现有文件,清除我可以基于字符串的行,然后以某种方式使用CsvParser解析生成的seq。

    我看到CsvFile可以从Streams和Readers加载,但我不确定这有多大帮助。

    有什么建议或者我只是要求太多?我是否应该在加载清理文件时处理额外的过滤?

1 个答案:

答案 0 :(得分:1)

您可以直接使用CsvFile类来避免执行大部分解析工作。

F# Data documentation有一些扩展示例,详细说明如何执行此操作。

在文件开头跳过行由skipRows参数处理。传递ignoreErrors参数也会忽略无法解析的行。

open FSharp.Data
let csv = CsvFile.Load(file, skipRows=1, ignoreErrors=true)
for row in csv.Rows do
    printfn "%s" row.GetColumn "Name"

如果您必须对行进行更复杂的过滤,那么不需要临时文件的简单方法是过滤File.ReadLines的结果并将其传递给CsvFile.Parse

下面的示例跳过六行前奏,读取行直到它到达空行,使用CsvFile解析数据,最后将结果行过滤到感兴趣的行。

let tableA =
    File.ReadLines(file)
    |> Seq.skip(6) 
    |> Seq.takeWhile(fun l -> String.length l > 0) 
    |> String.concat "\n"

let csv = CsvFile.Parse(tableA)
for row in csv.Rows.Filter(fun row -> row?Close.AsFloat() > row?Open.AsFloat()) do
    printfn "%s" row.GetColumn "Name"