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

我们正在考虑的一些想法是:带引号的标识符(value "," values ","等等)或使用|代替逗号。最大的问题是我们必须让它变得简单,否则客户就不会这么做。


当前回答

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模块不适应的一个问题是,可以在单个字段中编码多行,这意味着您不能假设每一行都是一个单独的记录,您要么需要不允许数据中出现换行,要么准备好处理这个问题。

其他回答

如果你想重新发明轮子,下面的方法可能对你有用:

public static IEnumerable<string> SplitCSV(string line)
{
    var s = new StringBuilder();
    bool escaped = false, inQuotes = false;
    foreach (char c in line)
    {
        if (c == ',' && !inQuotes)
        {
            yield return s.ToString();
            s.Clear();
        }
        else if (c == '\\' && !escaped)
        {
            escaped = true;
        }
        else if (c == '"' && !escaped)
        {
            inQuotes = !inQuotes;
        }
        else
        {
            escaped = false;
            s.Append(c);
        }
    }
    yield return s.ToString();
}

正如其他人所说,您需要转义包含引号的值。这是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' };
}

我使用Csvreader库,但通过使用它,我从列值中的逗号(,)爆炸获得数据。

所以如果你想要插入CSV文件数据,其中包含逗号(,)的大部分列值,你可以使用下面的函数。 作者链接=> https://gist.github.com/jaywilliams/385876

function csv_to_array($filename='', $delimiter=',')
{
    if(!file_exists($filename) || !is_readable($filename))
        return FALSE;

    $header = NULL;
    $data = array();
    if (($handle = fopen($filename, 'r')) !== FALSE)
    {
        while (($row = fgetcsv($handle, 1000, $delimiter)) !== FALSE)
        {
            if(!$header)
                $header = $row;
            else
                $data[] = array_combine($header, $row);
        }
        fclose($handle);
    }
    return $data;
}

在字符串周围加双引号。这就是Excel所做的。

阿拉伊莱,

将双引号转义为2 双引号。如。 “test1”、“foo”“酒吧”,“test2”

首先,让我们问问自己,“为什么我们觉得需要在CSV文件中以不同的方式处理逗号?”

对我来说,答案是:“因为当我将数据导出到CSV文件时,字段中的逗号消失了,我的字段被分隔成多个字段,其中逗号出现在原始数据中。”(这是因为逗号是CSV字段分隔符。)

根据您的情况,分号也可以用作CSV字段分隔符。

根据我的要求,我可以使用一个字符,例如,单个低9引号,它看起来像一个逗号。

所以,下面是你在Go中如何做到这一点:

// Replace special CSV characters with single low-9 quotation mark
func Scrub(a interface{}) string {
    s := fmt.Sprint(a)
    s = strings.Replace(s, ",", "‚", -1)
    s = strings.Replace(s, ";", "‚", -1)
    return s
}

Replace函数中的第二个逗号字符是十进制8218。

请注意,如果您的客户端可能只有ascii文本阅读器,那么这个十进制8218字符看起来不会像逗号。如果这是您的情况,那么根据RFC 4128,我建议用逗号(或分号)和双引号包围字段:https://www.rfc-editor.org/rfc/rfc4180