在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
读 CsvFileHelper myCsv = new CsvFileHelper(@"C:\Users\administer\Desktop\my6.csv", Encoding.UTF8);
var myData = myCsv.GetListCsvData();
//其他的操作
myCsv.Dispose();
写
CsvFileHelper.SaveCsvFile(@"C:\Users\administer\Desktop\my9.csv", myData, true, new System.Text.UTF8Encoding(false));
下面是有关CSV的介绍(若仅需要使用可忽略不用理会) csv(Comma Separated Values)逗号分隔值,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须象二进制数字那样被解读的数据。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。CSV是一种Excel表格的导出格式,在Excel表格的菜单栏中点击文件->另存为会弹出一个文件夹浏览窗口,在下拉框中可以选择保存格式,其中有一个就是.CSV(逗号分隔符)选项。
在这些常规的约束条件下,存在着许多CSV变体,故CSV文件并不完全互通
最后附上工具类代码如下:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; /******************************************************************************* * Copyright (c) 2016 lijie * All rights reserved. * * 文件名称: * 内容摘要: [email protected] * * 历史记录: * 日 期: 201601010 创建人: lulianqi * 描 述: 创建 *******************************************************************************/ namespace MyCommonHelper.FileHelper { /// <summary> /// 单个元素支持包括tab,换行回车(\r\n),空内容等在内的所有文本字符 (在使用时请确定文件的编码方式) /// 可指定元素分割符,换行官方必须为\r\n(\r\n可以作为内容出现在元素中),转义字符必须为". /// 转义所有的引号必须出现在首尾(如果不在首尾,则不会按转义符处理,直接作为引号处理)[excel可以读取转义出现在中间的情况,而本身存储不会使用这种方式,保存时并会强制修复这种异常,所以这里遇到中间转义的情况直接抛出指定异常] /// 如果在被转义的情况下需要出现引号,则使用2个引号代替(如果需要在首部使用双引号,则需要转义该元素,其他地方可直接使用)(excel对所有双引号都进行转义,无论其出现位置,对于保存方式可以选择是否按excel的方式进行保存) /// 每一行的结尾是不需要逗号结束的,如果多加一个逗号则标识该行会多一个空元素 /// 空行也是一个空元素,一个逗号是2个空元素,所以不可能出现有的行元素为空 /// 使用问题或疑问可通过[email protected]进行联系 /// </summary> public sealed class CsvFileHelper : IDisposable { #region Members //private FileStream _fileStream; private Stream _stream; private StreamReader _streamReader; //private StreamWriter _streamWriter; //private Stream _memoryStream; private Encoding _encoding; //private readonly StringBuilder _columnBuilder = new StringBuilder(100); private Type _type = Type.File; private bool _trimColumns = false; private char _csvSeparator = ','; #endregion Members #region Properties /// <summary> /// Gets or sets whether column values should be trimmed /// </summary> public bool TrimColumns { get { return _trimColumns; } set { _trimColumns = value; } } public Type DataSouceType { get{ return _type;} } /// <summary> /// get or set Csv Separator (Default Values is ,) /// </summary> public char CsvSeparator { get { return _csvSeparator; } set { _csvSeparator = value; } } #endregion Properties #region Enums /// <summary> /// Type enum /// </summary> public enum Type { File, Stream } #endregion Enums #region Methods /// <summary> /// Initialises the reader to work from a file /// </summary> /// <param name="filePath">File path</param> public CsvFileHelper(string filePath):this(filePath, Encoding.Default) { } /// <summary> /// Initialises the reader to work from a file /// </summary> /// <param name="filePath">File path</param> /// <param name="encoding">Encoding</param> public CsvFileHelper(string filePath, Encoding encoding) { _type = Type.File; if (!File.Exists(filePath)) { throw new FileNotFoundException(string.Format("The file '{0}' does not exist.", filePath)); } //_stream = File.OpenRead(filePath); //return a FileStream (OpenRead 源码就是 return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);) _stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); _stream.Position = 0; _encoding = (encoding ?? Encoding.Default); _streamReader = new StreamReader(_stream, _encoding); } /// <summary> /// Initialises the reader to work from an existing stream /// </summary> /// <param name="stream">Stream ( new MemoryStream(Encoding.Default.GetBytes(csvString ?? "")))</param> public CsvFileHelper(Stream stream):this(stream, Encoding.Default) { } /// <summary> /// Initialises the reader to work from an existing stream /// </summary> /// <param name="stream">Stream</param> /// <param name="encoding">Encoding</param> public CsvFileHelper(Stream stream, Encoding encoding) { _type = Type.Stream; if (stream == null) { throw new ArgumentNullException("The supplied stream is null."); } _stream = stream; _stream.Position = 0; _encoding = (encoding ?? Encoding.Default); _streamReader = new StreamReader(_stream, _encoding); } /// <summary> /// Initialises the reader to work from an existing stream (with the Separator char) /// </summary> /// <param name="stream">Stream</param> /// <param name="encoding">Encoding</param> /// <param name="yourSeparator"> the Separator char</param> public CsvFileHelper(Stream stream, Encoding encoding, char yourSeparator): this(stream, encoding) { CsvSeparator = yourSeparator; } /// <summary> /// Initialises the reader to work from an existing string /// </summary> /// <param name="useStringCsv">just set it null</param> /// <param name="csvString">csv string</param> public CsvFileHelper(object useStringCsv , string csvString) : this(new MemoryStream(Encoding.Default.GetBytes(csvString ?? "")), Encoding.Default) { } /// <summary> /// Initialises the reader to work from an existing string /// </summary> /// <param name="useStringCsv">just set it null</param> /// <param name="csvString">csv string</param> /// <param name="yourSeparator"></param> public CsvFileHelper(object useStringCsv,string csvString, char yourSeparator) : this(new MemoryStream(Encoding.Default.GetBytes(csvString ?? "")), Encoding.Default) { CsvSeparator = yourSeparator; } private List<string> ParseLine(string line) { StringBuilder _columnBuilder = new StringBuilder(); List<string> Fields = new List<string>(); bool inColumn = false; //是否是在一个列元素里 bool inQuotes = false; //是否需要转义 bool isNotEnd = false; //读取完毕未结束转义 _columnBuilder.Remove(0, _columnBuilder.Length); // Iterate through every character in the line for (int i = 0; i < line.Length; i++) { char character = line[i]; // If we are not currently inside a column if (!inColumn) { // If the current character is a double quote then the column value is contained within // double quotes, otherwise append the next character inColumn = true; if (character == '"') { inQuotes = true; continue; } } // If we are in between double quotes if (inQuotes) { if ((i + 1) == line.Length)//这个字符已经结束了整行 { if (character == '"') //正常转义结束,且该行已经结束 { inQuotes = false; continue; //当前字符不用添加,跳出后直结束后会添加该元素 } else //异常结束,转义未收尾 { isNotEnd = true; } } else if (character == '"' && line[i + 1] == _csvSeparator) //结束转义,且后面有可能还有数据 { inQuotes = false; inColumn = false; i++; //跳过下一个字符 } else if (character == '"' && line[i + 1] == '"') //双引号转义 { i++; //跳过下一个字符 if (line.Length - 1 == i)//异常结束,转义未收尾 { isNotEnd = true; } } else if (character == '"') //双引号单独出现(这种情况实际上已经是格式错误,为了兼容可暂时不处理) { throw new Exception(string.Format("[{0}]:格式错误,错误的双引号转义 near [{1}] ","ParseLine", line)); } //其他情况直接跳出,后面正常添加 } else if (character == _csvSeparator) inColumn = false; // If we are no longer in the column clear the builder and add the columns to the list if (!inColumn) //结束该元素时inColumn置为false,并且不处理当前字符,直接进行Add { Fields.Add(TrimColumns ? _columnBuilder.ToString().Trim() : _columnBuilder.ToString()); _columnBuilder.Remove(0, _columnBuilder.Length); } else // append the current column _columnBuilder.Append(character); } // If we are still inside a column add a new one (标准格式一行结尾不需要逗号结尾,而上面for是遇到逗号才添加的,为了兼容最后还要添加一次) if (inColumn) { if (isNotEnd) { _columnBuilder.Append("\r\n"); } Fields.Add(TrimColumns ? _columnBuilder.ToString().Trim() : _columnBuilder.ToString()); } //如果inColumn为false,说明已经添加,因为最后一个字符为分隔符,所以后面要加上一个空元素 //另外一种情况是line为""空行,(空行也是一个空元素,一个逗号是2个空元素),正好inColumn为默认值false,在此处添加一空元素 else { Fields.Add(""); } return Fields; } /// <summary> /// 处理未完成的Csv单行 /// </summary> /// <param name="line">数据源</param> /// <returns>元素列表</returns> private List<string> ParseContinueLine(string line) { StringBuilder _columnBuilder = new StringBuilder(); List<string> Fields = new List<string>(); _columnBuilder.Remove(0, _columnBuilder.Length); if (line == "") { Fields.Add("\r\n"); return Fields; } for (int i = 0; i < line.Length; i++) { char character = line[i]; if ((i + 1) == line.Length)//这个字符已经结束了整行 { if (character == '"') //正常转义结束,且该行已经结束 { Fields.Add(TrimColumns ? _columnBuilder.ToString().TrimEnd() : _columnBuilder.ToString()); return Fields; } else //异常结束,转义未收尾 { _columnBuilder.Append("\r\n"); Fields.Add(_columnBuilder.ToString()); return Fields; } } else if (character == '"' && line[i + 1] == _csvSeparator) //结束转义,且后面有可能还有数据 { Fields.Add(TrimColumns ? _columnBuilder.ToString().TrimEnd() : _columnBuilder.ToString()); i++; //跳过下一个字符 Fields.AddRange(ParseLine(line.Remove(0, i+1))); break; } else if (character == '"' && line[i + 1] == '"') //双引号转义 { i++; //跳过下一个字符 if (line.Length - 1 == i)//异常结束,转义未收尾 { _columnBuilder.Append(character); _columnBuilder.Append("\r\n"); Fields.Add(_columnBuilder.ToString()); return Fields; } } else if (character == '"') //双引号单独出现(这种情况实际上已经是格式错误,转义用双引号一定是【,"】【",】形式,包含在里面的双引号需要使用一对双引号进行转义) { throw new Exception(string.Format("[{0}]:格式错误,错误的双引号转义 near [{1}]", "ParseContinueLine", line)); } _columnBuilder.Append(character); } return Fields; } public List<List<string>> GetListCsvData() { _stream.Position = 0; List<List<string>> tempListCsvData = new List<List<string>>(); bool isNotEndLine = false; //这里的ReadLine可能把转义的/r/n分割,需要后面单独处理 string tempCsvRowString = _streamReader.ReadLine(); while (tempCsvRowString!=null) { List<string> tempCsvRowList; if (isNotEndLine) { tempCsvRowList = ParseContinueLine(tempCsvRowString); isNotEndLine = (tempCsvRowList.Count > 0 && tempCsvRowList[tempCsvRowList.Count - 1].EndsWith("\r\n")); List<string> myNowContinueList = tempListCsvData[tempListCsvData.Count - 1]; myNowContinueList[myNowContinueList.Count - 1] += tempCsvRowList[0]; tempCsvRowList.RemoveAt(0); myNowContinueList.AddRange(tempCsvRowList); } else { tempCsvRowList = ParseLine(tempCsvRowString); isNotEndLine = (tempCsvRowList.Count > 0 && tempCsvRowList[tempCsvRowList.Count - 1].EndsWith("\r\n")); tempListCsvData.Add(tempCsvRowList); } tempCsvRowString = _streamReader.ReadLine(); } return tempListCsvData; } public void Dispose() { if(_streamReader!=null) { _streamReader.Dispose(); } if(_stream!=null) { _stream.Dispose(); } } #endregion #region StaticTool #region 编码方式可接受值 //请考虑让应用程序使用 UTF-8 或 Unicode (UTF-16) 作为默认编码。大多数其他编码要么不完整并将许多字符转换为“?”,要么在不同的平台上具有稍有不同的行为。非 Unicode 编码通常具有多义性,应用程序则不再试图确定合适的编码,也不再提供用户用来修复文本语言或编码的更正下拉菜单。 /* This code produces the following output. CodePage identifier and name BrDisp BrSave MNDisp MNSave 1-Byte ReadOnly 37 IBM037 False False False False True True 437 IBM437 False False False False True True 500 IBM500 False False False False True True 708 ASMO-708 True True False False True True 720 DOS-720 True True False False True True 737 ibm737 False False False False True True 775 ibm775 False False False False True True 850 ibm850 False False False False True True 852 ibm852 True True False False True True 855 IBM855 False False False False True True 857 ibm857 False False False False True True 858 IBM00858 False False False False True True 860 IBM860 False False False False True True 861 ibm861 False False False False True True 862 DOS-862 True True False False True True 863 IBM863 False False False False True True 864 IBM864 False False False False True True 865 IBM865 False False False False True True 866 cp866 True True False False True True 869 ibm869 False False False False True True 870 IBM870 False False False False True True 874 windows-874 True True True True True True 875 cp875 False False False False True True 932 shift_jis True True True True False True 936 gb2312 True True True True False True 949 ks_c_5601-1987 True True True True False True 950 big5 True True True True False True 1026 IBM1026 False False False False True True 1047 IBM01047 False False False False True True 1140 IBM01140 False False False False True True 1141 IBM01141 False False False False True True 1142 IBM01142 False False False False True True 1143 IBM01143 False False False False True True 1144 IBM01144 False False False False True True 1145 IBM01145 False False False False True True 1146 IBM01146 False False False False True True 1147 IBM01147 False False False False True True 1148 IBM01148 False False False False True True 1149 IBM01149 False False False False True True 1200 utf-16 False True False False False True 1201 unicodeFFFE False False False False False True 1250 windows-1250 True True True True True True 1251 windows-1251 True True True True True True 1252 Windows-1252 True True True True True True 1253 windows-1253 True True True True True True 1254 windows-1254 True True True True True True 1255 windows-1255 True True True True True True 1256 windows-1256 True True True True True True 1257 windows-1257 True True True True True True 1258 windows-1258 True True True True True True 1361 Johab False False False False False True 10000 macintosh False False False False True True 10001 x-mac-japanese False False False False False True 10002 x-mac-chinesetrad False False False False False True 10003 x-mac-korean False False False False False True 10004 x-mac-arabic False False False False True True 10005 x-mac-hebrew False False False False True True 10006 x-mac-greek False False False False True True 10007 x-mac-cyrillic False False False False True True 10008 x-mac-chinesesimp False False False False False True 10010 x-mac-romanian False False False False True True 10017 x-mac-ukrainian False False False False True True 10021 x-mac-thai False False False False True True 10029 x-mac-ce False False False False True True 10079 x-mac-icelandic False False False False True True 10081 x-mac-turkish False False False False True True 10082 x-mac-croatian False False False False True True 20000 x-Chinese-CNS False False False False False True 20001 x-cp20001 False False False False False True 20002 x-Chinese-Eten False False False False False True 20003 x-cp20003 False False False False False True 20004 x-cp20004 False False False False False True 20005 x-cp20005 False False False False False True 20105 x-IA5 False False False False True True 20106 x-IA5-German False False False False True True 20107 x-IA5-Swedish False False False False True True 20108 x-IA5-Norwegian False False False False True True 20127 us-ascii False False True True True True 20261 x-cp20261 False False False False False True 20269 x-cp20269 False False False False True True 20273 IBM273 False False False False True True 20277 IBM277 False False False False True True 20278 IBM278 False False False False True True 20280 IBM280 False False False False True True 20284 IBM284 False False False False True True 20285 IBM285 False False False False True True 20290 IBM290 False False False False True True 20297 IBM297 False False False False True True 20420 IBM420 False False False False True True 20423 IBM423 False False False False True True 20424 IBM424 False False False False True True 20833 x-EBCDIC-KoreanExtended False False False False True True 20838 IBM-Thai False False False False True True 20866 koi8-r True True True True True True 20871 IBM871 False False False False True True 20880 IBM880 False False False False True True 20905 IBM905 False False False False True True 20924 IBM00924 False False False False True True 20932 EUC-JP False False False False False True 20936 x-cp20936 False False False False False True 20949 x-cp20949 False False False False False True 21025 cp1025 False False False False True True 21866 koi8-u True True True True True True 28591 iso-8859-1 True True True True True True 28592 iso-8859-2 True True True True True True 28593 iso-8859-3 False False True True True True 28594 iso-8859-4 True True True True True True 28595 iso-8859-5 True True True True True True 28596 iso-8859-6 True True True True True True 28597 iso-8859-7 True True True True True True 28598 iso-8859-8 True True False False True True 28599 iso-8859-9 True True True True True True 28603 iso-8859-13 False False False False True True 28605 iso-8859-15 False True True True True True 29001 x-Europa False False False False True True 38598 iso-8859-8-i True True True True True True 50220 iso-2022-jp False False True True False True 50221 csISO2022JP False True True True False True 50222 iso-2022-jp False False False False False |
请发表评论