【问题标题】:Custom functionality similar to SqlDataReader.Read()类似于 SqlDataReader.Read() 的自定义功能
【发布时间】:2013-10-30 21:35:00
【问题描述】:

我想创建一个类似于SqlDataReader.Read()的功能

我正在从 .txt/.csv 读取一个平面文件,并将其作为数据表返回给我的类处理业务逻辑。这会遍历数据表的行,并转换数据,写入结构化数据库。我将此结构用于多个导入源。

虽然文件很大,但运行起来非常非常缓慢。浏览 30 MB 数据需要 2 小时,我希望将其缩短到 30 分钟。朝这个方向迈出的一步是不要将整个文件读入 DataTable,而是逐行处理,并防止内存被占用。

这样的东西比较理想:PSEUDOCODE。

FlatFileReader ffr = new FlatFileReader(); //Set FlatFileParameters
while(ffr.ReadRow(out DataTable parsedFlatFileRow))
{
     //...Business Logic for handling the parsedFlatFileRow
}

如何实现类似.ReadRow(out DataTable parsedFlatFileRow) 的方法?


这是正确的方向吗?

foreach(obj in ff.lazyreading()){
    //Business Logic
} 

...

class FlatFileWrapper{

    public IEnumerable<obj> lazyreading(){
        while(FileReader.ReadLine()){ 
            yield return parsedFileLine; 
        }
    } 
}

【问题讨论】:

  • FileHelpers 对你来说可能是个不错的选择:filehelpers.sourceforge.net
  • 您可能还没有确定性能不佳的原因。您猜测这是内存使用情况,但我对此非常怀疑。分析应用程序,或暂停调试器 10 次以查看它最常停止的位置。
  • 不,你是对的 - 我还没有分析它。但是在此 SQL Server 上,内存使用是一个已知问题,在处理大文件时尤其受到注意。因此,保持低内存使用本身就是一个优先事项。

标签: c# .net


【解决方案1】:

正如蒂姆已经提到的,File.ReadLines 是您所需要的:

"当你使用ReadLines时,你可以开始枚举集合 返回整个集合之前的字符串"

您可以创建一个使用该方法的解析器,如下所示:

// object you want to create from the file lines.
public class Foo
{
    // add properties here....
}

// Parser only responsibility is create the objects.
public class FooParser
{
    public IEnumerable<Foo> ParseFile(string filename)
    {
        if(!File.Exists(filename))
            throw new FileNotFoundException("Could not find file to parse", filename);

        foreach(string line in File.ReadLines(filename))
        {
            Foo foo = CreateFoo(line);

            yield return foo;
        }
    }

    private Foo CreateFoo(string line)
    {
        // parse line/create instance of Foo here

        return new Foo {
            // ......
        };
    }
}

使用代码:

var parser = new FooParser();

foreach (Foo foo in parser.ParseFile(filename))
{
     //...Business Logic for handling the parsedFlatFileRow
}

【讨论】:

  • 谢谢,我想我理解了这个概念,将尝试并发布结果。这允许我对文件中的行进行延迟加载,而内存中只有当前行的数据?
  • 是的,请查看我的答案中的链接:ReadLines 和 ReadAllLines 方法的区别如下:使用 ReadLines 时,可以在返回整个集合之前开始枚举字符串集合;使用 ReadAllLines 时,必须等待返回整个字符串数组才能访问该数组。因此,当您处理非常大的文件时,ReadLines 会更高效
【解决方案2】:

您可以使用类似于StreamReaderFile.ReadLines

foreach(string line in File.ReadLines(path))
{
     //...Business Logic for handling the parsedFlatFileRow
}

【讨论】:

  • 我想把文件读取代码放到它自己的类中。所以我正在寻找你发布的代码的包装器,它从文件中返回一个行,下次调用该方法时,返回下一个行,直到整个文件被读取......而不加载整个归档到内存中。
  • @RafaelCichocki:File.ReadLines 是您所需要的。来自 MSDN:当您使用 ReadLines 时,您可以在返回整个集合之前开始枚举字符串集合。见msdn.microsoft.com/en-us/library/dd383503.aspx
  • @RafaelCichocki:您可以将导入器包装在一个包装StreamReader 的类中(记得实现IDisposable 来处理它)和yield return 懒惰地返回您的对象。 stackoverflow.com/a/286553/284240
  • @RafaelCichocki 这确实是您需要的方法。但您可能想查看 C# 迭代器,即使用 yield return 关键字。
  • @TimSchmelter:也许您可以使用yield return 关键字编辑您的答案以包含一个完整的方法,而不仅仅是一个foreach 循环?我认为 OP 会更容易理解
猜你喜欢
  • 1970-01-01
  • 2012-12-15
  • 2012-07-04
  • 2012-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多