首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# ExcelDataReader无效文件签名

C# ExcelDataReader无效文件签名
EN

Stack Overflow用户
提问于 2022-12-02 18:47:58
回答 2查看 41关注 0票数 0

我已经看过了,但它并没有解决我的问题,https://stackoverflow.com/questions/51079664/c-sharp-error-with-exceldatareader

我尝试构建一个读取XLS文件并将其转换为string[]的方法,但是当我尝试运行它时会出现一个错误: ExcelDataReader.Exceptions.HeaderException:无效文件签名。

我尝试过使用XLSX运行它,并且运行良好。

我所使用的文件以前是起作用的。

请注意。我运行过与以前使用XLS相同的方法,因此我很困惑为什么会发生此错误。(使用ExcelDataReader版本3.6.0)

以下是代码:

代码语言:javascript
复制
private static List<string[]> GetExcelRecords(string path, bool hasHeaders)
        {
            var records = new List<string[]>();
            using (var stream = File.Open(path, FileMode.Open, FileAccess.Read))
            {
                using (var reader = ExcelReaderFactory.CreateReader(stream))
                {
                    var sheetFile = reader.AsDataSet().Tables[0];

                    for (int i = 0; i < sheetFile.Rows.Count; i++)
                    {
                        var record = sheetFile.Rows[i];
                        if (hasHeaders)
                        {
                            hasHeaders = false;
                            continue;
                        }
                        var row = record.ItemArray.Select(o => o.ToString()).ToArray();
                        records.Add(row);
                    }
                }
            }
            return records;
        }

异常发生在第4行。

我试过使用ExcelReaderFactory.CreateBinaryReader和ExcelReaderFactory.CreateOpenXlmReader

EN

回答 2

Stack Overflow用户

发布于 2022-12-02 21:17:41

这更多是为了好玩(所以是社区wiki),但下面是我写它的方式:

代码语言:javascript
复制
private static IEnumerable<string[]> GetExcelRecords(string path, int headerRowsToSkip = 0)
{
    using (var stream = File.Open(path, FileMode.Open, FileAccess.Read))
    using (var reader = ExcelReaderFactory.CreateReader(stream))
    {
        while(headerRowsToSkip-- > 0 && reader.Read()) {} //intentionally empty
        while(reader.Read())
        {
             object[] temp = new object[reader.FieldCount];
             reader.GetValues(temp);
             yield return temp.Select(o => o.ToString()).ToArray();
        }
    }
}

我对此还不满意,特别是这部分:

代码语言:javascript
复制
object[] temp = new object[reader.FieldCount];
reader.GetValues(temp);
yield return temp.Select(o => o.ToString()).ToArray();

问题是,我们在每次迭代中最终得到三个数据副本:读取器中包含的副本、object[]中的副本和string[]中的副本。

还值得一提的是,由于文化/国际化问题,在字符串和日期/数值之间来回转换是我们在计算机中经常做的最慢的事情之一。如果ExcelDataReader尊重表中的类型信息,并给出数字和日期值,那么通常会导致LOT的性能比您意识到的要转换为字符串的性能要高。

我们不能避免一次从读取器中复制数据,所以无论如何我们都需要两个副本。考虑到这一点,我认为这样做可能更好:

代码语言:javascript
复制
object[] temp = new object[reader.FieldCount];
reader.GetValues(temp);
yield return temp;

然后,我们还需要更改方法签名。这就让我们返回一组object[]s,它仍然不太理想。

另一种选择更具有转换性:使用泛型和函数式编程概念来创建一个实际的类型化对象,而不仅仅是一个数组:

代码语言:javascript
复制
private static IEnumerable<T> GetExcelRecords<T>(string path, Func<IDataRecord, T> transform, int headerRowsToSkip = 0)
{
    using (var stream = File.Open(path, FileMode.Open, FileAccess.Read))
    using (var reader = ExcelReaderFactory.CreateReader(stream))
    {
        while(headerRowsToSkip-- > 0 && reader.Read()) {} //intentionally empty
        while(reader.Read())
        {
             yield return transform(reader);
        }
    }
}

这就把艰苦的工作推到了lambda表达式上。为了解释如何使用它,假设您有一个带有一个标题行和列(按顺序排列)的工作表,用于整数ID、日期、十进制价格、双重数量和字符串描述。您可以这样调用该方法:

代码语言:javascript
复制
var rows = GetExcelRecords("file path", row => 
    {
       return (row.GetInt32(0), row.GetDateTime(1), row.GetDecimal(2), row.GetDouble(3), row.GetString(4) );
    }, 1);

它使用ValueTuple来避免声明类,同时仍然保留原始数据类型并避免两个类(2!)为每一行分配数组。由于我们使用的是IEnumerable<>而不是List<>,这也意味着每次只需要在内存中保留一行。

我想我们最终会在一个好地方结束。我用来读取标题行的技巧可能有点太聪明了--依赖于布尔短路排序、操作符优先级的细微性和空置的here循环--但我喜欢这里的对称性。

票数 0
EN

Stack Overflow用户

发布于 2022-12-02 21:28:59

问题似乎是我的笔记本电脑损坏了XLS文件(我不知道为什么),错误不是文件类型造成的,而是我的文件已损坏的事实。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74660257

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档