在CSV文件中处理逗号

时间:2009-04-20 18:52:33

标签: csv

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

我们正在关注的一些想法是:引用标识符(值“,”值“,”等)或使用|而不是逗号。最大的问题是我们必须让它变得简单,否则客户就不会这样做。

27 个答案:

答案 0 :(得分:378)

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

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

只需使用任何易于使用的csv库 - 即RFC 4180。


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

  

包含换行符(CRLF),双引号和逗号的字段应括在双引号中。

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

因此,要获得值foobar,baz,请执行以下操作:

foo,"bar,baz"

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

  

如果使用双引号括起字段,则使用双引号   出现在一个字段内必须通过前面的方式进行转义   另一个双引号。例如:

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

答案 1 :(得分:213)

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

答案 2 :(得分:73)

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

test
list, of, items
"go" he said

将编码为:

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

可以引用任何字段,但只能引用包含逗号,CR / NL或引号的字段

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

我所看到的许多CSV模块不能容纳的事实是,可以在单个字段中编码多行,这意味着您不能假设每行都是单独的记录,您需要不允许您的数据中的换行符或准备好处理此问题。

答案 3 :(得分:39)

在字符串周围加上双引号。通常是what Excel does

Ala Eli,

  你躲过两个双引号   双引号。例如。   “测试1”, “foo” 的 “条”, “test2的”

答案 4 :(得分:9)

您可以在字段周围加上双引号。我不喜欢这种方法,因为它增加了另一个特殊字符(双引号)。只需定义一个转义字符(通常是反斜杠),并在需要转义的地方使用它:

data,more data,more data\, even,yet more

您不必尝试匹配引号,并且您解析的异常更少。这也简化了您的代码。

答案 5 :(得分:7)

有一个库可以通过nuget处理几乎任何格式良好的CSV(.net) - CsvHelper

映射到班级的示例:

var csv = new CsvReader( textReader );
var records = csv.GetRecords<MyClass>();

阅读单个字段的示例:

var csv = new CsvReader( textReader );
while( csv.Read() )
{
    var intField = csv.GetField<int>( 0 );
    var stringField = csv.GetField<string>( 1 );
    var boolField = csv.GetField<bool>( "HeaderName" );
}

让客户端驱动文件格式:
,是标准字段分隔符,"是用于转义包含分隔符,引号或行结尾的字段的标准值。

使用(例如)#表示字段,'表示转义:

