CSV文件对逗号和引号字符的转义处理,使用EXCEL打开时在一个单元格内

137 篇文章 9 订阅

CSV文件本质是一种用逗号和(回车)换行符分割的文本文件,可以直接用Excel打开
             * CSV (逗号分隔值文件格式)Comma-Separated Values 【每一行的分割符必须是英文的逗号,不能是中文的逗号】
             * 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。
             * 纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。
             * CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。
             * 通常,所有记录都有完全相同的字段序列。通常都是纯文本文件.
             * 建议使用WORDPAD或是记事本来开启,再则先另存新档后用EXCEL开启,也是方法之一。
             * 
             * CSV格式是分隔的数据格式,有字段/列分隔的逗号字符和记录/行分隔换行符。
             * 字段包含特殊字符(逗号,换行符,或双引号),必须以双引号括住。行内包含一个项目是空字符串,可以以双引号括住。
             * 字段的值包含双引号时,要双写这个双引号(就像把一个双引号当做转义符一样)。
             * CSV文件格式并不需要特定的字符编码,字节顺序,或行终止格式。
             * 每个记录是一个行终止换行符(ASCII码/LF = 0x0A) 或 回车换行符(ASCII码/CRLF = 0x0D0A)。
             * 0x0A【换行符】 在C#中代表字符 '\n'
             * 0x0D0A【回车换行】 在C#中代表字符串 "\r\n" 
             * 
             * 转义(Escaped)要求:
             * 包含逗号, 双引号, 或是换行符的字段必须放在引号内.【使用引号来进行特殊符号 Escaped转义】
             * 字段内部的引号必须在其前面增加一个引号来实现文字引号的转码.
             * 
             * 规则:
             * 1 开头是不留空,以行为单位。
             * 2 可含或不含列名,含列名则居文件第一行。
             * 3 一行数据不跨行,无空行。
             * 4 以半角英文逗号(即,)作分隔符,列为空也要表达其存在。
             * 5 列内容如存在半角引号(即"),替换成半角双引号("")转义,即用半角引号(即"")将该字段值包含起来。
             * 6 文件读写时引号,逗号操作规则互逆。
             * 7 内码格式不限,可为 ASCII、Unicode 或者其他。
             * 8 不支持数字
             * 9 不支持特殊字符

我们使用时,如果不对引号 或者 逗号 进行特殊处理,使用Excel打开就会排列不美观,如图:

如果进行转义(Escaped)处理,如图:

对CSV文件的每一项都进行转义处理,关键函数如下:

        /// <summary>
        /// 处理csv文件中的双引号和逗号,使其在Excel中完美显示为一个单元格
        /// 斯内科 20210321
        /// </summary>
        /// <param name="srcStr"></param>
        /// <returns></returns>
        private static string ProcessPunctuationForCsv(string srcStr)
        {
            if (srcStr == null)
            {
                return string.Empty;
            }
            bool quoteFlag = false;//是否添加过双引号
            //如果存在双引号,需要将字符串的一个双引号 替换为 两个双引号。并且需要在字符串的前后加上双引号
            if (srcStr.Contains("\""))
            {
                srcStr = srcStr.Replace("\"", "\"\"");
                srcStr = "\"" + srcStr + "\"";
                quoteFlag = true;
            }
            //如果只存在逗号(不存在引号),将前后加引号即可
            if (srcStr.Contains(",") && !quoteFlag)
            {
                srcStr = "\"" + srcStr + "\"";
            }
            return srcStr;
        }

下面使用Winform程序进行验证:

新建窗体应用程序CsvFileEscapedDemo,将默认的Form1重命名为 FormCsvFileEscaped

窗体设计如图:

新建测试类Employee.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CsvFileEscapedDemo
{
    /// <summary>
    /// 员工信息
    /// </summary>
    public class Employee
    {
        public Employee()
        {
            this.ListSkills = new List<string>();
        }

        /// <summary>
        /// 编号
        /// </summary>
        public int CoreId { get; set; }

        /// <summary>
        /// 姓名
        /// </summary>
        public string EmployeeName { get; set; }

        /// <summary>
        /// 地址
        /// </summary>
        public string Address { get; set; }

        /// <summary>
        /// 技能列表
        /// </summary>
        public List<string> ListSkills { get; set; }
    }
}

新建读写CSV的相关类CsvUtil

相关程序如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CsvFileEscapedDemo
{
    /// <summary>
    /// 对CSV文件进行读写
    /// </summary>
    public class CsvUtil
    {

        /*
         * 1:csv与Excel本质区别是文本文件
         * 2:每个单元格的数据用,分割
         * 3:每行的数据用\r\n分割
         */

        private static Object thisLock = new Object();

        /// <summary>
        /// 创建Csv
        /// </summary>
        /// <param name="_path">路径</param>
        /// <param name="_name">名称</param>
        /// <returns></returns>
        public static bool CreateCsv(string _path, string _name)
        {
            lock (thisLock)
            {
                try
                {
                    using (File.Create(_path + _name + ".csv"))
                    {

                    }
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 删除Csv
        /// </summary>
        /// <param name="_path">路径</param>
        /// <param name="_name">名称</param>
        /// <returns></returns>
        public static bool DeleteCsv(string _path, string _name)
        {
            string _tmPath = _path + _name + ".csv";
            lock (thisLock)
            {
                try
                {
                    if (File.Exists(_tmPath) == true)
                    {
                        File.Delete(_tmPath);
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show("CSV文件删除失败:" + e.Message);
                    return false;
                }
            }
        }

        /// <summary>
        /// 读取csv
        /// </summary>
        /// <param name="_path">路径,如 @"D:\MESLog\ABC\"</param>
        /// <param name="_name">名称,不带.csv</param>
        /// <param name="_recvData">读取到的数据</param>
        /// <returns></returns>
        public static bool ReadCsv(string _path, string _name, ref List<string[]> _recvData)
        {
            return ReadCsv(Path.Combine(_path, _name + ".csv"), ref _recvData);
        }
        /// <summary>
        /// 读取csv
        /// </summary>
        /// <param name="_path">路径,如 @"D:\MESLog\ABC\"</param>
        /// <param name="_name">名称,不带.csv</param>
        /// <param name="_recvData">读取到的数据</param>
        /// <param name="errorMsg">异常信息</param>
        /// <returns></returns>
        public static bool ReadCsv(string _path, string _name, ref List<string[]> _recvData, ref string errorMsg)
        {
            return ReadCsv(Path.Combine(_path, _name + ".csv"), ref _recvData, ref errorMsg);
        }
        /// <summary>
        /// 读取csv文件
        /// </summary>
        /// <param name="_fullfilePath">全路径</param>
        /// <param name="recvData">返回的值</param>
        /// <returns></returns>
        public static bool ReadCsv(string fullfilePath, ref List<string[]> recvData)
        {
            string errorMsg = "";
            return ReadCsv(fullfilePath, ref recvData, ref errorMsg);
        }
        /// <summary>
        /// 读取csv文件
        /// </summary>
        /// <param name="_fullfilePath">全路径</param>
        /// <param name="recvData">返回的值</param>
        /// <returns></returns>
        public static bool ReadCsv(string fullfilePath, ref List<string[]> recvData, ref string errorMsg)
        {
            lock (thisLock)
            {
                try
                {
                    if (!fullfilePath.Contains(".csv"))
                    {
                        errorMsg = "路径中没有.csv后缀";
                        return false;
                    }
                    string _tmPath = fullfilePath;
                    using (StreamReader reader = new StreamReader(_tmPath, Encoding.Default))
                    {
                        string _lin = reader.ReadToEnd().Trim();
                        string[] _str = _lin.Split('\n');
                        recvData.Clear();
                        for (int i = 0; i < _str.Length; i++)
                        {
                            _str[i] = _str[i].TrimEnd('\r');
                            recvData.Add(_str[i].Split(','));
                        }
                    }
                    return true;
                }
                catch (Exception e)
                {
                    errorMsg = e.Message;
                    MessageBox.Show("CSV文件读取失败:" + e.Message);
                    return false;
                }
            }
        }

        /// <summary>
        /// 写入csv文件
        /// </summary>
        /// <param name="_path">路径,如 @"D:\MESLog\ABC\"</param>
        /// <param name="_name">名称,不带.csv</param>
        /// <param name="_writeData">需要写入的数据</param>
        /// <param name="_append">是否拼接</param>
        /// <returns></returns>
        public static bool WriteCsv(string _path, string _name, List<string[]> _writeData, bool _append)
        {
            return WriteCsv(Path.Combine(_path, _name + ".csv"), _writeData, _append);
        }
        /// <summary>
        /// 写入csv文件
        /// </summary>
        /// <param name="_path">路径,如 @"D:\MESLog\ABC\"</param>
        /// <param name="_name">名称,不带.csv</param>
        /// <param name="_writeData">需要写入的数据</param>
        /// <param name="_append">是否拼接</param>
        /// <returns></returns>
        public static bool WriteCsv(string _path, string _name, List<string[]> _writeData, bool _append, ref string errorMsg)
        {
            return WriteCsv(Path.Combine(_path, _name + ".csv"), _writeData, _append, ref errorMsg);
        }

        /// <summary>
        /// 写入csv文件
        /// </summary>
        /// <param name="fullfilePath">文件全路径</param>
        /// <param name="_writeData">需要写入的数据</param>
        /// <param name="_append">是否拼接</param>
        /// <returns></returns>
        public static bool WriteCsv(string fullfilePath, List<string[]> writeData, bool append)
        {
            string errorMsg = "";
            return WriteCsv(fullfilePath, writeData, append, ref errorMsg);
        }

        /// <summary>
        /// 写入csv文件【不使用Escaped转义】
        /// </summary>
        /// <param name="fullfilePath">文件全路径</param>
        /// <param name="_writeData">需要写入的数据</param>
        /// <param name="_append">是否拼接</param>
        /// <returns></returns>
        public static bool WriteCsv(string fullfilePath, List<string[]> _writeData, bool _append, ref string errorMsg)
        {
            lock (thisLock)
            {
                try
                {
                    if (fullfilePath.Length < 1)
                    {
                        return false;
                    }
                    string directoryName = System.IO.Path.GetDirectoryName(fullfilePath);
                    //================判断文件夹是否存在,不存在就创建=====================================//
                    DirectoryInfo directoryInfo = new DirectoryInfo(directoryName);
                    if (!directoryInfo.Exists)
                    {
                        directoryInfo.Create();
                    }
                    //=====================================================//
                    if (!fullfilePath.Contains(".csv"))
                    {
                        errorMsg = "路径中没有.csv后缀";
                        return false;
                    }
                    string _tmPath = fullfilePath;
                    using (StreamWriter write = new StreamWriter(_tmPath, _append, Encoding.Default))
                    {
                        foreach (string[] strArr in _writeData)
                        {
                            write.WriteLine(string.Join(",", strArr));
                        }
                    }
                    return true;
                }
                catch (Exception e)
                {
                    errorMsg = e.Message;
                    MessageBox.Show("CSV文件写入失败:" + e.Message);
                    return false;
                }
            }
        }

        /// <summary>
        /// 写入csv文件【使用双引号进行Escaped转义】
        /// </summary>
        /// <param name="fullfilePath"></param>
        /// <param name="_writeData"></param>
        /// <param name="_append"></param>
        /// <param name="errorMsg"></param>
        /// <returns></returns>
        public static bool WriteCsvWithEscaped(string fullfilePath, List<string[]> _writeData, bool _append, ref string errorMsg)
        {
            errorMsg = "";
            if (_writeData == null)
            {
                errorMsg = "没有要写入的csv数据";
                return false;
            }
            //修正所有元素
            for (int i = 0; i < _writeData.Count; i++)
            {
                string[] dataArray = _writeData[i];
                for (int j = 0; dataArray != null && j < dataArray.Length; j++)
                {
                    dataArray[j] = ProcessPunctuationForCsv(dataArray[j]);
                }
                _writeData[i] = dataArray;
            }
            return WriteCsv(fullfilePath, _writeData, _append, ref errorMsg);
        }

        /// <summary>
        /// 处理csv文件中的双引号和逗号,使其在Excel中完美显示为一个单元格
        /// 斯内科 20210321
        /// </summary>
        /// <param name="srcStr"></param>
        /// <returns></returns>
        private static string ProcessPunctuationForCsv(string srcStr)
        {
            if (srcStr == null)
            {
                return string.Empty;
            }
            bool quoteFlag = false;//是否添加过双引号
            //如果存在双引号,需要将字符串的一个双引号 替换为 两个双引号。并且需要在字符串的前后加上双引号
            if (srcStr.Contains("\""))
            {
                srcStr = srcStr.Replace("\"", "\"\"");
                srcStr = "\"" + srcStr + "\"";
                quoteFlag = true;
            }
            //如果只存在逗号(不存在引号),将前后加引号即可
            if (srcStr.Contains(",") && !quoteFlag)
            {
                srcStr = "\"" + srcStr + "\"";
            }
            return srcStr;
        }
    }
}

窗体相关主要程序(忽略设计器自动生成的代码)如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CsvFileEscapedDemo
{
    public partial class FormCsvFileEscaped : Form
    {
        public FormCsvFileEscaped()
        {
            InitializeComponent();
        }
        private void btnCsvWithoutEscaped_Click(object sender, EventArgs e)
        {
            //添加对 System.Web.Extensions的引用
            System.Web.Script.Serialization.JavaScriptSerializer javaScriptSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            Employee employee = new Employee()
            {
                CoreId = 1,
                EmployeeName = "徐暮云",
                Address = "洛阳",
                ListSkills = new List<string>() { "疾风怒涛", "混沌千秋", "寒溟化蛟" }
            };
            string jsonString = javaScriptSerializer.Serialize(employee);
            string errorMsg = "";
            List<string[]> writeData = new List<string[]>();
            writeData.Add(new string[] { "编号", employee.CoreId.ToString() });
            writeData.Add(new string[] { "姓名", employee.EmployeeName });
            writeData.Add(new string[] { "地址", employee.Address });
            writeData.Add(new string[] { "技能列表", string.Join(",", employee.ListSkills) });
            writeData.Add(new string[] { "Json对象", jsonString });
            string fileName = AppDomain.CurrentDomain.BaseDirectory + "sword5\\xumuyun.csv";
            bool result = CsvUtil.WriteCsv(fileName, writeData, true, ref errorMsg);
            MessageBox.Show($"写入CSV文件的结果:{result}\n路径:{fileName}");
        }

        private void btnCsvWithEscaped_Click(object sender, EventArgs e)
        {
            //添加对 System.Web.Extensions的引用
            System.Web.Script.Serialization.JavaScriptSerializer javaScriptSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            Employee employee = new Employee()
            {
                CoreId = 3,
                EmployeeName = "皇甫朝云",
                Address = "成都",
                ListSkills = new List<string>() { "轩辕服太虚", "持国护天" }
            };
            string jsonString = javaScriptSerializer.Serialize(employee);
            string errorMsg = "";
            List<string[]> writeData = new List<string[]>();
            writeData.Add(new string[] { "编号", employee.CoreId.ToString() });
            writeData.Add(new string[] { "姓名", employee.EmployeeName });
            writeData.Add(new string[] { "地址", employee.Address });
            writeData.Add(new string[] { "技能列表", string.Join(",", employee.ListSkills) });
            writeData.Add(new string[] { "Json对象", jsonString });
            string fileName = AppDomain.CurrentDomain.BaseDirectory + "sword5\\huangfuzhaoyun.csv";
            bool result = CsvUtil.WriteCsvWithEscaped(fileName, writeData, true, ref errorMsg);
            MessageBox.Show($"写入CSV文件的结果:{result}\n路径:{fileName}");
        }
    }
}

程序运行结果:


            

  • 3
    点赞
  • 2
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

斯内科

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值