我正在寻找关于如何处理正在创建的csv文件的建议,然后由我们的客户上传,并且可能在值中有逗号,如公司名称。
我们正在考虑的一些想法是:带引号的标识符(value "," values ","等等)或使用|代替逗号。最大的问题是我们必须让它变得简单,否则客户就不会这么做。
我正在寻找关于如何处理正在创建的csv文件的建议,然后由我们的客户上传,并且可能在值中有逗号,如公司名称。
我们正在考虑的一些想法是:带引号的标识符(value "," values ","等等)或使用|代替逗号。最大的问题是我们必须让它变得简单,否则客户就不会这么做。
当前回答
正如其他人所说,您需要转义包含引号的值。这是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' };
}
其他回答
如果您在*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删除不需要的逗号,而不是用引号将它们括起来,请参考此答案。
您可以使用其他“分隔符”,如“;”或“|”,但最简单的可能只是引用,这是大多数(体面的)CSV库和大多数体面的电子表格所支持的。
有关CSV分隔符的更多信息以及描述分隔符和引用的标准格式的规范,请参阅此网页
在字符串周围加双引号。这就是Excel所做的。
阿拉伊莱,
将双引号转义为2 双引号。如。 “test1”、“foo”“酒吧”,“test2”
有一个可以通过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,而是使用带有库的XML来读写XML文件。 如果必须使用CSV。正确操作并使用免费库来解析和存储CSV文件。
为了证明1),大多数CSV解析器没有编码意识,所以如果你不处理US-ASCII,你是在自找麻烦。 例如,excel2002以本地编码存储CSV,而没有任何关于编码的说明。CSV标准没有被广泛采用:(。 另一方面,xml标准被很好地采用,它处理编码非常好。
为了证明2),几乎所有语言都有大量的csv解析器,所以即使解决方案看起来非常简单,也没有必要重新发明轮子。
举几个例子:
对于python使用构建CSV模块 为perl检查CPAN和文本::CSV 对于PHP,使用内置的fgetcsv/fputcsv函数 java检查SuperCVS库
实际上,如果你不打算在嵌入式设备上解析它,就没有必要手动实现它。