【问题标题】:Scientific notation when importing from Excel in .Net从 .Net 中的 Excel 导入时的科学记数法
【发布时间】:2009-01-09 21:40:24
【问题描述】:

我有一个从 Excel 导入数据然后处理它的 C#/.Net 作业。我们的客户放下文件,我们处理它们。我对原始文件没有任何控制权。

我使用 OleDb 库来填充数据集。该文件包含一些数字,如 30829300、30071500 等...这些列的数据类型是“文本”。

当我导入数据时,这些数字会转换为科学计数法。有没有办法防止这种情况发生?

【问题讨论】:

  • 您将数字字符串导入什么数据类型?

标签: c# .net excel oledb


【解决方案1】:

解决此问题的一种方法是更改​​您的 select 语句,而不是 SELECT * 这样做:

"SELECT Format([F1], 'General Number')  From [Sheet1$]"
 -or-
"SELECT Format([F1], \"#####\")  From [Sheet1$]"

但是,如果您的单元格包含超过 255 个字符并出现以下错误,则这样做会崩溃: “多步 OLE DB 操作产生错误。检查每个 OLE DB 状态值(如果可用)。没有完成任何工作。”

幸运的是,我的客户并不关心在这种情况下会出错。

这个页面还有很多值得尝试的好东西: http://www.dicks-blog.com/archives/2004/06/03/external-data-mixed-data-types/

【讨论】:

  • 嘿。我使用了此修复程序,它似乎为我解决了问题。问题是它似乎对每个数字进行四舍五入,以便最后两位数字为零。例如 12345678 变为 12345600。有谁知道我将如何解决这个问题?
【解决方案2】:

OleDb 库,通常会弄乱 Excel 电子表格中的数据。这主要是因为它强制所有内容进入固定类型的列布局,猜测每列的前 8 个单元格中的值中的每列的类型。如果它猜错了,你最终会将数字字符串转换为科学记数法。废话!

为避免这种情况,您最好自己跳过 OleDb 并直接阅读工作表。您可以使用 Excel 的 COM 接口(也是 blech!)或第三方 .NET Excel 兼容阅读器来执行此操作。 SpreadsheetGear 就是这样一个运行良好的库,其接口与 Excel 的 COM 接口非常相似。

