我正在寻找有关如何处理正在创建的csv文件的建议,然后由我们的客户上传,并且可能在值中使用逗号,例如公司名称。
我们正在关注的一些想法是:引用标识符(值“,”值“,”等)或使用|而不是逗号。最大的问题是我们必须让它变得简单,否则客户就不会这样做。
答案 0 :(得分:378)
2017年,csv已完全指定 - RFC 4180。
这是一个非常常见的规范,并且被许多库(example)完全覆盖。
只需使用任何易于使用的csv库 - 即RFC 4180。
实际上有CSV格式规范以及如何处理逗号:
包含换行符(CRLF),双引号和逗号的字段应括在双引号中。
http://tools.ietf.org/html/rfc4180
因此,要获得值foo
和bar,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)
答案 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
答案 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库和大多数不错的电子表格所支持的。
答案 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
根据不需要的逗号所在的字段,您必须更改/扩展正则表达式的捕获组(以及替换)。
上面的示例将用引号将第四个字段(六个中)括起来。
结合--in-place
-option,您可以将这些更改直接应用于文件。
为了“建立”正确的正则表达式,有一个简单的原则可以遵循:
[^,]*,
并将它们放在一个捕获组中。(.*)
。,.*
并将它们全部放在一个捕获组中。以下是根据具体字段对不同可能的正则表达式/替换进行简要概述。如果没有给出,则替换为\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)
由于这是关于一般做法,让我们从拇指规则开始:
不要使用CSV,将XML与库一起使用来阅读&amp;改为编写xml文件。
如果您必须使用CSV。正确执行并使用免费库来解析和存储CSV文件。
为证明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使用的解决方案:
"
替换为”
您也可以使用Excel使用的那个:
"
替换为""
请注意,其他人建议仅执行上面的第2步,但这不适用于"
后跟,
的行,就像您希望拥有的CSV一样一个包含字符串hello",world
的列,如CSV所示:
"hello",world"
这被解释为包含两列的行:hello
和world"
答案 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)分隔字段。