var csv = new CsvReader( textReader );
csv.Configuration.Delimiter = "#";
csv.Configuration.Quote = ''';
// read the file however meets your needs

More Documentation

答案 6 :(得分:4)

添加对Microsoft.VisualBasic的引用(是的,它说VisualBasic但它也适用于C# - 请记住,最后它只是IL)。

使用Microsoft.VisualBasic.FileIO.TextFieldParser类解析CSV文件以下是示例代码:

 Dim parser As TextFieldParser = New TextFieldParser("C:\mar0112.csv")
 parser.TextFieldType = FieldType.Delimited
 parser.SetDelimiters(",")      

   While Not parser.EndOfData         
      'Processing row             
      Dim fields() As String = parser.ReadFields         
      For Each field As String In fields             
         'TODO: Process field                   

      Next      
      parser.Close()
   End While 

答案 7 :(得分:4)

您可以使用“;”等替代“分隔符”或“|”但最简单的可能只是引用大多数(体面的)CSV库和大多数不错的电子表格所支持的。

more on CSV delimiters and a spec for a standard format for describing delimiters and quoting see this webpage

答案 8 :(得分:4)

正如我对harpo的回答所述,他的解决方案很好并适用于大多数情况,但在某些情况下,当逗号彼此直接相邻时,它无法在逗号上分割。

这是因为正则表达式字符串意外地表现为vertabim字符串。 为了使这种行为正确,需要手动转义正则表达式字符串中的所有“字符而不使用vertabim转义。”

IE中。正则表达式应该使用手动转义:

",(?=(?:[^\"\"]*\"\"[^\"\"]*\"\")*(?![^\"\"]*\"\"))"

转换为",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"

使用vertabim字符串@",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"时,如果您调试正则表达式,它的行为如下所示:

",(?=(?:[^"]*"[^"]*")*(?![^"]*"))"

总而言之,我推荐harpo的解决方案,但请注意这个小问题!

我已经在CsvReader中包含了一些可选的故障保护,以便在发生此错误时通知您(如果您有预先知道的列数):

if (_expectedDataLength > 0 && values.Length != _expectedDataLength) 
throw new DataLengthException(string.Format("Expected {0} columns when splitting csv, got {1}", _expectedDataLength, values.Length));

这可以通过构造函数注入:

public CsvReader(string fileName, int expectedDataLength = 0) : this(new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
    _expectedDataLength = expectedDataLength;
}

答案 9 :(得分:4)

如果您使用的是 * nix-system ,则可以访问 sed ,并且只能有一个或多个不需要的逗号在您的CSV的特定字段 中,您可以使用以下单行内容将"括起来RFC4180 Section 2建议:

sed -r 's/([^,]*,[^,]*,[^,]*,)(.*)(,.*,.*)/\1"\2"\3/' inputfile

根据不需要的逗号所在的字段,您必须更改/扩展正则表达式的捕获组(以及替换)。
上面的示例将用引号将第四个字段(六个中)括起来。

enter image description here

结合--in-place-option,您可以将这些更改直接应用于文件。

为了“建立”正确的正则表达式,有一个简单的原则可以遵循:

  1. 对于之前 字段中包含不需要的逗号的每个字段,您只需编写一个[^,]*,并将它们放在一个捕获组中。
  2. 对于包含您编写的不需要的逗号的字段(.*)
  3. 对于每个字段带有不需要的逗号的字段之后,您编写一个,.*并将它们全部放在一个捕获组中。
  4. 以下是根据具体字段对不同可能的正则表达式/替换进行简要概述。如果没有给出,则替换为\1"\2"\3

    ([^,]*)(,.*)                     #first field, regex
    "\1"\2                           #first field, substitution
    
    (.*,)([^,]*)                     #last field, regex
    \1"\2"                           #last field, substitution
    
    
    ([^,]*,)(.*)(,.*,.*,.*)          #second field (out of five fields)
    ([^,]*,[^,]*,)(.*)(,.*)          #third field (out of four fields)
    ([^,]*,[^,]*,[^,]*,)(.*)(,.*,.*) #fourth field (out of six fields)
    

    如果您想使用sed删除不需要的逗号,而不是用引号将其括起来,请参阅this answer

答案 10 :(得分:3)

如果您想重新发明轮子,以下内容可能对您有用:

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();
}

答案 11 :(得分:3)

在欧洲,我们有这个问题必须早于这个问题。在欧洲,我们使用逗号作为小数点。请参阅以下数字:

| American      | Europe        |
| ------------- | ------------- |
| 0.5           | 0,5           |
| 3.14159265359 | 3,14159265359 |
| 17.54         | 17,54         |
| 175,186.15    | 175.186,15    |

因此无法对CSV文件使用逗号分隔符。由于这个原因,欧洲的CSV文件用分号;分隔。

Microsoft Excel等程序可以使用分号读取文件,并且可以从分隔符切换。您甚至可以使用选项卡(\t)作为分隔符。请参阅this answer from Supper User

答案 12 :(得分:2)

如果您对如何解析文件的更多教育活动感兴趣(使用CSV作为示例),您可以查看Julian Bucknall的this article。我喜欢这篇文章,因为它把事情分解成更小的难以克服的问题。你首先创建一个语法,一旦你有一个好的语法,将语法转换成代码是一个相对简单和有条理的过程。

本文使用C#并在底部有一个链接来下载代码。

答案 13 :(得分:1)

只需在NuGet上使用SoftCircuits.CsvParser。它将为您处理所有这些详细信息,并有效处理非常大的文件。而且,如果需要,它甚至可以通过将列映射到对象属性来导入/导出对象。此外,我的测试表明,平均速度比流行的CsvHelper快4倍。

答案 14 :(得分:0)

我认为解决此问题的最简单方法是让客户在excel中打开csv,然后按ctrl + r将所有逗号替换为您想要的任何标识符。这对于客户来说非常简单,只需要对代码进行一次更改即可阅读您选择的分隔符。

答案 15 :(得分:0)

首先,让我们问自己,“为什么我们觉得需要以不同的方式处理逗号的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
}

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

请注意,如果您的客户端可能只有ascii文本阅读器,则此decima 8218字符将不会像逗号一样。如果是这种情况,那么我建议使用逗号(或分号)围绕字段,每个RFC 4128使用双引号:https://tools.ietf.org/html/rfc4180

答案 16 :(得分:0)

我通常对可以包含任何逗号或任何特殊字符的字段进行URL编码。然后在任何视觉媒体中使用/显示时对其进行解码。

(逗号变为%2C)

每种语言都应该有对URL编码和解码字符串的方法。

例如,在java

URLEncoder.encode(myString,"UTF-8"); //to encode
URLDecoder.decode(myEncodedstring, "UTF-8"); //to decode

我知道这是一个非常通用的解决方案,对于用户想要手动查看csv文件内容的情况,这可能不太理想。

答案 17 :(得分:0)

我通常在我的CSV文件解析例程中执行此操作。假设&#39; line&#39;变量是CSV文件中的一行,并且所有列都是&#39;值用双引号括起来。执行以下两行后,您将在&#39;值中获得CSV列。集合。

self.tableView = DraggableTableView()

答案 18 :(得分:0)

您可以像这样阅读csv文件。

这会使用分裂并处理空格。

ArrayList List = new ArrayList();
static ServerSocket Server;
static Socket socket;
static ArrayList<Object> list = new ArrayList<Object>();


public static void ReadFromXcel() throws FileNotFoundException
{   
    File f = new File("Book.csv");
    Scanner in = new Scanner(f);
    int count  =0;
    String[] date;
    String[] name;
    String[] Temp = new String[10];
    String[] Temp2 = new String[10];
    String[] numbers;
    ArrayList<String[]> List = new ArrayList<String[]>();
    HashMap m = new HashMap();

         in.nextLine();
         date = in.nextLine().split(",");
         name = in.nextLine().split(",");
         numbers = in.nextLine().split(",");
         while(in.hasNext())
         {
             String[] one = in.nextLine().split(",");
             List.add(one);
         }
         int xount = 0;
         //Making sure the lines don't start with a blank
         for(int y = 0; y<= date.length-1; y++)
         {
             if(!date[y].equals(""))
             {   
                 Temp[xount] = date[y];
                 Temp2[xount] = name[y];
                 xount++;
             }
         }

         date = Temp;
         name =Temp2;
         int counter = 0;
         while(counter < List.size())
         {
             String[] list = List.get(counter);
             String sNo = list[0];
             String Surname = list[1];
             String Name = list[2];
             for(int x = 3; x < list.length; x++)
             {           
                 m.put(numbers[x], list[x]);
             }
            Object newOne = new newOne(sNo, Name, Surname, m, false);
             StudentList.add(s);
             System.out.println(s.sNo);
             counter++;
         }

答案 19 :(得分:0)

由于这是关于一般做法,让我们从拇指规则开始:

  1. 不要使用CSV,将XML与库一起使用来阅读&amp;改为编写xml文件。

  2. 如果您必须使用CSV。正确执行并使用免费库来解析和存储CSV文件。

  3. 为证明1),大多数CSV解析器不能识别编码,所以如果你不处理US-ASCII,你就会遇到麻烦。 例如,excel 2002将CSV存储在本地编码中,而没有任何关于编码的注释。 CSV标准没有被广泛采用:(。 另一方面,xml标准很好用,它可以很好地处理编码。

    为了证明2),几乎所有语言都有大量的csv解析器,所以即使解决方案看起来很简单,也不需要重新发明轮子。

    仅举几例:

    • for python use csv module

    • 用于perl检查CPAN和Text::CSV

    • for php使用fgetcsv / fputcsv函数构建

    • 用于java check SuperCVS

    如果您不打算在嵌入式设备上解析它,则无需手动实现。

答案 20 :(得分:0)

我发现的最简单的解决方案是LibreOffice使用的解决方案:

  1. 将所有文字"替换为
  2. 在字符串周围添加双引号
  3. 您也可以使用Excel使用的那个:

    1. 将所有文字"替换为""
    2. 在字符串周围添加双引号
    3. 请注意,其他人建议仅执行上面的第2步,但这不适用于"后跟,的行,就像您希望拥有的CSV一样一个包含字符串hello",world的列,如CSV所示:

      "hello",world"
      

      这被解释为包含两列的行:helloworld"

答案 21 :(得分:0)

    public static IEnumerable<string> LineSplitter(this string line, char 
         separator, char skip = '"')
    {
        var fieldStart = 0;
        for (var i = 0; i < line.Length; i++)
        {
            if (line[i] == separator)
            {
                yield return line.Substring(fieldStart, i - fieldStart);
                fieldStart = i + 1;
            }
            else if (i == line.Length - 1)
            {
                yield return line.Substring(fieldStart, i - fieldStart + 1);
                fieldStart = i + 1;
            }

            if (line[i] == '"')
                for (i++; i < line.Length && line[i] != skip; i++) { }
        }

        if (line[line.Length - 1] == separator)
        {
            yield return string.Empty;
        }
    }

答案 22 :(得分:0)

我使用了Csvreader库,但是通过使用Csvreader库,我从列值中的comma(,)爆炸得到了数据。

因此,如果要在大多数列值中插入包含逗号(,)的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;
}

答案 23 :(得分:0)

我使用papaParse库解析了CSV文件并具有键值对(键/标题/ CSV文件值的第一行)。

这是我使用的示例:

https://codesandbox.io/embed/llqmrp96pm

其中具有dummy.csv文件以具有CSV解析演示。

我已经在reactJS中使用了它,尽管可以很容易地在用任何语言编写的应用程序中复制它。

答案 24 :(得分:0)

这是一个简洁的解决方法:

您可以改用希腊语下数字符号(U + 0375)

像这样͵

使用此方法也可以节省大量资源...

答案 25 :(得分:0)

一个示例可能有助于显示如何在.csv文件中显示逗号。创建一个简单的文本文件,如下所示:

将此文本文件另存为带有后缀“ .csv”的文本文件,并从Windows 10使用Excel 2000将其打开。

aa,bb,cc,d; d “在电子表格演示文稿中,除了以下内容显示了逗号,而不是d之间的分号外,下一行应类似于上一行。” aa,bb,cc,“ d,d”,即使在Excel中也可以使用

aa,bb,cc,“ d,d”,即使在Excel 2000中也可以使用 aa,bb,cc,“ d,d”,即使在Excel 2000中也可以使用 aa,bb,cc,“ d,d”,即使在Excel 2000中也可以使用

aa,bb,cc,“ d,d”,由于空格分隔第一引号,因此在Excel 2000中失败 aa,bb,cc,“ d,d”,由于空格belore第一引号,因此在Excel 2000中失败 aa,bb,cc,“ d,d”,由于空格将第一个引号引起来,因此在Excel 2000中失败

aa,bb,cc,“ d,d”,即使在Excel 2000中也可以使用,即使在第二个引号之前和之后都有空格。 aa,bb,cc,“ d,d”,即使在Excel 2000中也可以使用,即使在第二个引号之前和之后都有空格。 aa,bb,cc,“ d,d”,即使在Excel 2000中也可以使用,即使在第二个引号前后都有空格。

规则:如果要在.csv文件的单元格(字段)中显示逗号: “使用双引号将字段开头和结尾,但请避免在第一引号之前留空格”

答案 26 :(得分:-2)

使用制表符(\ t)分隔字段。