【讨论】:

    【解决方案3】:

    使用这个连接字符串:

    Provider=Microsoft.ACE.OLEDB.12.0; data source={0}; Extended Properties=\"Excel 12.0;HDR=NO;IMEX=1\"
    

    使用 Excel 2010,我注意到以下内容。如果在您运行 OLEDB SELECT 时 Excel 文件已打开,那么您将获得当前版本的单元格,而不是保存的文件值。此外,长数、十进制值和日期返回的字符串值如下所示:

    5.0130370071e+012
    4.08
    36808
    

    如果文件未打开,则返回值为:

    5013037007084
    £4.08
    Monday, October 09, 2000
    

    【讨论】:

      【解决方案4】:

      如果您使用 Open XML SDK 2.0 Productivity Tool 查看实际的 .XSLX 文件(或者只是解压缩文件并在记事本中查看 XML),您会发现 Excel 2007 实际上以科学格式存储原始数据。

      例如 0.00001 存储为 1.0000000000000001E-5

      <x:c r="C18" s="11" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
        <x:v>1.0000000000000001E-5</x:v>
      </x:c>
      

      查看 Excel 中的单元格,其在单元格和编辑栏中均显示为 0.00001。因此,OleDB 导致问题并不总是正确的。

      【讨论】:

        【解决方案5】:

        我发现最简单的方法是选择 Zip 格式,而不是为具有大“数字”的列选择文本格式。

        【讨论】:

        • 就像我上面所说的,我无法控制文件。客户创建它。
        【解决方案6】:

        您是否尝试在阅读时将该字段的值转换为 (int) 或 (Int64)?

        【讨论】:

        • 我不“读取”文件,OleDb API 会这样做。我只需调用 OleDbDataAdapter 上的“填充”方法并传入一个 DataSet。然后,Dataset 会被多汁美味的数据填充。
        • 数据集是否为强类型,因此该字段需要一个数字?
        • 不,它不是强类型的。
        • 尝试为它创建一个强类型数据集。那应该正确转换它。
        【解决方案7】:

        在 google 上查找 IMEX=1 连接字符串选项和 TypeGuessRows 注册表设置。 事实上,没有简单的方法来解决这个问题,因为读者通过查看前几行(默认为 8 行)来推断列数据类型。如果行包含所有数字,那么您就不走运了。

        我过去使用的一个不幸的解决方法是使用 HDR=NO 连接字符串选项并将 TypeGuessRows 注册表设置值设置为 1,这会强制它读取第一行作为有效数据来确定其数据类型,而不是标题。 这是一个黑客,但它的工作原理。代码将第一行(包含标题)读取为文本,然后相应地设置数据类型。

        更改注册表是一件痛苦的事情(并非总是可行),但我建议之后恢复原始值。

        如果您的导入数据没有标题行,那么另一种选择是预处理文件并在违规列中的每个数字之前插入一个 ' 字符。这会导致列数据被视为文本。

        总而言之,有很多技巧可以解决这个问题,但没有什么是万无一失的。

        【讨论】:

        • 我试过 IMEX=1 属性 ..con 字符串是“Provider=Microsoft.ACE.OLEDB.12.0;Data Source=#FILEPATH#;Extended Properties='Excel 12.0 Xml;HDR=NO; IMEX=1" 但值为 2,13123219999999E+18............
        • 您也必须设置注册表项 TypeGuessRows。就像我说的,这是一个不幸的黑客攻击。
        【解决方案8】:

        我也遇到过同样的问题,但无需借助 Excel COM 接口或第 3 方软件即可解决。它涉及一些处理开销,但似乎对我有用。

        1. 先读入数据得到列名
        2. 然后使用这些列中的每一列创建一个新的 DataSet,将它们的每个 DataTypes 设置为字符串。
        3. 再次将数据读入这个新的 数据集。瞧——科学的 符号现在消失了,所有内容都作为字符串读入。

        这里有一些代码可以说明这一点,作为额外的奖励,它甚至是 StyleCopped!

        public void ImportSpreadsheet(string path)
        {
            string extendedProperties = "Excel 12.0;HDR=YES;IMEX=1";
            string connectionString = string.Format(
                CultureInfo.CurrentCulture,
                "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"{1}\"",
                path,
                extendedProperties);
        
            using (OleDbConnection connection = new OleDbConnection(connectionString))
            {
                using (OleDbCommand command = connection.CreateCommand())
                {
                    command.CommandText = "SELECT * FROM [Worksheet1$]";
                    connection.Open();
        
                    using (OleDbDataAdapter adapter = new OleDbDataAdapter(command))
                    using (DataSet columnDataSet = new DataSet())
                    using (DataSet dataSet = new DataSet())
                    {
                        columnDataSet.Locale = CultureInfo.CurrentCulture;
                        adapter.Fill(columnDataSet);
        
                        if (columnDataSet.Tables.Count == 1)
                        {
                            var worksheet = columnDataSet.Tables[0];
        
                            // Now that we have a valid worksheet read in, with column names, we can create a
                            // new DataSet with a table that has preset columns that are all of type string.
                            // This fixes a problem where the OLEDB provider is trying to guess the data types
                            // of the cells and strange data appears, such as scientific notation on some cells.
                            dataSet.Tables.Add("WorksheetData");
                            DataTable tempTable = dataSet.Tables[0];
        
                            foreach (DataColumn column in worksheet.Columns)
                            {
                                tempTable.Columns.Add(column.ColumnName, typeof(string));
                            }
        
                            adapter.Fill(dataSet, "WorksheetData");
        
                            if (dataSet.Tables.Count == 1)
                            {
                                worksheet = dataSet.Tables[0];
        
                                foreach (var row in worksheet.Rows)
                                {
                                    // TODO: Consume some data.
                                }
                            }
                        }
                    }
                }
            }
        }
        

        【讨论】:

        • 该死!这种问题是不是让你想把头发扯掉?
        【解决方案9】:

        我从其他地方得到了一个解决方案,但它对我来说非常有效。 无需更改任何代码,只需将 excel 列单元格格式设置为“常规”而不是“数字”或“文本”等任何其他格式,然后甚至 Select * from [$Sheet1] 或 Select Column_name from [$Sheet1] 将读取即使是超过 9 位的大数值,它也很完美

        【讨论】:

          【解决方案10】:

          我用谷歌搜索了这个州.. 这是我的解决步骤

          • 用于模板excel文件

          1 格式 Excel 列作为文本 2- 编写宏来禁用数字的错误警告 -> 文本转换

            Private Sub Workbook_BeforeClose(Cancel As Boolean)
          Application.ErrorCheckingOptions.BackgroundChecking = Ture
          End Sub
          Private Sub Workbook_Open()
          Application.ErrorCheckingOptions.BackgroundChecking = False
          End Sub
          
          • 代码隐藏

          3- 在读取要导入的数据时 尝试将传入的数据解析为 Int64 或 Int32....

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-02-21
            • 2017-06-06
            相关资源
            最近更新 更多