CSV解析
我正在尝试使用C#来解析CSV。 我使用正则表达式来查找","
并且如果我的标题计数等于我的匹配计数则读取字符串。
如果我有一个像这样的值,这将不起作用:
"a",""b","x","y"","c"
然后我的输出是:
'a' '"b' 'x' 'y"' 'c'
但我想要的是:
'a' '"b","x","y"' 'c'
我可以使用任何正则表达式或任何其他逻辑吗?
CSV,在处理多行,引用,不同分隔符*等事情时,可能会比你想象的更棘手……或许考虑一个预先回答的答案? 我用它,效果很好。
* =记住某些区域设置使用[tab]作为CSV中的C …
CSV是代码重用的一个很好的例子 – 无论你选择哪一个csv解析器,都不要选择自己的。 停止滚动您自己的CSV解析器
如果我是你,我会使用FileHelpers 。 正则表达式很好但很难阅读,特别是如果你过一段时间后回去快速修复。
只是为了锻炼我的思维,快速和肮脏的工作 C#程序:
public static List SplitCSV(string line) { if (string.IsNullOrEmpty(line)) throw new ArgumentException(); List result = new List (); bool inQuote = false; StringBuilder val = new StringBuilder(); // parse line foreach (var t in line.Split(',')) { int count = t.Count(c => c == '"'); if (count > 2 && !inQuote) { inQuote = true; val.Append(t); val.Append(','); continue; } if (count > 2 && inQuote) { inQuote = false; val.Append(t); result.Add(val.ToString()); continue; } if (count == 2 && !inQuote) { result.Add(t); continue; } if (count == 2 && inQuote) { val.Append(t); val.Append(','); continue; } } // remove quotation for (int i = 0; i < result.Count; i++) { string t = result[i]; result[i] = t.Substring(1, t.Length - 2); } return result; }
为了获得可解析的CSV文件,值中的任何双引号都需要以某种方式正确转义。 执行此操作的两种标准方法是将双引号表示为两个双引号,或反斜杠双引号。 这是以下两种forms之一:
“”
\”
在第二种forms中,您的初始字符串将如下所示:
“一”, “\” B \ “\ ”×\“,\ ”Y \“”, “c” 的
如果您的输入字符串没有按照这样严格的格式进行格式化,那么您在自动化环境中成功解析它的可能性很小。
如果保证所有值都在引号中,请查找值,而不是逗号:
("".*?""|"[^"]*")
这利用了“最早的最长匹配胜利”这一事实 – 它首先查找双引号值,并且正常引用值的优先级较低。
如果您不希望封闭引号成为匹配的一部分,请使用:
"(".*?"|[^"]*)"
并转到匹配组1中的值。
正如我所说的:这项工作的先决条件是格式良好的输入,每个值周围都有保证引号或双引号。 必须引用空值! 一个很好的副作用是它不关心分隔符char。 你可以用逗号,TAB,分号,空格命名。 一切都会奏效。
有一句经常引用说:
有些人在遇到问题时会想“我知道,我会使用正则表达式”。 现在他们有两个问题。 (Jamie Zawinski)
鉴于CSV文件没有官方标准(而是存在大量略微不兼容的样式),您需要确保实现的内容适合您将接收的文件。 实现比你需要的更好的东西没有意义 – 我很确定你不需要正则表达式。
这是我用一种简单的方法来提取术语 – 基本上,它循环遍历查找逗号的行,跟踪当前索引是否在字符串中:
public IEnumerable SplitCSV(string line) { int index = 0; int start = 0; bool inString = false; foreach (char c in line) { switch (c) { case '"': inString = !inString; break; case ',': if (!inString) { yield return line.Substring(start, index - start); start = index + 1; } break; } index++; } if (start < index) yield return line.Substring(start, index - start); }
标准警告 - 未经测试的代码,可能存在逐个错误。
限制
-
不会自动删除值周围的引号。
要执行此操作,请在结束时的yield return
语句之前添加一个检查。 -
不支持单引号与双引号相同
您可以在inSingleQuotedString
添加单独的布尔值,将现有布尔值重命名为inDoubleQuotedString
并以相同的方式处理它们。 (你不能让现有的boolean做双重工作,因为你需要字符串以启动它的相同引号结束。) -
空格不会自动删除
有些工具会在CSV文件的逗号周围引入空格,以“漂亮”文件; 然后很难从格式化空格中分辨出有意的空格。
FileHelpers支持多行字段。
您可以解析这些文件:
a,"line 1 line 2 line 3" b,"line 1 line 2 line 3"
这是数据类型声明:
[DelimitedRecord(",")] public class MyRecord { public string field1; [FieldQuoted('"', QuoteMode.OptionalForRead, MultilineMode.AllowForRead)] public string field2; }
这是用法:
static void Main() { FileHelperEngine engine = new FileHelperEngine(typeof(MyRecord)); MyRecord[] res = engine.ReadFile("file.csv"); }
尝试CsvHelper (我维护的库)或FastCsvReader 。 两者都运作良好。 CsvHelper也写作。 像其他人一直在说的那样,不要自己动手。 :P
.Net的FileHelpers是你的朋友。
请点击“使用CSV进行正则表达式”链接:
Lumenworks CSV解析器(开源,免费但需要代码项目登录)是迄今为止我用过的最好的解析器。 这将节省您必须编写正则表达式并且直观易用。
好吧,我不是正则表达式,但我确信他们有这个答案。
程序上它正在逐字逐句地进行。 将变量(例如dontMatch)设置为FALSE。
每次你遇到报价切换dontMatch。
每次遇到逗号时,请检查dontMatch。 如果为TRUE,则忽略逗号。 如果为FALSE,则以逗号分隔。
这适用于您给出的示例,但您用于引号的逻辑基本上是错误的 – 您必须转义它们或使用其他分隔符(例如,单引号)来设置除小引号之外的主要引号。
例如,
"a", ""b", ""c", "d"", "e""
会产生不好的结果。
这可以用另一个补丁修复。 而不是简单地保持真假,你必须匹配报价。
要匹配引号,您必须知道上次看到的内容,它会进入相当深的解析区域。 在那时,您可能希望确保您的语言设计得很好,如果是,您可以使用编译器工具为您创建解析器。
-亚当
我只是在我的代码中尝试你的正则表达式。对于带有引号的格式化文本工作正常…
但是想知道我们是否可以通过Regex解析价值低于…
“First_Bat7679”,“”NAME“,”ENAME“,”FILE“”,“”,“”,“来自:”DDD,_Ala%as“@ sib.com”
我正在寻找结果:
'First_Bat7679' ' “名称”, “ENAME”, “程序文件”' “” “” '来自:“DDD,_Ala%为”@ sib.com“
感谢名单