最近做了一个小Demo,需要导出CSV文件。
/// <summary>
/// 保存CSV文件
/// </summary>
/// <param name="path">文件夹路径</param>
/// <param name="fileName">文件全路径</param>
/// <param name="msg">要写入的信息</param>
private void SaveCSV(string path, string fileName, string msg)
{
string Folder = Environment.CurrentDirectory + path; // 文件夹路径
if (!System.IO.Directory.Exists(Folder)) // 判断文件夹是否存在
System.IO.Directory.CreateDirectory(Folder); // 创建文件夹
using (TextWriter fw = new StreamWriter(fileName, true)) // 以有序字符写入
{
fw.WriteLine(msg); // 写入数据
}
}
上面代码保存的CSV文件用Excel打开,里面的中文会出现乱码。查了一下是因为Excel使用的编码是和操作系统的编码格式一致。
可以通过dos窗口输入:chcp,查看当前操作系统的编码情况
活动代码页:936
表示编码格式为GBK。
修改了一下程序代码,使用了UTF8的BOM:byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF };
以二进制形式写入
using (Stream source = new FileStream(fileName, FileMode.Append, FileAccess.Write))
{
using (BinaryWriter bw = new BinaryWriter(source, Encoding.UTF8)) // 以二进制形式将基元类型写入流
{
byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //带BOM头
bw.Write(UTF8);
bw.Write(msg + "\r\n");
//bw.Write(Environment.NewLine); // 新行
}
}
这样就不会出现中文乱码了。
使用二进制形式写入会出现写在一行的情况,上面的代码中在msg后加了"\r\n",写出再写入时就会在新行。也可使用Environment.NewLine添加新行。
下面的这段代码保存CSV文件,也不会出现中文乱码
// using表示在{}区间后,自动调用Dispose方法,保证对象被销毁
using (FileStream fs = new FileStream(fileName, FileMode.Append, FileAccess.Write)) // FileStream是指向网络或者硬盘的一个文件的对象
{
using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8)) // 以一种特定的编码向流中写入字符
{
sw.WriteLine(msg); // 写入数据
}
}
下面这个例子是从数据库里查询的数据保存的CSV文件
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SaveCSV
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btSave_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog1 = new SaveFileDialog(); //这是一个文件保存窗口类,这个类负责调用windows资源管理器
saveFileDialog1.Filter = "CSV文件|*.CSV"; //这步是在保存文件的时候,有可选的保存类型,也就是.csv类型
saveFileDialog1.InitialDirectory = "E:\\Project"; //这步是当点击按钮进行保存的时候,弹出的资源管理器界面是E:\\Project
if (saveFileDialog1.ShowDialog() == DialogResult.Cancel)
{
return;
}
else
{
DataSet ds = GetTables(); //定义一个DataSet用来保存从数据库中取出的数据
string fileName1 = "E:\\Project\\MyPractice\\SaveCSV\\CSV.csv"; //这里传入的是FileStream的一个参数,这个参数的意义就是写一个绝对路径+文件名
SaveCSV(ds.Tables[0], fileName1); //将数据库中的表格数据保存成.csv文件
MessageBox.Show("CSV文件保存成功");
}
}
//定义一个容器用来放从数据库中搜索出来的信息
public DataSet GetTables()
{
string sql = @"select * from test"; //所定义的查询语句
using (SqlConnection cnn = new SqlConnection("Server = 127.0.0.1;uid = sa; pwd = ****;database = TestDB"))
{
cnn.Open();
DataSet ds = new DataSet();//定义一个容器DataSet用来存从数据库中得到的数据
SqlDataAdapter sda = new SqlDataAdapter(sql, cnn); //数据库和DataSet之间的桥梁
sda.Fill(ds); //将数据填充到DataSet中
return ds; //返回填充了数据的DataSet
}
}
public void SaveCSV(DataTable dt, String fileName)
{
string data = "";
//FileStream是指向网络或者硬盘的一个文件的对象
using (FileStream fs = new FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write))
{
using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.Default))
{
//写出列名称
for (int i = 0; i < dt.Columns.Count; i++) //遍历DataTable中数据的列名,遍历列
{
data += dt.Columns[i].ColumnName.Tostring();
if (i < dt.Columns.Count - 1) //如果在到达最后一列之前,列名用,分隔
{
data += ",";
}
}
sw.WriteLine(data); //写入StreamWriter流,就是硬盘
//写出各行数据
for (int i = 0; i < dt.Rows.Count; i++) //遍历行
{
data = "";
for (int j = 0; j < dt.Columns.Count; j++) //遍历列
{
// EXCEL里面,默认的情况下,对于字段内容长度超过15位的数字,后面的内容会全部被搞成0
// 数据过长会被转义,所以在这里使用\t处理这种转义
data += "\t";
data += dt.Rows[i][j].Tostring(); //将数据转成字符串
if (j < dt.Columns.Count - 1) //如果在最后一个之前,用,分隔
{
data += ",";
}
}
sw.WriteLine(data); //写入硬盘
}
}
}
}
}
}