2

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

结果,我可能最终得到的数字看起来像

2 123
3'322
1,233.44
2123,45

等等

现在我必须在我的程序中处理这样的 CSV 文件。

除了告诉用户如何构建他们的 CSV 文件之外——是否有任何智能或规范的解决方案来处理这个问题?切换到另一种数据格式也是一种选择——但是哪一种呢?它必须由 Excel 原生支持,并且应该易于处理(所以我想避免使用 xlsx)。

4

2 回答 2

2

我和一个客户有同样的问题,我给他们写了一个导出到 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
于 2013-02-28T14:13:54.210 回答
0

到目前为止,除了自己导出文件之外,我还没有找到任何更好的解决方案(CsvWriter是一个自定义类,但很简单——CsvWriter.WriteItems需要一个可枚举的字符串,必要时引用它们,然后在 csv 文件中用它们构建一行):

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


}
于 2013-03-04T14:46:50.610 回答