以独立于语言环境的方式读取Excel文件

本文关键字:方式 读取 Excel 文件 环境 于语言 独立 语言 | 更新日期: 2023-09-27 18:17:39

我使用以下代码从各种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/中性语言环境。

我也尝试了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文化格式化数据?

以独立于语言环境的方式读取Excel文件

在多次尝试和错误之后,在阅读了这篇过时的文章之后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字符串化数字和货币的区域性将匹配。而且,为了做一个好孩子,在我的任务结束时,我恢复了以前的文化。

如果有人有更好的解决办法,我会非常感激。但是现在我将保持它的原样——我所有的单元测试现在都是绿色的。