CSV文件格式标准:
- 编码方式:UTF-8
- 行分割符为换行符(\r\n); 列分割符为英文逗号(,);
- 内容行 第一行为标题行(即列名);
- 约定特殊字符处理标准:
数据源文本 | 目标文本 | |
英文逗号(,) | 英文逗号(,) | 出现左侧一种或多中情况时,在文本两侧加上英文冒号(“ ) |
英文冒号(“ ) | 替换成两个英文冒号(“”) | |
换行符(\n) | 换行符(\n) | |
回车符(\r) | 回车符(\r) | |
回车换行符(\r\n) | 替换成(\r/\n) |
操作示例:
private DataTable GetData()
{
DataTable dta = new DataTable();
dta.Columns.Add("Code");
dta.Columns.Add("Name");
dta.Columns.Add("Address");
dta.Rows.Add("1", "正常", "中国北京");
dta.Rows.Add("1", "逗号", "中国,北京");
dta.Rows.Add("1", "换行", "中国\n北京");
dta.Rows.Add("1", "回车换行", "中国\r\n北京");
dta.Rows.Add("1", "逗号换行", "中,国北\n京");
dta.Rows.Add("1", "1个单引号", "中国'北京");
dta.Rows.Add("1", "2个单引号", "中国'北'京");
dta.Rows.Add("1", "1个双引号", "中国\"北京");
dta.Rows.Add("1", "2个双引号", "中国\"北\"京");
dta.Rows.Add("1", "1个单双引号", "中国'北\"京");
return dta;
}
private void WCsv_Loaded(object sender, RoutedEventArgs e)
{
string path = $@"C:\Users\Administrator\Desktop\ABC.csv";
string path2 = $@"C:\Users\Administrator\Desktop\ABC2.csv";
DataTable dta = GetData();
string errInfo;
//把数据集写到文件1
bool isOk =FuncCsv. CsvWrite(dta, path, out errInfo);
//读取文件1
DataTable dt2 = FuncCsv.CsvRead(path, out errInfo);
//把文件1的数据写到文件2,比较文件是否一致
bool isOk2= FuncCsv.CsvWrite(dt2, path2, out errInfo);
}
自定义类:
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
namespace Wpfjsj
{
/// <summary>
/// CSV自定义类
/// </summary>
public static class FuncCsv
{
private static readonly object Obj = new object();
/// <summary>
/// 把数据集写到CSV文件
/// </summary>
/// <param name="dt">数据集</param>
/// <param name="path">文件路径</param>
/// <param name="errInfo">异常信息</param>
/// <param name="en">编码方式 默认为空(UFT-8)</param>
/// <param name="isConHeader">是否包含标题行(列名) 默认包含</param>
/// <returns>返回值 写入是否成功</returns>
public static bool CsvWrite(DataTable dt, string path, out string errInfo, Encoding en = null, bool isConHeader = true)
{
lock (Obj)
{
try
{
errInfo = "";
//以半角逗号(即,)作分隔符,列为空也要表达其存在。
//列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。
//列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。
StringBuilder sb = new StringBuilder();
if (isConHeader)
{
for (int m = 0; m < dt.Columns.Count; m++)
{
sb.Append(dt.Columns[m].ColumnName);
if (m != dt.Columns.Count - 1)
sb.Append(",");
}
sb.Append("\r\n");
}
foreach (DataRow row in dt.Rows)
{
for (int i = 0; i < dt.Columns.Count; i++)
{
var colum = dt.Columns[i];
if (i != 0) sb.Append(",");
string str = row[colum].ToString();
sb.Append(colum.DataType == typeof(string) ? ConvStr_CsvWrite(str) : str);
}
sb.Append("\r\n");
}
StreamWriter sw = new StreamWriter(path, false, en ?? Encoding.UTF8);
sw.Write(sb);
sw.Flush();
sw.Close();
return true;
}
catch (Exception ex)
{
errInfo = ex.Message;
return false;
}
}
}
private static readonly object Obj1 = new object();
/// <summary>
/// 读取CSV文件内容到数据集
/// </summary>
/// <param name="path">文件路径</param>
/// <param name="errInfo">异常信息</param>
/// <param name="en">编码方式 默认为空(UFT-8)</param>
/// <param name="isConHeader">文件内容是否包含标题行(列名),默认包含</param>
/// <returns>返回的数据集</returns>
public static DataTable CsvRead(string path, out string errInfo, Encoding en = null, bool isConHeader = true)
{
lock (Obj1)
{
errInfo = "";
DataTable dt = new DataTable();
try
{
//读取文件
StreamReader sr = new StreamReader(path, en ?? Encoding.UTF8);
string result = sr.ReadToEnd();
sr.Close();
//解析文件
int index = 0;
foreach (string content in result.Split(new[] {"\r\n"}, StringSplitOptions.None))
{
if (string.IsNullOrWhiteSpace(content)) continue;
string[] strCols = StrToStrArray(content);
if (index == 0)
{
if (isConHeader)
{
foreach (string str in strCols)
dt.Columns.Add(str);
index++;
continue;
}
//自定义标题
for (int m = 1; m <= strCols.Length; m++)
dt.Columns.Add($"Col{m}");
index = 1;
}
dt.Rows.Add(strCols);
}
}
catch (Exception ex)
{
errInfo = ex.Message;
}
return dt;
}
}
private static string[] StrToStrArray(string strLine)
{
string[] strFirsts = strLine.Split(',');
List<string> list = new List<string>();
StringBuilder sbCombie = new StringBuilder();
foreach (string str in strFirsts)
{
int indexOneFirst = str.IndexOf('\"');
//常规字符串
if (sbCombie.Length == 0 && indexOneFirst < 0)
{
list.Add(str);
continue;
}
//字符串末尾 冒号连续出现的次数为奇数 则字符串结束
int count = 0;
for (int n = str.Length - 1; n >= 0; n--)
{
if (str[n] == '\"')
count++;
else
break;
}
//源字符串带冒号 不带逗号
if (sbCombie.Length == 0 && indexOneFirst == 0 && count % 2 == 1)
{
list.Add(ConvStr_CsvRead(str));
continue;
}
if (count % 2 == 0)
{
sbCombie.Append($"{str},");
}
else
{
sbCombie.Append(str);
list.Add(ConvStr_CsvRead(sbCombie.ToString()));
sbCombie.Clear();
}
}
return list.ToArray();
}
private static string ConvStr_CsvRead(string str)
{
if (str.Contains('\"'))
{
str = str.Replace("\r/\n", "\r\n").Replace("\"\"", "\"");
return str.Substring(1, str.Length - 2);
}
return str;
}
private static string ConvStr_CsvWrite(string str)
{
if (str.Contains(',') || str.Contains('\n') || str.Contains('\r') || str.Contains('"'))
return $"\"{str.Replace("\r\n", "\r/\n").Replace("\"", "\"\"")}\"";
return str;
}
}
}