将CsvHelper与流一起使用

时间:2019-11-13 02:23:54

标签: csv stream streamreader blazor csvhelper

我正在尝试使用CsvHelper读取CSV文件并从中创建一个DataTable。第一行将是提供列名称的​​头记录,但除此之外,文件的结构是未知的。如果我使用以下代码(摘自CsvHelper的作者的示例),则它可以工作。

{{1}}

但是,如果我为StreamReader使用备用构造函数,该构造函数将Stream作为参数而不是文件路径作为参数,则CsvDataReader的创建将失败,并显示错误消息“不支持同步读取”。

我尝试了CsvHelper的其他几种方法来尝试不同地处理数据,但是在通过传递Stream而不是文件路径创建StreamReader的任何时候,我都会遇到相同的错误。我开始怀疑真正的问题在于StreamReader的实现还是CsvHelper。在我的情况下(使用Blazor Server应用程序),在Stream中传递更为有意义。有任何想法吗?

编辑:

我认为David Specht是对的,因为我使用的特定流有一些独特之处。在进一步的测试中,我发现某些字符串可以起作用。在出现错误的情况下,我正在使用IFileListEntry.Data流接口从Steve Sanderson的BlazorInputFile组件(在GitHub上)读取流。我怀疑它的实现中会引起我遇到的错误。如果真是这样,那么解决方法将很有帮助。 (也许是从另一个流中创建一个流以在异步流和同步流之间切换?还不确定如何执行此操作,但是我将对其进行介绍。)

3 个答案:

答案 0 :(得分:1)

我与BlazorInputFile和CsvReader遇到相同的问题。我查看了BlazorInputFile的代码,如果您同步读取流,则可以看到Steve在哪里抛出错误。此外,CsvReader的git中还有一些开放项目和讨论,这似乎与为什么BlazorInputFile不允许使用它有关。 CsvParser's ReadAsync can read stream synchronouslyimprove async/await performance另一方面,在IFileListEntry中添加了扩展方法ReadAllAsync,该方法返回异步MemoryStream。您只需在创建StreamReader时调用它即可。

注意:

  1. 对于最新版本的CsvReader,您必须指定CultureInfo参数。
  2. 我正在成功使用它,并将csv映射到一个IList而不是DataTable,所以您仍然可能会遇到问题,但是应该可以。
            using var stream = new StreamReader(await file.ReadAllAsync());
            using var csv = new CsvReader(stream, CultureInfo.InvariantCulture);
            using var dr = new CsvDataReader(csv);
            var dt = new DataTable();
            dt.Load(dr);

答案 1 :(得分:0)

可能您正在使用的流需要异步读取?以下对我有用。

var request = WebRequest.Create("https://people.sc.fsu.edu/~jburkardt/data/csv/addresses.csv");
var response = request.GetResponse();

using (var stream = response.GetResponseStream())
using (var csv = new CsvReader(new StreamReader(stream)))
{
    using (var dr = new CsvDataReader(csv))
    {
        var dt = new DataTable();
        dt.Load(dr);
    }
}

答案 2 :(得分:0)

正如David Specht在回答中指出的那样,正如我对原始问题的编辑所指出的那样,它确实适用于某些Streams。在下面的示例中,流file.Data的实现中的某些内容不适用于CsvHelper,从而导致“不支持同步读取”错误。该特定流是由Steve Sanderson创建的IFileListEntry.Data组件的一个实例BlazorInputFile,可在GitHub上使用。 (总而言之,请记住,我正在使用版本 0.1.0-preview-00002 ,所以谢谢,史蒂夫!),此组件似乎运行得很好。)

通过使用Stream.CopyToAsync()将流复制到新流中,问题就解决了。需要牢记的一点是,执行此函数后,输入流和输出流都将位于流的末尾。必须将将用于创建CsvReader的流重新设置为开始,以使CsvDataReader构造函数正常工作。如果不这样做,将出现“找不到头记录”错误。

以下示例对我有用,希望会对其他人有所帮助!

using (var stream2 = new MemoryStream())
{
    await file.Data.CopyToAsync(stream2);   // although file.Data is itself a stream, using it directly causes "synchronous reads are not supported" errors below.
    stream2.Seek(0, SeekOrigin.Begin);      // at the end of the copy method, we are at the end of both the input and output stream and need to reset the one we want to work with.
    var reader = new System.IO.StreamReader(stream2);

    using (var csv = new CsvReader(reader))
    {
        using (var dr = new CsvDataReader(csv)) // error happens here when "file.Data" is used as the stream: "Synchronous reads are not supported"
                                                // error happens here when the stream isn't reset to the beginning: "No header record was found"
        {
            var dt = new DataTable();
            dt.Load(dr);
        }
    }
}