版权声明:转载需注明本博客的链接 https://blog.csdn.net/someboy53/article/details/82120199
今天在给另一个系统准备数据时,需要将很多个CSV文件转换成Excel文件。本来说这么简单一个事情,网上应该有现成的解决方案或者程序来处理,结果找了一下午,发现不是这儿有问题,就是那儿有问题,最大的问题就是转移后的Excel的长数字存在丢失精度的现象。
当然丢失精度的问题,可以通过对CSV文件进行处理解决,有以下几种办法:
- 在长数字字符串之前增加单引号,但是如果有一列长数字是空时,这个单引号就会显示出来
- 用双引号将长数字字符串包住,再加前面加上=号,以给Excel说明这个字段是一个字符串公式,但是和上面有同样的问题
最后只能自己编程解决,引用Aspose.Cells的早期版本,因为新版要给钱。(当然也可以使用Microsoft.Office.Excel类,稍微要麻烦一点),思路为:
- 读取CSV所在目录中所有CSV文件
- 对文件进行循环处理
- 每次读入1024个字节,并判断是否为另一行(0x2a && 引号成对)
- 如果一行完结,则将之前的这一行的数据写入Excel的行中
- 如果未完结,则继续读取
- 文件读完,设置之前所有单元格为文本格式,并写Excel文件,并开始下一文件。
核心代码如下:
class AsposeExcel
{
private string outFileName = "";
private Workbook book = null;
private Worksheet sheet = null;
private int startRow = 0;
public AsposeExcel()
{
}
public AsposeExcel(string outfilename)
{
outFileName = outfilename;
}
private void NewBook()
{
book = new Workbook();
sheet = book.Worksheets[0];
qLevel = 0;
sb = new StringBuilder();
lineEnd = false;
cells = new List<string>();
startRow = 0;
maxColumns = 0;
}
//引号层级
private int qLevel = 0;
private System.Text.StringBuilder sb = new StringBuilder();
private bool lineEnd = false;
private int maxColumns = 0;
//private char[] lastChars;
private List<string> cells = new List<string>();
private void AddBody(char[] chars, int len)
{
int i;
for (i = 0; i < len; i++)
{
if (chars[i] == ',' && qLevel == 0)
{
//一段结束,开始下一段
cells.Add(sb.ToString());
sb.Clear();
continue;
}
if (chars[i] == '"' && qLevel == 0)
{
qLevel = 1;
continue;
}
if (chars[i] == '"' && qLevel == 1)
{
qLevel = 0;
continue;
}
if (chars[i] == 0x0a && qLevel == 0)
{
lineEnd = true;
//break;
}
sb.Append(chars[i]);
if (lineEnd)
{
cells.Add(sb.ToString());
sb.Clear();
for (int col = 0; col < cells.Count; col++)
{
Cell cell = null;
cell = sheet.Cells[startRow, col];
cell.PutValue(cells[col]);
}
maxColumns = maxColumns > cells.Count ? maxColumns : cells.Count;
startRow += 1;
lineEnd = false;
cells.Clear();
}
}
}
private void Save()
{
try
{
Aspose.Cells.Range r = sheet.Cells.CreateRange(0, 0, startRow, maxColumns);
Aspose.Cells.Style s = book.Styles[book.Styles.Add()];
s.Custom = "@";
StyleFlag sf = new StyleFlag();
sf.NumberFormat = true;
r.ApplyStyle(s, sf);
sheet.AutoFitColumns();
book.Save(outFileName);
}
catch (Exception e)
{
throw e;
}
}
public void convert(FileInfo fi, string outputPath)
{
this.NewBook();
FileStream fs = fi.OpenRead();
TextReader tr = new StreamReader(fs, Encoding.GetEncoding("gb2312"), true);
this.outFileName = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(fi.Name) + ".xlsx");
//ae.AddHeader(tr.ReadLine());
char[] buf = new char[1024];
int len = tr.Read(buf, 0, 1024);
while (len > 0)
{
AddBody(buf, len);
len = tr.Read(buf, 0, 1024);
}
Save();
}
}
最后附上编译好的文件,需要.net 4.0 Framework