zoukankan      html  css  js  c++  java
  • 兼容标准的CSV文件读写类

    这几天工作要读写CSV文件,CSV格式虽然很简单,为省事想先从网上找一个现成的,但找了半天只有一个看上去可以,至少读写都支持,在此基础上,按照标准修正了一下,标准才是王道。

    贴一下CSV格式标准规则,来自百度百科 http://baike.baidu.com/view/468993.htm

      1 开头是不留空,以行为单位。

      2 可含或不含列名,含列名则居文件第一行。

      3 一行数据不垮行,无空行。

      4 以半角逗号(即,)作分隔符,列为空也要表达其存在。

      5 列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。

      6 列内容如存在半角引号(即")则应替换成半角双引号("")转义。

      7 文件读写时引号,逗号操作规则互逆。

      8 内码格式不限,可为ASCII、Unicode或者其他。

      9 不支持特殊字符和中文。

    网上看到的,都满足前4条,5和6基本都没考虑,应该只是为处理自己手头工作需要而用。其实只继续努力一下,让它变成通用的,再共享出来就有价值的多。

    此类可以读文件或流的数据,返回DataTable。也可以将DataTable处理后保存成csv文件。

    public class CsvHelper
        {
            public CsvHelper() { }
    
            public CsvHelper(string csvFile)
            {
                this.CsvFile = csvFile;
            }
    
            public string CsvFile { get; set; }
    
            public DataTable Read(Stream stream)
            {
                if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
    
                return Read(new StreamReader(stream));
            }
    
            public DataTable Read()
            {
                if (String.IsNullOrEmpty(CsvFile)) throw new InvalidOperationException("File name can't be null or empty.");
    
                StreamReader reader = new StreamReader(this.CsvFile);
    
                return Read(reader);
            }
    
            private DataTable Read(TextReader reader)
            {
                string line = string.Empty;
                int lineNumber = 0;
                DataTable dt = null;
    
                try
                {
                    while ((line = reader.ReadLine()) != null)
                    {
                        if (lineNumber == 0)
                        {//Create Tole
                            dt = CreateDataTable(line);
                            if (dt.Columns.Count == 0) return null;
                        }
                        else
                        {
                            bool added = CreateDataRow(dt, line);
                            if (!added) break;  //Meet the empty rows.
                        }
                        lineNumber++;
                    }
                }
                finally
                {
                    reader.Close();
                }
                return dt;
            }
    
            public void Write(DataTable dt)
            {
                if (String.IsNullOrEmpty(CsvFile)) throw new InvalidOperationException("File name can't be null or empty.");
    
                Write(dt, this.CsvFile);
            }
    
            public void Write(DataTable dt, string filename)
            {
                var sb = WriteString(dt);
    
                using (StreamWriter writer = new StreamWriter(filename))
                {
                    writer.Write(sb);
                    writer.Flush();
                }
            }
    
            public string WriteString(DataTable dt)
            {
                if (dt == null) throw new ArgumentNullException("dt");
    
                var sb = new System.Text.StringBuilder(9999);
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    if (i > 0) sb.Append(',');
                    sb.Append(dt.Columns[i].ColumnName);
                }
                sb.Append(Environment.NewLine);
    
                foreach (DataRow dr in dt.Rows)
                {
                    for (int i = 0; i < dt.Columns.Count; i++)
                    {
                        var text = dr[i] as string;
                        if (text.IndexOf('"') >= 0) text = text.Replace("\"", "\"\"");  //Replace quote to double quotes.
                        if (text.IndexOf(',') >= 0) text = '"' + text + '"';
    
                        if (i > 0) sb.Append(',');
                        sb.Append(text);
                    }
    
                    sb.Append(Environment.NewLine);
                }
                return sb.ToString();
            }
    
            /// <summary>
            /// Init DataTable's colomns
            /// </summary>
            /// <param name="line"></param>
            /// <returns></returns>
            private DataTable CreateDataTable(string line)
            {
                DataTable dt = new DataTable();
                foreach (string field in line.Split(','))
                {
                    dt.Columns.Add(field);
                }
                return dt;
            }
    
            private bool CreateDataRow(DataTable dt, string line)
            {
                DataRow dr = dt.NewRow();
                string[] fields = new string[dt.Columns.Count];
    
                bool add = true;
                int index = 0;
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    if (index >= line.Length) break;
                    var text = ReadField(ref index, line);
    
                    if (i == 0 && text.Length == 0)
                    {
                        add = false;
                        break;
                    }
                    dr[i] = text;
                }
    
                if (add) dt.Rows.Add(dr);
                return add;
            }
    
            private string ReadField(ref int startIndex, string line)
            {
                int endIndex;
                string text;
                if (line[startIndex] == '"')
                {
                    startIndex++;
                    endIndex = line.IndexOf("\",", startIndex);     //The normal case, except the end of line.
                    if (endIndex == -1)
                    {
                        endIndex = line.IndexOf(',', startIndex);  //Unnormal case, a comma required.
                        if (endIndex == -1) endIndex = line.Length; //Meet string's end.
                        if (line[endIndex - 1] == '"') endIndex--;      //The normal case.
                    }
    
                    text = line.Substring(startIndex, endIndex - startIndex);
                    startIndex = endIndex + 2;
                }
                else
                {
                    endIndex = line.IndexOf(',', startIndex);
                    if (endIndex == -1) endIndex = line.Length; //Meet string's end.
                    text = line.Substring(startIndex, endIndex - startIndex);
                    startIndex = endIndex + 1;
                }
    
                if (text.Contains("\"\"")) text = text.Replace("\"\"", "\"");
                return text;
            }
    
    }
    

    处理边界条件,如行尾时,要特别小心。既要保证功能正确,又要确保一定的健壮性和容错性。

    这是个不依赖状态的类,也就是其中大部分方法都可以转换为静态方法,看你心情了。希望下次有人像我一样偷懒时,能幸运地google到这篇。

  • 相关阅读:
    HTTP/1.1 Status Code Definitions
    宽带到底“宽不宽”
    tmux的简介及使用
    Simple Gesture – Fling
    使用postfix搭建匿名smtp服务器
    dos2unix和unix2dos命令使用 [〓 脚本功略 〓]
    Android Coding: Gestures Builder: create your gestures library
    Android Gesture 手势识别使用实例 Android mobile ITeye论坛
    使用socat进行端口转发
    notepad++在编辑python文件时以4个空格代替TAB
  • 原文地址:https://www.cnblogs.com/XmNotes/p/2227552.html
Copyright © 2011-2022 走看看