【问题标题】:CsvHelper Error "No header record found" on reading a csv stream读取 csv 流时出现 CsvHelper 错误“未找到标头记录”
【发布时间】:2020-12-24 19:21:42
【问题描述】:

下面是我用来读取 csv 文件的流源的代码,但我收到错误为“未找到标头记录”。该库是 15.0,我已经按照某些解决方案中的建议使用 .ToList() ,但错误仍然存​​在。下面是该方法以及 tablefield 类和 Read Stream 方法。

这里还要注意,如果我将源作为 MemoryStream 传递,我可以获得所需的结果,但如果我将它作为 Stream 传递它会失败,因为我需要避免每次都写入内存。

public async Task<Stream> DownloadBlob(string containerName, string fileName, string connectionString)
        {
            //  MemoryStream memoryStream = new MemoryStream();           

            if (string.IsNullOrEmpty(connectionString))
            {
                connectionString = @"UseDevelopmentStorage=true";
                containerName = "testblobs";
            }

            Microsoft.Azure.Storage.CloudStorageAccount storageAccount = Microsoft.Azure.Storage.CloudStorageAccount.Parse(connectionString);
            CloudBlobClient serviceClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = serviceClient.GetContainerReference(containerName);
            CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
            if (!blob.Exists())
            {
                throw new Exception($"Blob Not found");
            }
          
            return await blob.OpenReadAsync();



public class TableField
    {
        public string Name { get; set; }
        public string Type { get; set; }

        public Type DataType
        {
            get
            {
                switch( Type.ToUpper() )
                {
                    case "STRING":
                        return typeof(string);

                    case "INT":
                        return typeof( int );

                    case "BOOL":
                    case "BOOLEAN":
                        return typeof( bool );

                    case "FLOAT":
                    case "SINGLE":
                    case "DOUBLE":
                        return typeof( double );

                    case "DATETIME":
                        return typeof( DateTime );

                    default:
                        throw new NotSupportedException( $"CSVColumn data type '{Type}' not supported" );
                }
            }
        }

 private IEnumerable<Dictionary<string, EntityProperty>> ReadCSV(Stream source, IEnumerable<TableField> cols)           

 {                
         using (TextReader reader = new StreamReader(source, Encoding.UTF8))
                {
                
                    var cache = new TypeConverterCache();
                    cache.AddConverter<float>(new CSVSingleConverter());
                    cache.AddConverter<double>(new CSVDoubleConverter());
                    var csv = new CsvReader(reader,
                        new CsvHelper.Configuration.CsvConfiguration(global::System.Globalization.CultureInfo.InvariantCulture)
                        {
                            Delimiter = ";",
                            HasHeaderRecord = true,
                            CultureInfo = global::System.Globalization.CultureInfo.InvariantCulture,
                            TypeConverterCache = cache
                        });
                    csv.Read();
                    csv.ReadHeader();


                    var map = (
                            from col in cols
                            from src in col.Sources()
                            let index = csv.GetFieldIndex(src, isTryGet: true)
                            where index != -1
                            select new { col.Name, Index = index, Type = col.DataType }).ToList();

                    while (csv.Read())
                    {
                        yield return map.ToDictionary(
                            col => col.Name,
                            col => EntityProperty.CreateEntityPropertyFromObject(csv.GetField(col.Type, col.Index)));
                    }
                
                }
            
        }

StreamReading 代码:

public async Task<Stream> ReadStream(string containerName, string digestFileName, string fileName, string connectionString)
        {
            string data = string.Empty;
            string fileExtension = Path.GetExtension(fileName);
            var contents = await DownloadBlob(containerName, digestFileName, connectionString);               
                
            
            return contents;
        }

要读取的示例 CSV:

PartitionKey;Time;RowKey;State;RPM;Distance;RespirationConfidence;HeartBPM
te123;2020-11-06T13:33:37.593Z;10;1;8;20946;26;815
te123;2020-11-06T13:33:37.593Z;4;2;79944;8;36635;6
te123;2020-11-06T13:33:37.593Z;3;3;80042;9;8774;5
te123;2020-11-06T13:33:37.593Z;1;4;0;06642;6925;37
te123;2020-11-06T13:33:37.593Z;6;5;04740;74753;94628;21
te123;2020-11-06T13:33:37.593Z;7;6;6;2;14;629
te123;2020-11-06T13:33:37.593Z;9;7;126;86296;9157;05
te123;2020-11-06T13:33:37.593Z;5;8;5;3;7775;08
te123;2020-11-06T13:33:37.593Z;2;9;44363;65;70;229
te123;2020-11-06T13:33:37.593Z;8;10;02;24666;2;2

【问题讨论】:

  • 您似乎没有包含要下载 Blob 的源代码,也没有明确说明源 csv 位于 azure blob 存储上。添加指向您相关的其他问题的链接也是明智的

标签: c# csvhelper


【解决方案1】:

与我的answer to your other question 相关(它有更多细节;你可以在那里阅读)我没有遇到将 CsvHelper 连接到 blob 存储源流的任何问题

这是使用的代码(我获取了您发布的 CSV 数据,将其添加到文件中,然后将其升级为 blob):

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        var cstr = "YOUR CONNSTR" HERE;

        var bbc = new BlockBlobClient(cstr, "temp", "ankit.csv");

        var s = await bbc.OpenReadAsync(new BlobOpenReadOptions(true) { BufferSize = 16384 });

        var sr = new StreamReader(s);

        var csv = new CsvHelper.CsvReader(sr, new CsvConfiguration(CultureInfo.CurrentCulture) { HasHeaderRecord = true, Delimiter = ";" });

        
        //try by read/getrecord
        while(await csv.ReadAsync())
        {
            var rec = csv.GetRecord<X>();
            Console.WriteLine(rec.PartitionKey);
        }

        var x = new X();
        //try by await foreach
        await foreach (var r in csv.EnumerateRecordsAsync(x))
        {
            Console.WriteLine(r.PartitionKey);
        }
    }
}

