批量读取CSV数据并进行处理

时间:2010-12-02 20:33:29

标签: c# linq

我有一个看起来像这样的csv文件

#DELTA,1#    
Risk1,10
Risk2,10
Risk3,10
Risk4,10
Risk5,10
#DELTA,1#    
Risk6,10
Risk7,10
Risk8,10
Risk9,10
Risk10,10

等等。这些是非常大的文件(按GB的顺序)。

我想要做的是分批阅读

从第一行到下一个#Delta启动之前的csv文件启动streamreader

---Batch 1---
#DELTA,1
Risk1,10
Risk2,10
Risk3,10
Risk4,10
Risk5,10
--Batch 2-----
#DELTA,1
Risk6,10
Risk7,10
Risk8,10
Risk9,10
Risk10,10
----------------------

一旦我得到一个批处理,就把这个子集放进去处理,然后回来重新开始准备另一个批处理,依此类推,直到文件结束。

我已经尝试过制作LINQ了,但是由于我对LINQ的了解,我并没有走得太远。

基本上总的来说,它必须根据我的流中的模式分批流式传输数据..也许我的脑细胞死了或者晚上可能已经太晚了。 真的很适合任何人的帮助

8 个答案:

答案 0 :(得分:3)

最简单的方法是TextReader和ReadLine()。

对于定位,我只是在处理批次之间打开Reader。如果这不是一个选项,保存(流)位置并稍后恢复

使用StreamReader,如果必须关闭文件,则必须保留lineCount并再次从头开始读取和跳过。不太吸引人。

答案 1 :(得分:2)

在Codeplex上至少有一个工具可以在这里使用:KBCsv

答案 2 :(得分:2)

这可能是一种有用的方法。如果您编写一个返回IEnumerable的方法,那么您可以使用yield return来允许调用者在方法继续之前执行处理。所以,例如,如果你在下面编写像ReadBatches这样的方法......

    static IEnumerable<IEnumerable<string>> ReadBatches(string fileName)
    {
        var file = File.OpenText(fileName);
        var batchItems = new List<string>();

        while (!file.EndOfStream)
        {
            // clear the batch list
            batchItems.Clear();

            // read file in batches of 3
            // your logic on splitting batches might differ
            for (int i = 0; i < 3; i++)
            {
                if (file.EndOfStream)
                    break;

                batchItems.Add(file.ReadLine());
            }

            // this allows the caller to perform processing, and only
            // returns back here when they pull on the next item in the
            // IEnumerable
            yield return batchItems;                
        }

        file.Close();
    }

...然后你可以像这样调用这个方法......

    static void Main(string[] args)
    {
        foreach (IEnumerable<string> batch in ReadBatches("data.txt"))
        {
            Console.WriteLine("*** Processing Batch ***");
            foreach (var item in batch)
            {
                Console.WriteLine(item);
            }
        }                      
    }

...并且使用看起来像这样的data.txt ......

Row1
Row2
Row3
Row4
Row5
Row6
Row7

...然后你在控制台上看到了这个...

*** Processing Batch ***
Row1
Row2
Row3
*** Processing Batch ***
Row4
Row5
Row6
*** Processing Batch ***
Row7

Press any key to continue . . .

您的方法ReadBatches是一个迭代器块。编译器构建一个状态机,允许执行返回此方法。它允许您编写方法ReadBatches,就好像执行的线程跳回到调用者然后再返回以获得下一批。这不是真正发生的事情(编译器在这里给人留下了深刻的印象),但它是一种编写流式API的非常强大的方法。

我没有处理你的批处理逻辑(我的逻辑只是简单地将3行文件一起批处理)但希望这会给你这个想法。

有关收益率回报的更多信息: http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

关于收益率的热门SO问题在这里: https://stackoverflow.com/tags/yield-return/hot

答案 3 :(得分:2)

假设您可以一次将每个批处理保留在内存中,并且可以始终保持StreamReader打开,您可能希望编写如下内容:

public static void ProcessBatches(TextReader reader,
    Func<string, bool> delimiterDetector,
    Action<List<string>> batchAction)
{
    string line;
    List<string> batch = new List<string>();
    while ((line = reader.ReadLine()) != null)
    {
        if (delimiterDetector(line))
        {
            batchAction(batch);
            batch = new List<string>();
        }
    }
    batchAction(batch);
}

这假设处理批次时不需要分隔符。

然后你会这样称呼它:

using (TextReader reader = File.OpenText("foo.csv"))
{
    ProcessBatch(reader, line => line == "# DELTA,1", BatchAction);
}
...
private static void BatchAction(List<string> batch)
{
    ...
}

答案 4 :(得分:1)

内存映射文件非常适合读取非常大的文件部分,而.NET 4.0现在有managed support for them,因此您不必直接使用Windows API。

答案 5 :(得分:0)

