【问题标题】:How do I handle line breaks in a CSV file using C#?如何使用 C# 处理 CSV 文件中的换行符?
【发布时间】:2010-11-13 19:55:09
【问题描述】:

我有一个 Excel 电子表格正在 C# 中转换为 CSV 文件,但在处理换行符时遇到问题。例如:

"John","23","555-5555"

"Peter","24","555-5
555"

"Mary,"21","555-5555"

当我阅读 CSV 文件时,如果记录不是以双引号 (") 开头,则错误地存在换行符,我必须将其删除。我有一些来自互联网的 CSV 阅读器类,但我是担心他们会在换行时失败。

我应该如何处理这些换行符?


非常感谢大家的帮助。

这是我到目前为止所做的。我的记录有固定的格式,都以

开头
JTW;...;....;...;

JTW;...;...;....

JTW;....;...;..

..;...;... (wrong record, line break inserted)

JTW;...;...

所以我在每一行的 [3] 位置检查了;。如果是真的,我会写;如果为假,我将在最后一个(删除换行符)追加

我现在遇到了问题,因为我将文件保存为 txt。

顺便说一句,我通过在 Excel 中另存为 csv 将 Excel 电子表格转换为 csv。但我不确定客户是否这样做。

所以作为 TXT 的文件是完美的。我检查了记录和总数。但现在我必须将它转换回 csv,我真的很想在程序中这样做。有人知道怎么做吗?

这是我的代码:

namespace EditorCSV
{
    class Program
    {
        static void Main(string[] args)
        {
            ReadFromFile("c:\\source.csv");
        }

        static void ReadFromFile(string filename)
        {
            StreamReader SR;
            StreamWriter SW;
            SW = File.CreateText("c:\\target.csv");
            string S;
            char C='a';
            int i=0;
            SR=File.OpenText(filename);
            S=SR.ReadLine();
            SW.Write(S);
            S = SR.ReadLine();
            while(S!=null)
            {
                try { C = S[3]; }
                catch (IndexOutOfRangeException exception){
                    bool t = false;
                    while (t == false)
                    {
                        t = true;
                        S = SR.ReadLine();
                        try { C = S[3]; }
                        catch (IndexOutOfRangeException ex) { S = SR.ReadLine(); t = false; }

                    }
                }
                if( C.Equals(';'))
                {
                    SW.Write("\r\n" + S);
                    i = i + 1;
                }
                else
                {
                    SW.Write(S);

                }
                S=SR.ReadLine();
            }
            SR.Close();
            SW.Close();
            Console.WriteLine("Records Processed: " + i.ToString() + " .");
            Console.WriteLine("File Created SucacessFully");
            Console.ReadKey();


        }

    }
} 

【问题讨论】:

  • 我可能会误解,但是您是否将 excel 文件解析为 csv,然后当您尝试读取它时遇到问题?如果是这种情况,您用于将 excel 文件解析为 csv 的代码是什么样的?
  • 您是在生成 csv,还是在读取 csv,还是两者兼而有之?
  • 啊解析 CSV 的乐趣。它看起来很简单,但有一些令人讨厌的情况。

标签: c# csv


【解决方案1】:

CSV 具有预定义的处理方式。该站点提供了对标准way to handle all the caveats of CSV 的易于阅读的说明。

尽管如此,确实没有理由不使用可靠的开源库来读写 CSV 文件以避免犯非标准错误。 LINQtoCSV 是我最喜欢的库。它以干净简单的方式支持阅读和写作。

或者,this SO question on CSV libraries 将为您提供最受欢迎的选择列表。

【讨论】:

    【解决方案2】:

    不要检查当前行是否缺少 (") 作为第一个字符,而是检查最后一个字符是否是 (")。如果不是,你知道你有一个换行符,你可以读取下一行并将其合并在一起。

    我假设您的示例数据是准确的 - 字段用引号括起来。如果引号可能无法分隔文本字段(或以某种方式在非文本数据中找到换行符),那么所有赌注都将失败!

    【讨论】:

    • 某些 CSV 应用程序在生成 CSV 文件时不会将每个字段都用引号括起来,因此这可能是一个有问题的解决方案。
    • 当然,我假设他的示例数据是准确的 - 字段用引号括起来。如果引号可能无法分隔文本字段(或以某种方式在非文本数据中找到换行符),那么所有的赌注都没有了!
    • 道格,也许把假设放在你的答案中
    【解决方案3】:

    .NET 中有一个读取 CSV 文件的内置方法(需要添加 Microsoft.VisualBasic 程序集参考):

    public static IEnumerable<string[]> ReadSV(TextReader reader, params string[] separators)
    {
        var parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(reader);
        parser.SetDelimiters(separators);
        while (!parser.EndOfData)
            yield return parser.ReadFields();
    }
    

    如果您要处理非常大的文件,这个 CSV 阅读器声称是您能找到的最快的阅读器:http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader

    【讨论】:

      【解决方案4】:

      我最近使用这段代码来解析 CSV 文件中的行(这是一个简化版本):

      private void Parse(TextReader reader)
          {
              var row = new List<string>();
              var isStringBlock = false;
              var sb = new StringBuilder();
      
              long charIndex = 0;
              int currentLineCount = 0;
      
              while (reader.Peek() != -1)
              {
                  charIndex++;
      
                  char c = (char)reader.Read();
      
                  if (c == '"')
                      isStringBlock = !isStringBlock;
      
                  if (c == separator && !isStringBlock) //end of word
                  {
                      row.Add(sb.ToString().Trim()); //add word
                      sb.Length = 0;
                  }
                  else if (c == '\n' && !isStringBlock) //end of line
                  {
                      row.Add(sb.ToString().Trim()); //add last word in line
                      sb.Length = 0;
      
                      //DO SOMETHING WITH row HERE!
      
                      currentLineCount++;
      
                      row = new List<string>();
                  }
                  else
                  {
                      if (c != '"' && c != '\r') sb.Append(c == '\n' ? ' ' : c);
                  }
              }
      
              row.Add(sb.ToString().Trim()); //add last word
      
              //DO SOMETHING WITH LAST row HERE!
          }
      

      【讨论】:

        【解决方案5】:

        试试CsvHelper(我维护的一个库)。它忽略空行。我相信您可以在 FastCsvReader 中设置一个标志,让它也处理空行。

        【讨论】:

        • 这也是我发现的最快的。谢谢你,乔希。
        【解决方案6】:

        也许您可以在 ReadLine() 期间计算 (")。如果它们是奇数,则会引发标志。您可以忽略这些行,或者获取接下来的两行并消除第一个 "\n" 出现合并线。

        【讨论】:

        • string.IsNullOrEmpty(value.Trim()) 可能更安全。
        【解决方案7】:

        由于这个问题,我通常做的是逐个字符而不是逐行阅读文本。

        当您阅读每个字符时,您应该能够弄清楚每个单元格的开始和停止位置,以及行和单元格中的换行符之间的区别:如果我没记错的话,无论如何对于 Excel 生成的文件,行以\r\n开头,单元格中的换行符只有\r。

        【讨论】:

          【解决方案8】:

          听取专家的建议和Don't roll your own CSV parser

          您的第一个想法是,“如何处理换行符?”

          您的下一个想法是,“我需要处理引号内的逗号。”

          你的下一个想法是,“哦,废话,我需要处理引号内的引号。转义引号。双引号。单引号......”

          这是一条通往疯狂的道路。不要自己写。找到一个具有广泛单元测试覆盖率的库,该库涵盖了所有困难的部分,并为你经历了地狱。对于 .NET,请使用 free FileHelpers library

          【讨论】:

            【解决方案9】:

            有一个示例解析器是 c#,它似乎可以正确处理您的情况。然后,您可以读取数据并在读取后清除换行符。 Part 2 是解析器,有一个 Part 1 覆盖了编写器部分。

            【讨论】:

              【解决方案10】:

              阅读该行。
              拆分为列(字段)。
              如果每行预期有足够的列,则进行处理。
              如果没有,请阅读下一行,并捕获剩余的列,直到获得所需的内容。
              重复。

              【讨论】:

              • 如果引号之间有逗号,拆分可能很危险。精心设计的正则表达式会更安全。
              【解决方案11】:

              可以在每一行上使用一个稍微简单的正则表达式。当它匹配时,您处理匹配中的每个字段。如果找不到匹配项,则跳过该行。

              正则表达式可能看起来像这样。

              Match match = Regex.Match(line, @"^(?:,?(?<q>['"](?<field>.*?\k'q')|(?<field>[^,]*))+$");
              if (match.Success)
              {
                foreach (var capture in match.Groups["field"].Captures)
                {
                  string fieldValue = capture.Value;
                  // Use the value.
                }
              }
              

              【讨论】:

                【解决方案12】:

                看看FileHelpers Library 它支持读\写带有换行符的CSV以及读\写到excel

                【讨论】:

                  【解决方案13】:

                  LINQy 解决方案:

                  string csvText = File.ReadAllText("C:\\Test.txt");
                  
                  var query = csvText
                      .Replace(Environment.NewLine, string.Empty)
                      .Replace("\"\"", "\",\"").Split(',')
                      .Select((i, n) => new { i, n }).GroupBy(a => a.n / 3);
                  

                  【讨论】:

                    【解决方案14】:

                    您还可以在 NuGet 上查看我的 CSV 解析器 SoftCircuits.CsvParser。它不仅会解析 CSV 文件,而且——如果需要的话——还可以自动将列值映射到您的类属性。它的运行速度比 CsvHelper 快近四倍。

                    【讨论】:

                      【解决方案15】:

                      要在 CSV 中存在换行符,必须有一个未闭合的打开双引号。 假设所有 CSV 单元格必须打开和关闭双引号,只需检查是否有奇数个引号

                      my_string.Count(c => c == '"') % 2 == 1
                      

                      如果是这样,请继续阅读,直到找到偶数。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-10-09
                        • 2013-01-22
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多