处理CSV文件中的逗号

我正在寻找关于如何处理正在创建,然后由我们的客户上传的csv文件的建议,并且该文件可能具有逗号(例如公司名称)。

我们所看到的一些想法是:引用标识符(值“,”值“,”等)或使用| 而不是逗号。 最大的问题是我们必须简化,否则客户不会这样做。


正如其他人所说的,您需要避开包含引号的值。 这里有一个支持引用值的C♯CSV阅读器,包括嵌入式引号和回车符。

顺便说一下,这是经过单元测试的代码。 我现在发布它,因为这个问题似乎出现了很多,而其他人在简单的CSV支持时可能不需要整个图书馆。

你可以使用它如下:

using System;
public class test
{
    public static void Main()
    {
        using ( CsvReader reader = new CsvReader( "data.csv" ) )
        {
            foreach( string[] values in reader.RowEnumerator )
            {
                Console.WriteLine( "Row {0} has {1} values.", reader.RowIndex, values.Length );
            }
        }
        Console.ReadLine();
    }
}

这里是类。 请注意,您也可以使用Csv.Escape函数编写有效的CSV。

using System.IO;
using System.Text.RegularExpressions;

public sealed class CsvReader : System.IDisposable
{
    public CsvReader( string fileName ) : this( new FileStream( fileName, FileMode.Open, FileAccess.Read ) )
    {
    }

    public CsvReader( Stream stream )
    {
        __reader = new StreamReader( stream );
    }

    public System.Collections.IEnumerable RowEnumerator
    {
        get {
            if ( null == __reader )
                throw new System.ApplicationException( "I can't start reading without CSV input." );

            __rowno = 0;
            string sLine;
            string sNextLine;

            while ( null != ( sLine = __reader.ReadLine() ) )
            {
                while ( rexRunOnLine.IsMatch( sLine ) && null != ( sNextLine = __reader.ReadLine() ) )
                    sLine += "n" + sNextLine;

                __rowno++;
                string[] values = rexCsvSplitter.Split( sLine );

                for ( int i = 0; i < values.Length; i++ )
                    values[i] = Csv.Unescape( values[i] );

                yield return values;
            }

            __reader.Close();
        }
    }

    public long RowIndex { get { return __rowno; } }

    public void Dispose()
    {
        if ( null != __reader ) __reader.Dispose();
    }

    //============================================


    private long __rowno = 0;
    private TextReader __reader;
    private static Regex rexCsvSplitter = new Regex( @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))" );
    private static Regex rexRunOnLine = new Regex( @"^[^""]*(?:""[^""]*""[^""]*)*""[^""]*$" );
}

public static class Csv
{
    public static string Escape( string s )
    {
        if ( s.Contains( QUOTE ) )
            s = s.Replace( QUOTE, ESCAPED_QUOTE );

        if ( s.IndexOfAny( CHARACTERS_THAT_MUST_BE_QUOTED ) > -1 )
            s = QUOTE + s + QUOTE;

        return s;
    }

    public static string Unescape( string s )
    {
        if ( s.StartsWith( QUOTE ) && s.EndsWith( QUOTE ) )
        {
            s = s.Substring( 1, s.Length - 2 );

            if ( s.Contains( ESCAPED_QUOTE ) )
                s = s.Replace( ESCAPED_QUOTE, QUOTE );
        }

        return s;
    }


    private const string QUOTE = """;
    private const string ESCAPED_QUOTE = """";
    private static char[] CHARACTERS_THAT_MUST_BE_QUOTED = { ',', '"', 'n' };
}

对于2017年,csv完全指定 - RFC 4180。

这是一个非常常见的规范,并且被许多库(例子)完全覆盖。

只需使用任何容易获得的csv库 - 也就是说RFC 4180。


实际上有一个CSV格式的规范以及如何处理逗号:

包含换行符(CRLF),双引号和逗号的字段应用双引号引起来。

http://tools.ietf.org/html/rfc4180

所以,要bar,baz foobar,baz ,你这样做:

foo,"bar,baz"

另一个需要考虑的重要要求(也来自规范):

如果使用双引号将字段括起来,那么出现在字段内的双引号必须通过在另一个双引号之前进行转义来进行转义。 例如:

"aaa","b""bb","ccc"

CSV格式使用逗号分隔值,包含回车符,换行符,逗号或双引号的值用双引号括起来。 包含双引号的值被引用,并且每个文字引号都被前面的引号转义:例如,3个值:

test
list, of, items
"go" he said

将被编码为:

test
"list, of, items"
"""go"" he said"

任何字段都可以被引用,但只有包含逗号,CR / NL或引号的字段必须被引用。

CSV格式没有真正的标准,但几乎所有的应用程序都遵循这里记录的约定。 其他地方提到的RFC不是CSV的标准,它是在MIME中使用CSV的RFC,并且包含一些非常规和不必要的限制,使其在MIME之外无用。

我见过的很多CSV模块都无法容纳的一个问题是,多行可以在单个字段中编码,这意味着您不能假设每行都是单独的记录,您不需要在您的行中允许换行数据或准备处理这个。

链接地址: http://www.djcxy.com/p/46865.html

上一篇: Dealing with commas in a CSV file

下一篇: File input 'accept' attribute