提问人:Code Ninja 提问时间:8/9/2013 最后编辑:InterrobangCode Ninja 更新时间:9/5/2023 访问量:150969
用于拆分 CSV 的正则表达式
Regex to split a CSV
问:
我知道这个(或类似)已经被问过很多次了,但是在尝试了许多可能性之后,我还没有找到一个 100% 有效的正则表达式。
我有一个 CSV 文件,我正在尝试将其拆分为一个数组,但遇到了两个问题:带引号的逗号和空元素。
CSV 如下所示:
123,2.99,AMO024,Title,"Description, more info",,123987564
我尝试使用的正则表达式是:
thisLine.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/)
唯一的问题是,在我的输出数组中,第 5 个元素显示为 123987564 而不是空字符串。
答:
将 JScript 用于经典 ASP 页的优点是,您可以使用为 JavaScript 编写的众多库之一。
就像这个:https://github.com/gkindel/CSV-JS。下载它,将其包含在您的 ASP 页面中,用它解析 CSV。
<%@ 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>
评论
描述
我认为与其使用拆分,不如简单地执行匹配并处理所有找到的匹配项会更容易。
此表达式将:
- 在逗号分隔符上划分示例文本
- 将处理空值
- 将忽略双引号逗号,前提是双引号不嵌套
- 从返回值中剪裁分隔逗号
- 从返回值中剪裁周围的引号
- 如果字符串以逗号开头,则第一个捕获组将返回 null 值
正则表达式:(?:^|,)(?=[^"]|(")?)"?((?(1)[^"]*|[^,"]*))"?(?=,|$)
例
示例文本
123,2.99,AMO024,Title,"Description, more info",,123987564
使用非 java 表达式的 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
使用非 java 表达式进行匹配
组 0 获取包含逗号
的整个子字符串 组 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
编辑
正如鲍里斯所指出的,CSV格式将把双引号转义为双双引号。尽管 OP 中没有包含此要求,但如果您的文本包含双引号,那么您将需要使用以下修改后的表达式:"
""
正则表达式:(?:^|,)(?=[^"]|(")?)"?((?(1)(?:[^"]|"")*|[^,"]*))"?(?=,|$)
参见:https://regex101.com/r/y8Ayag/1
还应该指出的是,正则表达式是一个模式匹配工具,而不是一个解析引擎。因此,如果您的文本包含双双引号,则在模式匹配完成后,它仍将包含双双引号。使用此解决方案,您仍然需要搜索双引号并在捕获的文本中替换它们。
评论
providing double quotes are not nested
将单引号值与转义的 [doubled] 单引号匹配的正确正则表达式是:
'([^n']|(''))+'
我个人尝试了许多正则表达式,但没有找到与所有情况相匹配的完美表达式。
我认为正则表达式很难正确配置以正确匹配所有情况。 虽然很少有人不喜欢命名空间(我是其中的一员),但我提出了一些作为 .Net 框架一部分的东西,并在所有情况下都给我适当的结果(主要是很好地管理每个双引号大小写):
Microsoft.VisualBasic.FileIO.TextFieldParser
在这里找到它: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)
{
...
希望它能有所帮助。
我也有类似的需求,需要从 SQL 插入语句中拆分 CSV 值。
就我而言,我可以假设字符串用单引号括起来,而数字不是。
csv.split(/,((?=')|(?=\d))/g).filter(function(x) { return x !== '';});
出于一些可能显而易见的原因,此正则表达式会产生一些空白结果。我可以忽略这些,因为我的数据中的任何空值都表示为 而不是 ....,'',...
...,,...
我也需要这个答案,但我发现这些答案虽然信息量很大,但对其他语言来说有点难以理解和复制。这是我为 CSV 行中的单个列想出的最简单的表达式。我不是在分裂。我正在构建一个正则表达式来匹配 CSV 中的一列,因此我不会拆分该行:
("([^"]*)"|[^,]*)(,|$)
这与 CSV 行中的单个列匹配。表达式的第一部分是匹配带引号的条目,第二部分是匹配未带引号的条目。然后,后跟 a 或行尾。"([^"]*)"
[^,]*
,
$
以及随附的 debuggex 来测试表达式。
https://www.debuggex.com/r/s4z_Qi2gZiyzpAhx
评论
在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,"
缺点:不起作用,当列有奇数个引号时:(
如果我尝试使用“g”标志在 http://regex101.com 上发布@chubbsondubs正则表达式,则会出现仅包含“”“或空字符串的匹配项。
使用此正则表达式:
我可以匹配 CSV 的各个部分(包括带引号的部分)。(该行必须以“”结尾,否则无法识别最后一部分。
https://regex101.com/r/dF9kQ8/4
如果 CSV 如下所示:有 4 个匹配项:
'' 'huhu' 'hel lo' 'world'
(?:"([^"]*)"|([^,]*))(?:[,])
"",huhu,"hel lo",world,
几个月前,我为一个项目创建了这个。
".+?"|[^"]+?(?=,)|(?<=,)[^"]+
它在 C# 中工作,当我选择 Python 和 PCRE 时,Debuggex 很高兴。Javascript 无法识别这种形式的 Proceeded By ?<=...。
对于您的值,它将在
123
,2.99
,AMO024
,Title
"Description, more info"
,
,123987564
请注意,引号中的任何内容都没有前导逗号,但对于空值用例,需要尝试与前导逗号匹配。完成后,根据需要修剪值。
我使用 RegexHero.Net 来测试我的正则表达式。
评论
single,"quoted","with ""quotes""",end
"[^"]+"|[^"]+?(?=,)|(?<=,)[^"]+
(?:")
\"
|,$
如果您知道不会有空字段 (,,),那么此表达式效果很好:
("[^"]*"|[^,]+)
如以下示例所示...
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
但是,如果您预计会有一个空字段,并且您的文本相对较小,则可以考虑在解析之前将空字段替换为空格,以确保捕获它们。例如。。。
...
Set col = rx.Execute(Replace(sText, ",,", ", ,"))
...
如果需要保持字段的完整性,可以恢复逗号并测试循环内的空格。这可能不是最有效的方法,但它可以完成工作。
评论
另一个答案是一些额外的功能,例如支持包含转义引号和 CR/LF 字符(跨多行的单个值)的引号值。
注意:尽管下面的解决方案可能适用于其他正则表达式引擎,但按原样使用它将要求正则表达式引擎将使用相同名称的多个命名捕获组视为单个捕获组。(默认情况下,.NET 执行此操作)
当 CSV 文件/流的多行/记录(匹配 RFC 标准 4180)传递到下面的正则表达式时,它将返回每个非空行/记录的匹配项。每个匹配项将包含一个名为的捕获组,该组包含该行/记录中捕获的值(如果行/记录末尾有开引号,则可能是一个 OpenValue
捕获组)。Value
这是注释的模式(在 Regexstorm.net 上进行测试):
(?<=\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
这是没有所有注释或空格的原始模式。
[这是来自 Debuggex.com][3]的可视化效果(为清晰起见,捕获组命名): ![Debuggex.com 可视化][4]
(?<=\r|\n|^)(?!\r|\n|$)(?:(?:"(?<Value>(?:[^"]|"")*)"|(?<Value>(?!")[^,\r\n]+)|"(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)|(?<Value>))(?:,|(?=\r|\n|$)))+?(?:(?<=,)(?<Value>))?(?:\r\n|\r|\n|$)
有关如何使用正则表达式模式的示例可以在我对类似问题的回答中找到 这里, 或者在 C# pad 上 这里, 或者 这里.
评论
,?\s*'.+?'|,?\s*".+?"|[^"']+?(?=,)|[^"']+
这个正则表达式适用于单引号和双引号,也适用于另一个引号中的一个引号!
这个符合我在 c# 中需要的所有内容:
(?<=(^|,)(?<quote>"?))([^"]|(""))*?(?=\<quote>(?=,|$))
- 条带报价
- 让新线
- 让带引号的字符串中的双引号
- 让逗号在带引号的字符串中
我迟到了,但以下是我使用的正则表达式:
(?:,"|^")(""|[\w\W]*?)(?=",|"$)|(?:,(?!")|^(?!"))([^,]*?)(?=$|,)|(\r\n|\n)
此模式有三个捕获组:
- 带引号的单元格的内容
- 未加引号的单元格的内容
- 新生产线
此模式可处理以下所有操作:
- 没有任何特殊特征的正常单元格内容:一、二、三
- 包含双引号的单元格(“转义为”“):无引号,”一个“”引号“的东西”,结束
- 单元格包含换行符:one,two\nthree,four
- 具有内部引号的正常单元格内容:一,二“三,四
- 单元格包含引号后跟逗号:一,“二”“三”“,四”,五
如果您正在使用功能更强大的正则表达式风格,并带有命名组和后视,我更喜欢以下内容:
(?<quoted>(?<=,"|^")(?:""|[\w\W]*?)*(?=",|"$))|(?<normal>(?<=,(?!")|^(?!"))[^,]*?(?=(?<!")$|(?<!"),))|(?<eol>\r\n|\n)
编辑
(?:^"|,")(""|[\w\W]*?)(?=",|"$)|(?:^(?!")|,(?!"))([^,]*?)(?=$|,)|(\r\n|\n)
只要您不使用 Javascript,这种略微修改的模式就会处理第一列为空的行。出于某种原因,Javascript 将省略具有此模式的第二列。我无法正确处理这种边缘情况。
评论
..., column5,
var pat = new System.Text.RegularExpressions.Regex(@"(?:^""|,"")(""""|[\w\W]*?)(?="",|""$)|(?:^(?!"")|,(?!""))([^,]*?)(?=$|,)|(\r\n|\n)", System.Text.RegularExpressions.RegexOptions.Multiline);
var all = pat.Matches(",one,two,\"lets test, some \"\"quotes\"\"\",three,");
MatchCollection(5) { [], [,two], [,"lets test, some ""quotes""], [,three], [,] }
\r\n
我正在使用这个,它适用于昏迷分隔符和双引号转义。 通常,这应该可以解决您的问题:
/(?<=^|,)(\"(?:[^"]+|"")*\"|[^,]*)(?:$|,)/g
研究了一段时间,并提出了这个解决方案:
(?:,|\n|^)("(?:(?:"")*[^"]*)*"|[^",\n]*|(?:\n|$))
此解决方案可处理“不错的”CSV 数据,例如
"a","b",c,"d",e,f,,"g"
0: "a"
1: "b"
2: c
3: "d"
4: e
5: f
6:
7: "g"
还有更丑陋的东西,比如
"""test"" one",test' two,"""test"" 'three'","""test 'four'"""
0: """test"" one"
1: test' two
2: """test"" 'three'"
3: """test 'four'"""
以下是其工作原理的解释:
(?:,|\n|^) # all values must start at the beginning of the file,
# the end of the previous line, or at a comma
( # single capture group for ease of use; CSV can be either...
" # ...(A) a double quoted string, beginning with a double quote (")
(?: # character, containing any number (0+) of
(?:"")* # escaped double quotes (""), or
[^"]* # non-double quote characters
)* # in any order and any number of times
" # and ending with a double quote character
| # ...or (B) a non-quoted value
[^",\n]* # containing any number of characters which are not
# double quotes ("), commas (,), or newlines (\n)
| # ...or (C) a single newline or end-of-file character,
# used to capture empty values at the end of
(?:\n|$) # the file or at the ends of lines
)
评论
,foo,bar
""
啊啊和另一个答案在这里。:)因为我无法让其他人完全工作。
我的解决方案都处理转义引号(重复出现),并且匹配中不包含分隔符。
请注意,我一直在匹配 '
而不是 “
,因为这是我的场景,但只需在模式中替换它们即可获得相同的效果。
这里是(如果您使用下面的注释版本,请记住使用“忽略空格”标志):/x
# Only include if previous char was start of string or delimiter
(?<=^|,)
(?:
# 1st option: empty quoted string (,'',)
'{2}
|
# 2nd option: nothing (,,)
(?:)
|
# 3rd option: all but quoted strings (,123,)
# (included linebreaks to allow multiline matching)
[^,'\r\n]+
|
# 4th option: quoted strings (,'123''321',)
# start pling
'
(?:
# double quote
'{2}
|
# or anything but quotes
[^']+
# at least one occurance - greedy
)+
# end pling
'
)
# Only include if next char is delimiter or end of string
(?=,|$)
单线版本:
(?<=^|,)(?:'{2}|(?:)|[^,'\r\n]+|'(?:'{2}|[^']+)+')(?=,|$)
我用这个表达方式。它考虑了我遇到的逗号后面的空格。
(?:,"|^"|, ")(""|[\w\W]*?)(?=",|"$)|(?:,(?!")|^(?!"))([^,]*?)(?=$|,)|(\r\n|\n)
评论