Excel生成的CSV文件中的数字
不幸的是,Excel在生成CSV文件时对程序员不是很友好,因为它存储屏幕上显示的数字。因此,数字在CSV中的外观取决于区域设置和用户的喜好 因此,我可能最终会得到如下的数字Excel生成的CSV文件中的数字,excel,csv,vsto,Excel,Csv,Vsto,不幸的是,Excel在生成CSV文件时对程序员不是很友好,因为它存储屏幕上显示的数字。因此,数字在CSV中的外观取决于区域设置和用户的喜好 因此,我可能最终会得到如下的数字 2 123 3'322 1,233.44 2123,45 等等 现在我必须在我的程序中处理这些CSV文件 除了告诉用户如何构建他们的CSV文件之外,还有什么智能或规范的解决方案来处理这个问题吗?切换到另一种数据格式也是一种选择——但选择哪一种呢?它必须由Excel本机支持,并且应该易于处理(因此我希望避免使用xlsx)。我
2 123
3'322
1,233.44
2123,45
等等
现在我必须在我的程序中处理这些CSV文件
除了告诉用户如何构建他们的CSV文件之外,还有什么智能或规范的解决方案来处理这个问题吗?切换到另一种数据格式也是一种选择——但选择哪一种呢?它必须由Excel本机支持,并且应该易于处理(因此我希望避免使用xlsx)。我的客户机也有同样的问题,我已经为他们编写了一个导出到csv宏,该宏显式地告诉Excel每个列的格式,下面是一个代码示例
Public Sub Export_To_CSV()
'Give excel the sheet with data to be exported to CSV
Sheets("Sheet1").Activate
'Declare your string array to be filled - currently set to 5 columns
Dim csvread(1 To 5) As String
Dim i As Integer
i = 1
Dim ObjFso
Dim StrFileName
Dim ObjFile
'Name the export csv - This will generate it in the folder you have the excel you are working with in and then names it CSVexport-Day-Month.csv
StrFileName = Application.ThisWorkbook.Path & "\CSVExport-" & Day(Date) & "-" & Month(Date) & ".csv"
Set ObjFso = CreateObject("Scripting.FileSystemObject")
'Creating a file for writing data
Set ObjFile = ObjFso.CreateTextFile(StrFileName)
'takes the headers from row 1 - set by i if different
csvread(1) = Range("A" & i).Value
csvread(2) = Range("B" & i).Value
csvread(3) = Range("C" & i).Value
csvread(4) = Range("D" & i).Value
csvread(5) = Range("E" & i).Value
'Writes the Headers
ObjFile.WriteLine (csvread(1) & "," & csvread(2) & "," & csvread(3) & "," & csvread(4) & "," & csvread(5))
i = i + 1
'Do until loop can be altered to meet your parameters
Do Until Range("A" & i).Value = 0 Or i = 1000
'format your data however you require
csvread(15) = Format(Range("A" & i).Value, "###0.00")
csvread(15) = Format(Range("B" & i).Value, "###0.00")
csvread(15) = Format(Range("C" & i).Value, "###0.00")
csvread(15) = Format(Range("D" & i).Value, "###0.00")
csvread(15) = Format(Range("E" & i).Value, "###0.00")
'write the line to the file
ObjFile.WriteLine (csvread(1) & "," & csvread(2) & "," & csvread(3) & "," & csvread(4) & "," & csvread(5))
i = i + 1
Loop
ObjFile.Close
p = MsgBox("Exported", vbOKOnly)
End Sub
到目前为止,除了自己导出文件之外,我还没有找到更好的解决方案(
CsvWriter
是一个自定义类,但很简单--CsvWriter.WriteItems
获取可枚举的字符串,必要时引用它们,并在csv文件中用它们生成一行):
公共静态类CsvExporter{
公共静态工作表(
CsvWriter CsvWriter,
Excel.工作表,
Action reportProgressCallback=null
) {
foreach(工作表中的var行ToString(工作表,reportProgressCallback))
csvWriter.WriteItems(行);
}
公共静态IEnumerable工作表字符串(
Excel.Worksheet工作表,Action reportProgressCallback=null
) {
var usedRange=worksheet.usedRange;
返回范围字符串(usedRange、reportProgressCallback);
}
公共静态IEnumerable RangeToString(
Excel.范围,
Action reportProgressCallback=null
) {
if(reportProgressCallback==null)
reportProgressCallback=(行,总计)=>{};
int rowsTotal=range.Rows.Count;
int currentRow=0;
foreach(range.Rows中的变量行){
++当前行;
reportProgressCallback(currentRow,rowsTotal);
var rowValues=(行作为Excel.Range)。Value2作为对象;
弗雷奇(
var convertedRow in
ConvertToEnumerableofEnumerableSofString(行值)
)
收益率-收益率转换流;
}
}
私有静态IEnumerable
ConvertToEnumerableSofEnumerableSofString(
对象值
) {
返回ConvertToEnumerableOfEnumerableSOfObject(值)的ConvertToEnumerableOfEnumerableSOf
.选择(
r=>r.选择(
c=>convertSingleValueToString(c)
)
);
}
私有静态IEnumerable
转换为numerable of numerable对象(
对象值
) {
var ary=对象[,]的值;
if(ari!=null)
返回convertTwoDimary到numerableofEnumerablesof对象(ary)的numerables;
var obj=作为对象的值;
如果(obj!=null)
返回可枚举的。重复(
可枚举。重复(obj,1),1
);
返回Enumerable.Empty();
}
私有静态IEnumerable
将两个DIMARY转换为numerable of numerable对象(
对象[,]元
) {
var firstUpperBound=ary.GetUpperBound(0);
var secondUpperBound=ary.GetUpperBound(1);
返回可枚举的范围(1,firstUpperBound)。选择(
i=>Enumerable.Range(1,第二个上限)。选择(j=>ary[i,j])
);
}
私有静态字符串convertSingleValueToString(对象值){
如果(值==null){
返回字符串。空;
}else if(值为字符串){
以字符串形式返回值;
}else if(值为long){
返回((长)值).ToString(CultureInfo.InvariantCulture);
}else if(值为双精度){
返回((双)值).ToString(CultureInfo.InvariantCulture);
}else if(值为bool){
返回(bool)值?“真”:“假”;
}否则{
返回value.ToString();
}
}
}
xlsx有什么问题?它很复杂,但是有一些解决方案,比如EPPLUS,它可以读/写xlsx,甚至支持LINQ。对于CSV:Excel存储,没有关于此的元数据,因此很难猜出是哪个国家。我建议使用CSV以外的其他文件格式。我知道这并不是一个真正的答案,但是当遇到像1233.44(!)这样的数字时,CSV文件当然会给你带来麻烦。您的用户可以向您发送工作簿吗?甚至制表符分隔的文本文件也会更好。如果我让Excel生成制表符分隔的文本文件,我不会有同样的问题吗?谢谢你的建议。事实上,我现在已经自己建立了一个出口程序。
public static class CsvExporter {
public static void ExportWorksheet (
CsvWriter csvWriter,
Excel.Worksheet worksheet,
Action<int,int> reportProgressCallback = null
) {
foreach (var row in WorksheetToStrings (worksheet, reportProgressCallback))
csvWriter.WriteItems (row);
}
public static IEnumerable<IEnumerable<string>> WorksheetToStrings (
Excel.Worksheet worksheet, Action<int,int> reportProgressCallback = null
) {
var usedRange = worksheet.UsedRange;
return RangeToStrings (usedRange, reportProgressCallback);
}
public static IEnumerable<IEnumerable<string>> RangeToStrings (
Excel.Range range,
Action<int,int> reportProgressCallback = null
) {
if (reportProgressCallback == null)
reportProgressCallback = (line, total) => { };
int rowsTotal = range.Rows.Count;
int currentRow = 0;
foreach (var row in range.Rows) {
++currentRow;
reportProgressCallback (currentRow, rowsTotal);
var rowValues = (row as Excel.Range).Value2 as object;
foreach (
var convertedRow in
convertToEnumerableOfEnumerablesOfStrings (rowValues)
)
yield return convertedRow;
}
}
private static IEnumerable<IEnumerable<string>>
convertToEnumerableOfEnumerablesOfStrings (
object values
) {
return convertToEnumerableOfEnumerablesOfObjects (values)
.Select (
r => r.Select (
c => convertSingleValueToString (c)
)
);
}
private static IEnumerable<IEnumerable<object>>
convertToEnumerableOfEnumerablesOfObjects (
object values
) {
var ary = values as object [,];
if (ary != null)
return convertTwoDimAryToEnumerableOfEnumerablesOfObjects (ary);
var obj = values as object;
if (obj != null)
return Enumerable.Repeat<IEnumerable<object>> (
Enumerable.Repeat<object> (obj, 1), 1
);
return Enumerable.Empty<IEnumerable<object>> ();
}
private static IEnumerable<IEnumerable<object>>
convertTwoDimAryToEnumerableOfEnumerablesOfObjects (
object [,] ary
) {
var firstUpperBound = ary.GetUpperBound (0);
var secondUpperBound = ary.GetUpperBound (1);
return Enumerable.Range (1, firstUpperBound).Select (
i => Enumerable.Range (1, secondUpperBound).Select (j => ary [i, j])
);
}
private static string convertSingleValueToString (object value) {
if ( value == null ) {
return string.Empty;
} else if (value is string) {
return value as string;
} else if (value is long) {
return ((long) value).ToString (CultureInfo.InvariantCulture);
} else if (value is double) {
return ( (double) value ).ToString (CultureInfo.InvariantCulture);
} else if (value is bool) {
return (bool) value ? "TRUE" : "FALSE";
} else {
return value.ToString ();
}
}
}