【问题标题】:Reading Excel files in a locale independent way以独立于语言环境的方式读取 Excel 文件
【发布时间】:2013-06-10 15:33:26
【问题描述】:

我正在使用以下代码从各种 Excel 文件中读取数据:

    // IMEX=1 - to force strings on mixed data
    // HDR=NO - to process all the available data
    // Locale 1033 is en-US. This was my first attempt to force en-US locale.
    string connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Locale Identifier=1033;Extended Properties=\"{1};READONLY=TRUE;HDR=NO;IMEX=1;\"";

    // source type according to the
    // http://www.microsoft.com/en-us/download/details.aspx?id=13255

    // try determining from extension
    bool isOldFormat =
            Path.GetExtension(sourceExcel).Equals(".xls", StringComparison.OrdinalIgnoreCase);

    bool isBinary =
            Path.GetExtension(sourceExcel).Equals(".xlsb", StringComparison.OrdinalIgnoreCase);

    string sourceType = isOldFormat ? "Excel 8.0" : "Excel 12.0";

    if (!isOldFormat)
        sourceType += " Xml"; // for some reason the new binary xlsb files also need Xml

    connectionString = string.Format(connectionString, sourceExcel, sourceType);

    // this was my second attempt to force Excel to use US culture
    var oldCulture = Thread.CurrentThread.CurrentCulture;
    Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");

    var dt = new DataTable();
    try
    {
        using (var con = new OleDbConnection(connectionString))
        {
            con.Open();

            // get all the available sheets
            using (DataTable dataSet = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null))
            {
                // this was my third attempt to force Excel to use US culture
                dataSet.Locale = CultureInfo.CreateSpecificCulture("en-US");
                // get the sheet name in the file (will throw if out of range)
                string workSheetName = dataSet.Rows[worksheetIndex]["TABLE_NAME"].ToString();//.Trim(new[] { '$' }).Replace("'", "");

                string sql = String.Format("select * from [{0}]", workSheetName);

                var da = new OleDbDataAdapter(sql, con);
                // this was my fourth attempt to force Excel to use US culture
                dt.Locale = CultureInfo.CreateSpecificCulture("en-US");
                da.Fill(dt);
            }

            con.Close();
        }

如您所见,我非常绝望,试图在导入数据时强制 Excel 使用与 en-US 兼容的语言环境。我需要这个,因为我的代码可能在具有不同语言环境的服务器上执行,但数据需要一些额外的处理,假设传入的数据是 en-US/neutral locale。

我也试过CultureInfo.InvariantCulture而不是CultureInfo.CreateSpecificCulture("en-US")

无论我如何尝试,当服务器语言环境设置为使用. 作为千位分隔符和, 作为小数分隔符的其他语言环境时,我的dt DataTable 得到错误结果。

比较货币价值的结果 - £200000.00 :

当服务器区域设置对应美国语言环境时,我得到"-£200,000.00"

当服务器区域设置对应拉脱维亚语言环境时,我得到"-£200 000,00"

我什至无法使用Thread.CurrentThread.CurrentCulture 中的当前数字分隔符对数据进行后处理,因为 OleDb 似乎完全忽略了它。

OleDb 从哪里获得当前文化?如何告诉 OleDbConnection 或 Microsoft.ACE.OLEDB.12.0 提供程序我需要根据en-USInvariant 文化格式化的数据?

【问题讨论】:

    标签: c# excel oledb locale


    【解决方案1】:

    经过大量试验和错误以及阅读这篇过时的文章 http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q320744 我发现当前版本的 OLEDB 默认似乎使用来自HKEY_CURRENT_USER\Control Panel\International 的文化。不幸的是,我没有找到如何从我的 C# 代码中调用 SetVarConversionLocaleSetting 函数来强制 OLEDB 使用当前线程文化,所以我遵循了这个原则 - 如果我不能为我的代码调整 OLEDB,那么我会调整我的与 OLEDB 文化兼容的代码。完成后,我可以将所有数据转换为不变文化。

    但有一个棘手的部分。您不能只从HKEY_CURRENT_USER\Control Panel\International 中获取小数点分隔符,因为 OLEDB 会忽略用户自定义的数字格式设置。 OLEDB 仅采用该区域性的默认预设值。所以我必须做以下事情:

    var oldCulture = Thread.CurrentThread.CurrentCulture;
    
    using (RegistryKey international = 
            Registry.CurrentUser.OpenSubKey("Control Panel\\International", false))
    {
        string userDefaultCulture = international.GetValue("LocaleName").ToString();
        // notice: although the user might have customized his decimal/thousand separators,
        // still OLEDB ignores these customizations. That is why I create a culture with default settings.
        cultureToNormalize = new CultureInfo(userDefaultCulture, false);
    }
    
    // force both OLEDB and current thread cultures to match for the next ToString() etc. conversions in my function
    Thread.CurrentThread.CurrentCulture = cultureToNormalize;
    
    string decSep = cultureToNormalize.NumberFormat.NumberDecimalSeparator;
    string groupSep = cultureToNormalize.NumberFormat.NumberGroupSeparator;
    

    现在我可以根据需要处理数据,也可以安全地调用 ToString() - OLEDB 和 .NET 字符串化数字和货币的文化将匹配。而且,作为一个好孩子,我会在工作结束时恢复以前的文化。

    如果有人有更好的解决方案,我将不胜感激。但现在我会保持原样 - 我所有的单元测试现在都是绿色的。

    【讨论】:

      猜你喜欢
      • 2013-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-11
      • 2011-08-29
      • 2019-03-29
      • 1970-01-01
      相关资源
      最近更新 更多