以独立于区域的方式读取Excel文件

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

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

不pipe我如何尝试,当服务器区域设置为其他使用的区域设置. 作为千位分隔符和小数点分隔符,我得到错误的结果在我的dt DataTable

比较币值的结果 – £200000.00:

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

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

我甚至不能使用Thread.CurrentThread.CurrentCulture的当前数字分隔符来后处理数据,因为OleDb似乎完全忽略它。

OleDb从哪里获得当前的文化? 我该告诉OleDbConnection还是Microsoft.ACE.OLEDB.12.0提供者,我需要根据en-USInvariant文化格式化的数据?

经过大量的试验和错误,阅读这篇过时的文章后, http://support.microsoft.com/default.aspx? scid=KB;EN-US;Q320744我发现默认情况下当前版本的OLEDB似乎是使用从HKEY_CURRENT_USER\Control Panel\International文化。 不幸的是,我没有find如何从我的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的文化数字和货币将会匹配。 而且,为了成为一个好孩子,我在function结束时恢复了以前的文化。

如果有人有更好的解决办法,我会很感激。 但现在我会保持原样 – 现在所有的unit testing都是绿色的。