我终于设法解决了。感谢大家的投入。我采用了Enumerable集合并进行了2次循环。 循环1: - 获取所有START OF BATCH(#)的索引在这种情况下+最后一行的索引 循环2: - 取出块并跳过已经拍摄的块。

我计划增强它以使用Observable Collection并在批量完成后调用加载调用然后继续

这是最终的代码,以防它可能有用(或有人可以查看它并建议改进)

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Linq;

namespace LinqToText
{
  class Program
  {

    static void Main(string[] args)
    {
        var csvLines = new List<string>();
        var contextIndexes = new List<int>();
        int counter = 0;


        var instream = FastReadCsvFile(@"D:\Data\Mock\mock_data.csv");
        foreach (var str in instream)
        {
            if(str.Contains("#"))
            {
                contextIndexes.Add(counter);
            }
            counter++;
        }
        contextIndexes.Add(instream.Count());

        foreach (var indexes in contextIndexes)
        {
            Console.WriteLine(indexes);
        }
        int[] ixpos = contextIndexes.ToArray();


        for(int i = 0 ;i< ixpos.Length-1;i++)
        {
            int strtPos = ixpos[i];
            int endPos = ixpos[i+1];
            var batch = instream.Skip(strtPos).Take(endPos - strtPos);
            foreach (var dt in batch)
            {
                Console.WriteLine(dt);
            }
        }
        Console.WriteLine("End Of Processing");
        Console.Read();

    }

      private static IEnumerable<string> FastReadCsvFile(string file)
      {
          using (var reader = new StreamReader(file, Encoding.Default))
          {
              string line;
              while ((line = reader.ReadLine()) != null)
              {
                  yield return line;
              }
          }
      }
  }
}

答案 6 :(得分:0)

以下是基于KBCsv的实现:

using (var csvReader = new CsvReader(@"D:\Data\Mock\mock_data.csv"))
{
    while (csvReader.HasMoreRecords)
    {
        var record = csvReader.ReadDataRecord();

        if (record[0].StartsWith("#"))
        {
            if (csvReader.RecordCount > 0)
            {
                EndBatch();
            }

            BeginBatch();
        }
        else
        {
            ProcessRecord(record);
        }
    }
}

private void BeginBatch()
{
    Console.WriteLine("Beginning batch");
}

private void EndBatch()
{
    Console.WriteLine("Ending batch");
}

private void ProcessRecord(DataRecord record)
{
    Console.WriteLine("Processing record: {0}", record);
}

答案 7 :(得分:0)

所以在Jon Skeet的帮助之后的最终答案是(对某些人有用)。

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;

namespace LinqToText
{
  class Program
  {

    static void Main(string[] args)
    {
        using (TextReader reader = File.OpenText(@"D:\Data\Mock\mock_data.csv"))
            {
                ProcessBatches(reader, line => line.Contains("#"), BatchAction); 
            } 
        Console.WriteLine("End Of Processing");
        Console.Read();

    }

      public static void ProcessBatches(TextReader reader, Func<string, bool> delimiterDetector,Action<List<string>> batchAction)
      {
          string line;
          var batch = new List<string>();
          var counter = 0;
          while ((line = reader.ReadLine()) != null)
          {
              if (delimiterDetector(line) && counter !=0)
              {

                  batchAction(batch);
                  batch = new List<string>();
              }
              batch.Add(line);
              counter++;
          }
          batchAction(batch);
      }
      private static void BatchAction(List<string> batch) 
        {
          Console.WriteLine("Processing a single batch...................");
          foreach (var str in batch)
          {
              Console.WriteLine(str);
          }
          Console.WriteLine("End of single batch processing...................");
          Thread.Sleep(1000);

        } 


  }
}

结果是

Processing a single batch...................
#ROW1,1,0,CNO,CURVE CNO #0,CNO6M,Tenor set CNO #0,ON|TN|1D|1W|1M|2M|3M|1Y|2Y|3Y|
4Y|5Y|6Y|7Y|8Y|9Y|10Y|11Y|12Y|13Y|14Y|15Y|16Y|17Y|18Y|19Y|20Y|21Y|
Risk1,10
Risk2,10
Risk3,10
Risk4,10
Risk5,10
End of single batch processing...................
Processing a single batch...................
#ROW2,1,0,CNO,CURVE CNO #0,CNO6M,Tenor set CNO #0,ON|TN|1D|1W|1M|2M|3M|1Y|2Y|3Y|
4Y|5Y|6Y|7Y|8Y|9Y|10Y|11Y|12Y|13Y|14Y|15Y|16Y|17Y|18Y|19Y|20Y|21Y|
Risk6,10
Risk7,10
Risk8,10
Risk9,10
Risk10,10
End of single batch processing...................
Processing a single batch...................
#ROW3,1,0,CNO,CURVE CNO #0,CNO6M,Tenor set CNO #0,ON|TN|1D|1W|1M|2M|3M|1Y|2Y|3Y|
4Y|5Y|6Y|7Y|8Y|9Y|10Y|11Y|12Y|13Y|14Y|15Y|16Y|17Y|18Y|19Y|20Y|21Y|
Risk11,10
Risk12,10
Risk13,10
Risk14,10
Risk15,10
End of single batch processing...................
Processing a single batch...................
#ROW4,1,0,CNO,CURVE CNO #0,CNO6M,Tenor set CNO #0,ON|TN|1D|1W|1M|2M|3M|1Y|2Y|3Y|
4Y|5Y|6Y|7Y|8Y|9Y|10Y|11Y|12Y|13Y|14Y|15Y|16Y|17Y|18Y|19Y|20Y|21Y|
Risk16,10
Risk17,10
Risk18,10
Risk19,10
Risk20,10
End of single batch processing...................
End Of Processing

感谢大家的帮助和Jon给出的最终解决方案。