2013-02-20 23 views
2

不幸的是,当Excel生成CSV文件时,它不是非常适合程序员,因为它将数字存储在屏幕上。 CSV中数字的外观如何取决于区域设置和用户的喜好。由Excel生成的CSV文件中的数字

结果,我可能最终与数字看上去就像

2 123 
3'322 
1,233.44 
2123,45 

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

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

+1

xlsx有什么问题?这很复杂,但是它有像EPPLUS这样的解决方案,它可以读/写xlsx,甚至支持LINQ。 对于CSV:Excel存储没有关于此的元数据,所以这将很难猜测国家 – 2013-02-20 09:33:14

+0

我会建议使用一些其他文件格式比CSV。我知道这不是一个真正的答案,但当遇到像1,233.44(!)这样的数字时,CSV文件当然会给你带来麻烦。你的用户可以向你发送工作簿吗?甚至制表符分隔的文本文件会更好。 – 2013-02-20 12:39:33

+0

如果我让Excel生成制表符分隔的文本文件,难道我没有同样的问题吗? – JohnB 2013-02-20 12:56:31

回答

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 
+0

谢谢你的建议。事实上,我现在已经自己建立了一个出口程序。 – JohnB 2013-03-04 14:39:35

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


}