class X {
    public string PartitionKey { get; set; }
}

【讨论】:

    【解决方案2】:

    我尝试使用库的 15.0 版重现该问题,但使用类 CSVSingleConverterCSVDoubleConverter 失败。然而,使用 CSVHelper 的标准类,读取标题是可行的:

    using System;
    using System.IO;
    using System.Text;
    using CsvHelper;
    using CsvHelper.TypeConversion;
    
    namespace ConsoleApp2
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (Stream stream = new FileStream(@"e:\demo.csv", FileMode.Open, FileAccess.Read))
                {
                    ReadCSV(stream);
                }
            }
    
            private static void ReadCSV(Stream source)
    
            {
                using (TextReader reader = new StreamReader(source, Encoding.UTF8))
                {
    
                    var cache = new TypeConverterCache();
                    cache.AddConverter<float>(new SingleConverter());
                    cache.AddConverter<double>(new DoubleConverter());
                    var csv = new CsvReader(reader,
                        new CsvHelper.Configuration.CsvConfiguration(global::System.Globalization.CultureInfo.InvariantCulture)
                        {
                            Delimiter = ";",
                            HasHeaderRecord = true,
                            CultureInfo = global::System.Globalization.CultureInfo.InvariantCulture,
                            TypeConverterCache = cache
                        });
                    csv.Read();
                    csv.ReadHeader();
    
                    foreach (string headerRow in csv.Context.HeaderRecord)
                    {
                        Console.WriteLine(headerRow);
                    }
                }
            }
        }
    }
    

    我改变了台词...

    cache.AddConverter<float>(new CSVSingleConverter());
    cache.AddConverter<double>(new CSVDoubleConverter());
    

    ...到...

    cache.AddConverter<float>(new SingleConverter());
    cache.AddConverter<double>(new DoubleConverter());
    

    我将 CSV 数据放入一个 UTF-8 文本文件中。控制台的输出是:

    PartitionKey
    Time
    RowKey
    State
    RPM
    Distance
    RespirationConfidence
    HeartBPM
    

    编辑 2020-12-24: 将整个源文本放到网上,而不仅仅是其中的一部分。

    【讨论】:

    • 我尝试了和你说的一样的方法,但我得到了同样的错误。你确定你没有改变什么吗?你是如何传递源代码的?
    • 将问题修改为我如何读取流并将其传递给 ReadCsv()。我相信您使用了 MemoryStream()。它适用于内存流,但我避免读取内存,因为它会给大文件带来问题。
    • @AnkitKumar 我将更新我的答案并发布完整的源代码。事实上,我已经适应了更多或删除了(据说)不需要的部分。但是,我没有使用 MemeoryStream,而是 FileStream。
    • 请像我一样尝试,相信你会得到同样的错误
    • @AnkitKumar 你检查过这个流中有什么吗?您是否已将内容写入文件?不知何故,我怀疑问题就在那里。
    【解决方案3】:

    尝试将源流设置回开始。

    private IEnumerable<Dictionary<string, EntityProperty>> ReadCSV(Stream source, IEnumerable<TableField> cols)           
    {    
       source.Position = 0;
    

    你也不能在那里使用yield return。它会延迟代码的执行,直到您访问从ReadCSV 方法返回的IEnumerable&lt;Dictionary&lt;string, EntityProperty&gt;&gt;。问题是此时您已经使用 TextReader 关闭了 using 语句,CsvHelper 需要读取您的数据,因此您得到了 NullReferenceException

    您要么需要删除yield return

    var result = new List<Dictionary<string, EntityProperty>>();
    while (csv.Read()){
       // Add to result
    }
    return result;
    

    或者将TextReader 传递给您的方法。 IEnumerable&lt;Dictionary&lt;string, EntityProperty&gt;&gt; 的任何枚举必须在离开 using 语句之前发生,该语句将处理 CsvReader 所需的 TextReader

    IEnumerable<Dictionary<string, EntityProperty>> result;
    
    using (TextReader reader = new StreamReader(source, Encoding.UTF8)){
       // Calling ToList() will enumerate your yield statement
       result = ReadCSV(reader, cols).ToList(); 
    }
    

    【讨论】:

    • 我得到 System.NullReferenceException: '对象引用未设置为对象的实例。'
    • 这可能是另一个问题。你不能在那里使用yield return。它会延迟代码的执行,直到您访问从ReadCSV 方法返回的IEnumerable&lt;Dictionary&lt;string, EntityProperty&gt;&gt;。问题是此时您已经使用 TextReader 关闭了 using 语句,CsvHelper 需要读取您的数据,因此您得到了 NullReferenceException。您需要删除 yield return 或将 TextReader 传递给您的方法并在迭代 IEnumerable&lt;Dictionary&lt;string, EntityProperty&gt;&gt; 后将其销毁
    • 您能否详细说明一下代码并告诉我您销毁 textReader 是什么意思
    • @AnkitKumar 我添加了更多代码。当您调用 ReadCSV 时,它会返回一个 IEnumerable&lt;Dictionary&lt;string, EntityProperty&gt;&gt; 给您。不幸的是,您当时还留下了using(){ } 语句,它会自动处理TextReader。当您稍后迭代IEnumerableyield return 时,CsvHelper 会尝试使用已经被释放的TextReader
    【解决方案4】:

    我收到了同样的错误“找不到头文件...”,这是在成功读取同一个文件数百次之后。我添加了分隔符=","

    reader = csv.reader(filename, delimiter=",")

    这就解决了问题。我认为如果未指定分隔符,csv_reader 将尝试确定分隔符,并在一段时间后失败,可能是内存泄漏?逗号是默认值,但如果读者必须以编程方式确定它,则更有可能失败。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-03
      • 1970-01-01
      相关资源
      最近更新 更多