正则expression式来分割CSV
我知道这个(或类似的)已被问了很多次,但已经尝试了许多可能性,我一直没能find一个正常工作100%的正则expression式。
我有一个CSV文件,我试图把它分成一个数组,但遇到两个问题:引号和空元素。
CSV看起来像:
123,2.99,AMO024,Title,"Description, more info",,123987564
我尝试使用的正则expression式是:
thisLine.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/)
唯一的问题是,在我的输出数组中第五个元素出来123987564而不是一个空string。
描述
而不是使用拆分,我认为简单地执行匹配并处理所有find的匹配会更容易。
这个expression会:
- 将您的示例文本划分为逗号分隔符
- 将处理空值
- 会忽略双引号,提供双引号不嵌套
- 修剪返回的值的分隔逗号
- 从返回的值中修剪引号
正则expression式: (?:^|,)(?=[^"]|(")?)"?((?(1)[^"]*|[^,"]*))"?(?=,|$)
例
示例文本
123,2.99,AMO024,Title,"Description, more info",,123987564
使用非javaexpression式的ASP示例
Set regEx = New RegExp regEx.Global = True regEx.IgnoreCase = True regEx.MultiLine = True sourcestring = "your source string" regEx.Pattern = "(?:^|,)(?=[^""]|("")?)""?((?(1)[^""]*|[^,""]*))""?(?=,|$)" Set Matches = regEx.Execute(sourcestring) For z = 0 to Matches.Count-1 results = results & "Matches(" & z & ") = " & chr(34) & Server.HTMLEncode(Matches(z)) & chr(34) & chr(13) For zz = 0 to Matches(z).SubMatches.Count-1 results = results & "Matches(" & z & ").SubMatches(" & zz & ") = " & chr(34) & Server.HTMLEncode(Matches(z).SubMatches(zz)) & chr(34) & chr(13) next results=Left(results,Len(results)-1) & chr(13) next Response.Write "<pre>" & results
使用非javaexpression式匹配
组0获取包含逗号的整个子string
如果使用了第1组,则会得到报价
组2得到的值不包括逗号
[0][0] = 123 [0][1] = [0][2] = 123 [1][0] = ,2.99 [1][1] = [1][2] = 2.99 [2][0] = ,AMO024 [2][1] = [2][2] = AMO024 [3][0] = ,Title [3][1] = [3][2] = Title [4][0] = ,"Description, more info" [4][1] = " [4][2] = Description, more info [5][0] = , [5][1] = [5][2] = [6][0] = ,123987564 [6][1] = [6][2] = 123987564
几个月前我创build了这个项目。
".+?"|[^"]+?(?=,)|(?<=,)[^"]+
它在C#中工作,当selectPython和PCRE时,Debuggex很高兴。 Javascript不能识别这种forms的Proceeded By ?<= …。
对于你的价值观,它会创造匹配
123 ,2.99 ,AMO024 ,Title "Description, more info" , ,123987564
请注意,引号中的任何内容都没有前导逗号,但是尝试与前导逗号匹配是空值用例所必需的。 完成后,根据需要修改值。
我使用RegexHero.Net来testing我的正则expression式。
我也需要这个答案,但是我find了答案,而信息丰富,有点难以遵循和复制其他语言。 这是我为CSV列中的一列提出的最简单的expression式。 我不是分裂。 我build立一个正则expression式来匹配一个CSV列,所以我不分裂线:
("([^"]*)"|[^,]*)(,|$)
这与CSV行中的单个列匹配。 expression式的第一部分"([^"]*)"
是匹配一个引用的条目,第二部分[^,]*
是匹配一个非引用的条目。 。
并附带debuggex来testingexpression式。
将JScript用于传统的ASP页面的优点是可以使用为JavaScript编写的许多库中的一个。
像这样的: https : //github.com/gkindel/CSV-JS 。 下载它,将其包含在你的ASP页面,parsingCSV。
<%@ language="javascript" %> <script language="javascript" runat="server" src="scripts/csv.js"></script> <script language="javascript" runat="server"> var text = '123,2.99,AMO024,Title,"Description, more info",,123987564', rows = CSV.parse(line); Response.Write(rows[0][4]); </script>
我个人尝试了许多RegExexpression式,却没有find与所有情况匹配的完美expression式。
我认为正则expression式很难正确configuration以正确匹配所有情况。 尽pipe很less有人不喜欢这个名字空间(我也是他们的一部分),但是我提出了一些属于.Net框架的东西,并且在所有情况下都给出了适当的结果(主要处理每个双引号的情况):
Microsoft.VisualBasic.FileIO.TextFieldParser
在这里find它: StackOverflow
使用示例:
TextReader textReader = new StringReader(simBaseCaseScenario.GetSimStudy().Study.FilesToDeleteWhenComplete); Microsoft.VisualBasic.FileIO.TextFieldParser textFieldParser = new TextFieldParser(textReader); textFieldParser.SetDelimiters(new string[] { ";" }); string[] fields = textFieldParser.ReadFields(); foreach (string path in fields) { ...
希望它可以帮助。
在Java中这个模式",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))"
String text = "\",\",\",,\",,\",asdasd a,sd s,ds ds,dasda,sds,ds,\""; String regex = ",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))"; Pattern p = Pattern.compile(regex); String[] split = p.split(text); for(String s:split) { System.out.println(s); }
输出:
"," ",a,," ",asdasd a,sd s,ds ds,dasda,sds,ds,"
缺点:不工作,当列有奇数的报价:(
还有一些额外的function,比如支持包含转义引号和CR / LF字符(单个值跨越多行)的引用值。
注意:尽pipe下面的解决scheme可能适用于其他正则expression式引擎,但是按原样使用它将需要您的正则expression式引擎将多个命名的捕获组与同一个名称作为一个捕获组对待。 (.NET默认是这样做的)
当CSV文件/stream(匹配RFC标准4180 )的多行/logging传递给下面的正则expression式时,它将返回每个非空行/logging的匹配。 每个匹配将包含一个名为Value
的捕获组,该捕获组包含该行/logging中的捕获值(如果在行/logging的OpenValue
有开放引用,则可能包含OpenValue
捕获组) 。
这是注释模式( 在Regexstorm.net上testing ):
(?<=\r|\n|^)(?!\r|\n|$) // Records start at the beginning of line (line must not be empty) (?: // Group for each value and a following comma or end of line (EOL) - required for quantifier (+?) (?: // Group for matching one of the value formats before a comma or EOL "(?<Value>(?:[^"]|"")*)"| // Quoted value -or- (?<Value>(?!")[^,\r\n]+)| // Unquoted value -or- "(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)| // Open ended quoted value -or- (?<Value>) // Empty value before comma (before EOL is excluded by "+?" quantifier later) ) (?:,|(?=\r|\n|$)) // The value format matched must be followed by a comma or EOL )+? // Quantifier to match one or more values (non-greedy/as few as possible to prevent infinite empty values) (?:(?<=,)(?<Value>))? // If the group of values above ended in a comma then add an empty value to the group of matched values (?:\r\n|\r|\n|$) // Records end at EOL
这里是没有所有评论或空白的原始模式。
(?<=\r|\n|^)(?!\r|\n|$)(?:(?:"(?<Value>(?:[^"]|"")*)"|(?<Value>(?!")[^,\r\n]+)|"(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)|(?<Value>))(?:,|(?=\r|\n|$)))+?(?:(?<=,)(?<Value>))?(?:\r\n|\r|\n|$)
以下是来自Debuggex.com的一个可视化文件 (为了清楚起见而命名的捕获组):
有关如何使用正则expression式模式的示例可以在我的答案中find类似的问题,也可以在这里或在这里的 C#平台上 find 。
我迟到了,但以下是我使用的正则expression式:
(?:,"|^")(""|[\w\W]*?)(?=",|"$)|(?:,(?!")|^(?!"))([^,]*?)(?=$|,)|(\r\n|\n)
这种模式有三个捕获组:
- 引用的单元格的内容
- 没有引号的单元格的内容
- 一条新的线
该模式处理以下所有内容:
- 正常的单元格内容没有任何特殊的function: 一,二,三
- 包含双引号的单元格(“转义为”): 不引用,“a”,“引用”,“结束”
- 单元格包含一个换行符: 一个,两个\ nthree,四个
- 正常的单元格内容有一个内部引用: 一个,两个“三个,四个
- 单元格包含引号,后跟逗号: 1,“2”,“3”,“4”,5
看到这个模式在使用。
如果你正在使用正则expression式更有效的风格与命名组和lookbehinds,我更喜欢以下内容:
(?<quoted>(?<=,"|^")(?:""|[\w\W]*?)*(?=",|"$))|(?<normal>(?<=,(?!")|^(?!"))[^,]*?(?=(?<!")$|(?<!"),))|(?<eol>\r\n|\n)
看到这个模式在使用。
我正在使用这个,它与昏迷分离器和双引号转义。 通常这应该解决你的问题:
/(?<=^|,)(\"(?:[^"]+|"")*\"|[^,]*)(?:$|,)/g
我有类似的需要从SQL插入语句拆分CSV值。
在我的情况下,我可以假定string被包裹在单引号中,而数字不是。
csv.split(/,((?=')|(?=\d))/g).filter(function(x) { return x !== '';});
一些可能是显而易见的原因,这个正则expression式产生一些空白的结果。 我可以忽略这些,因为我的数据中的任何空值都表示为...,'',...
而不是...,,...
如果我使用'g'标志尝试@chubbsondubs在http://regex101.com上发布的正则expression式,那么匹配项只包含','或一个空string。; 有了这个正则expression式:
(?:"([^"]*)"|([^,]*))(?:[,])
我可以匹配CSV的部分(包括引用的部分)。 (该行必须以','结尾,否则最后一部分不被识别。)
https://regex101.com/r/dF9kQ8/4
如果CSV看起来像:
"",huhu,"hel lo",world,
有4场比赛:
“”
“忽忽”
'你好'
'世界'
如果你知道你不会有一个空的字段(,,),那么这个expression效果很好:
("[^"]*"|[^,]+)
如下例所示…
Set rx = new RegExp rx.Pattern = "(""[^""]*""|[^,]+)" rx.Global = True Set col = rx.Execute(sText) For n = 0 to col.Count - 1 if n > 0 Then s = s & vbCrLf s = s & col(n) Next
但是,如果预计空字段和文本相对较小,则可能需要考虑在parsing之前先用空格replace空字段,以确保它们被捕获。 例如…
... Set col = rx.Execute(Replace(sText, ",,", ", ,")) ...
如果您需要保持字段的完整性,则可以恢复逗号并testing循环中的空白空间。 这可能不是最有效的方法,但它完成了工作。
,?\s*'.+?'|,?\s*".+?"|[^"']+?(?=,)|[^"']+
这个正则expression式适用于单引号和双引号,也可以在另一个引号内引用。
这一个匹配所有我需要在C#中:
(?<=(^|,)(?<quote>"?))([^"]|(""))*?(?=\<quote>(?=,|$))
- 去掉报价
- 让新的线
- 让引号内的string加双引号
- 让引号中的逗号string
使用正确的正则expression式将单引号值与单引号中的转义[doubled]单引号匹配:
'([^n']|(''))+'