Excel生成的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)。我

不幸的是,Excel在生成CSV文件时对程序员不是很友好,因为它存储屏幕上显示的数字。因此,数字在CSV中的外观取决于区域设置和用户的喜好

因此,我可能最终会得到如下的数字

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 ();
        }
    }


}