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

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


当前回答

我通常url编码的字段可以有任何逗号或任何特殊字符。然后解码,当它被使用/显示在任何视觉媒体。

(逗号变为%2C)

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

例如,在Java中

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

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

其他回答

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

阿拉伊莱,

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

正如我在对harpo的回答的评论中提到的,他的解决方案在大多数情况下都很好,但是在某些情况下,当逗号直接相邻时,它无法在逗号上分割。

这是因为Regex字符串意外地表现为vertabim字符串。 为了获得正确的行为,regex字符串中的所有“字符都需要手动转义,而不使用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;
}

如果您在*nix-系统上,可以访问sed,并且仅在您的CSV的特定字段中可以有一个或多个不需要的逗号,您可以使用以下一行程序,以便将它们包含在RFC4180 Section 2中:

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

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

结合使用——In -place选项,您可以将这些更改直接应用到文件。

为了“构建”正确的正则表达式,需要遵循一个简单的原则:

对于CSV中出现在不需要的逗号字段之前的每个字段,您可以编写一个[^,]*,并将它们放在一个捕获组中。 对于包含不需要的逗号的字段,请写入(.*)。 对于带不需要的逗号的字段后面的每个字段,都要写一个,。*并将它们放在一个捕获组中。

下面是根据特定字段的不同可能的正则表达式/替换的简短概述。如果没有给出,则替换为\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删除不需要的逗号,而不是用引号将它们括起来,请参考此答案。

有一个可以通过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

更多的文档

如果您对如何解析一般文件(以CSV为例)更有教育意义的练习感兴趣,您可以查看Julian Bucknall的这篇文章。我喜欢这篇文章,因为它把事情分解成更小的问题,这些问题不那么难以克服。首先创建一个语法,一旦您有了一个好的语法,将语法转换为代码是一个相对简单和有条理的过程。

本文使用c#,并在底部有一个下载代码的链接。