【问题标题】:GetValue() does not read decimal values when first row is string当第一行是字符串时,GetValue() 不读取十进制值
【发布时间】:2013-02-22 13:37:32
【问题描述】:

'正在使用 Microsoft.ACE.OLEDB.12.0 提供程序从 Excel 工作表中读取数据。 我正在使用OleDbDataReader 和他的GetValue() 来获取数据。 第一行/行(可以多于一个)是字符串标题,我不能跳过它。 接下来是设置为 0 小数位的数字数据,但当我选择其中一个时,它会以正确的小数格式显示在栏中。

如何以完整的原始十进制格式读取这些混合数据,如 Excel 中的条形? 我无法更改 Excel 工作表的设置。

这是我的代码:

using System.Data.OleDb;

    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                string query = "SELECT * FROM [List1$]";
                string connString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\Temp\Test.xls;Extended Properties=""Excel 12.0;HDR=NO;IMEX=1""";
                using (OleDbConnection connection = new OleDbConnection(connString))
                {
                    connection.Open();
                    using (OleDbCommand command = new OleDbCommand(query, connection))
                    {
                        using (OleDbDataReader reader = command.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                object value = reader.GetValue(0);
                            }
                        }
                    }
                }
            }
        }
    }

【问题讨论】:

  • 你试过用“.GetDecimal”代替“.GetValue”吗?
  • 是的。 GetDecimal 抛出异常,因为有空格字符。 GetFieldType 返回字符串。所以它必须在 OleDbCommand 或 OleDbConnection 设置中的某个地方,可能是连接字符串中的某个属性。
  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。

标签: c# sql-server excel c#-3.0 oledb


【解决方案1】:

根据经验,您可能会做的最好的事情是以下。 我一直对excel文件有问题,读取数据。 这就是为什么我鄙视 excel 作为数据传输机制的原因。

我在一家通过 excel 获取所有“银行数据”的公司工作。 我很高兴被证明是错误的。

注意。 GetValue(0) 运行后,请对其进行监视。它可能会告诉你它是一个字符串。 但是你可以确定它认为它是什么,然后稍微调整你的“获取”方法。 比如,如果值是一个“字符串”,你可以将 GetValue(0) 更改为 GetString(0)。

while (reader.Read())
{
    Decimal tryParseResultDec;
    object value = reader.GetValue(0);
    if !(Decimal.TryParse(value, out tryParseResultDec))
    {
        throw new ArgumentException(string.Format("Unable to parse '{0}'.", value));   
    }

}

额外的建议。

我通常在类的顶部放置一些私有常量而不是“0”、“1”、“2”等,以告诉我列是什么。

private const int EXCEL_COLUMN_TOTAL_AMOUNT = 0;

(你可以做类似的事情,你只是保持示例简单)

额外提示:

我认为它的工作方式是 excel 将查看 第一行数据 ,并查看数据类型,并将其用于同一列中的其余行。我不认为它说“检查每一行的数据类型”。因此你的难题。

如果您说没有标题行,它将在 A1 中查找 A 中所有行的数据类型。如果您说有标题行,它将在 A2 中查找 A 中所有行的数据类型。

【讨论】:

  • 感谢您的帮助。如果 GetValue () 返回正确的数据,它可以工作,但它返回的数据没有小数位。有关小数的信息丢失。
  • 好的,我知道了。该列具有数据类型的大杂烩。是的。 “格式化”设置会搞砸你。我们不得不与我们的客户讨论这个问题。首先,我们有一个规则,即每一列的数据类型必须一致。他们在 2 行中有“一条记录”的数据。就像,在第 1 行 colA 中,会有“LastName”,在第 2 行 colA 中,会有“TransactionAmount”......(第 1 行和第 2 行做了一个“记录”)所以我们必须让他们要么(1)全部一个excel行或(2)row1 colA上的数据有LastName,row2 colB有TAmount,但row1 colB或row2 colA中没有数据。
  • 所以格式是四舍五入个别金额,聚合数据有时会减少 1 美分。我们必须在规则中编码说“允许 1 便士”......因为 excel 是如何进行四舍五入的。因此…………为什么我讨厌将 excel 作为数据传输机制。
  • 我期望GetValue()返回原始值,但它返回的值显示在excel表格中。
  • 在我的回答中阅读我的“额外提示”。我认为这个前提应该很容易测试。
【解决方案2】:

尝试在连接字符串中使用HDR=YES 并停止跳过第一行:

string connString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\Temp\Test.xls;Extended Properties=""Excel 12.0;HDR=YES;IMEX=1""";

[更新]

我建议您使用的解决方法是读取两次文件(使用相同的方法):

  1. 首先,您得到标题行,可能稍后您将需要它用于数据结构
  2. 在第二次读取时,您会跳过标题并读取行。

它应该是这样的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.OleDb;
using System.Data;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        // the Excel file
        string file = @"c:\Temp\Test.xls";

        if (!File.Exists(file))
        {
            Console.WriteLine("File not found.");
            return;
        }

        // DataTable bonus! :)
        System.Data.DataTable dt = new System.Data.DataTable();

        IEnumerable<List<object>> header = new List<List<object>>();
        IEnumerable<List<object>> rows = new List<List<object>>();

        // read the header first
        header = GetData(file, true);

        // read the rows
        rows = GetData(file, false);

        // add the columns
        foreach (var column in header.First())
        {
            dt.Columns.Add(column.ToString());
        }

        // add the rows
        foreach (var row in rows)
        {
            dt.Rows.Add(row.ToArray());
        }

        // now you may use the dt DataTable for your purpose
    }

    /// <summary>
    /// Read from the Excel file
    /// </summary>
    /// <param name="file">The path to the Excel file</param>
    /// <param name="readHeader">True if you want to read the header, 
    /// False if you want to read the rows</param>
    /// <returns></returns>
    private static IEnumerable<List<object>> GetData(string file, bool readHeader)
    {
        string query = "SELECT * FROM [List1$]";
        string connString = @"Provider=Microsoft.ACE.OLEDB.12.0;" +
            @"Data Source=" + file + @";Extended Properties=""Excel 12.0 Xml;HDR=NO;IMEX="
            + ((readHeader) ? "1" : "0") + @";""";
        using (OleDbConnection connection = new OleDbConnection(connString))
        {
            connection.Open();
            using (OleDbCommand command = new OleDbCommand(query, connection))
            {
                using (OleDbDataReader reader = command.ExecuteReader())
                {
                    bool isHeaderRead = false;
                    while (reader.Read())
                    {
                        if (readHeader && isHeaderRead)
                        { break; }
                        isHeaderRead = true;
                        List<object> values = new List<object>();
                        for (int i = 0; i < reader.FieldCount; i++)
                        {
                            values.Add(reader.GetValue(i));
                        }
                        yield return values;
                    }
                }
            }
        }
    }
}

【讨论】:

  • 谢谢它适用于这些示例,不幸的是我无法使用 HDR=YES 禁用标题。
  • 谢谢您的解决方案看起来不错,但实施起来对我来说并不容易。
猜你喜欢
  • 2012-03-22
  • 1970-01-01
  • 1970-01-01
  • 2016-06-11
  • 2018-02-02
  • 1970-01-01
  • 1970-01-01
  • 2014-07-20
  • 2011-04-14
相关资源
最近更新 更多