使用Javascript按逗号拆分字符串,但忽略双引号内的逗号

89
我想把[a, b, c, "d, e, f", g, h]转换为一个6个元素的数组: a, b, c, "d,e,f", g, h。我尝试通过JavaScript实现这个目标。以下是我的尝试代码:

str = str.split(/,+|"[^"]+"/g); 

但现在它正在分离出所有在双引号中的内容,这是不正确的。

编辑:好的,对不起,我表达得很差。我收到了一个字符串而不是一个数组。

var str = 'a, b, c, "d, e, f", g, h';

我想使用类似于“split”函数的东西将那个转换为数组。


3
正则表达式不是这个最好的工具,因为正则表达式不保存状态。 - Amber
@Amber:那么最好的工具是什么? - gen_Eric
1
字符串操作,当然啦!我正在准备一个答案... - Elliot Bonneville
如果由于某些原因这里的答案不能满足您特定的用例,就像我遇到的情况一样,您可以尝试在此重复问题中的答案:https://dev59.com/WmAg5IYBdhLWcg3wb6od#23582323 - Mathieu de Lorimier
如果这个解决方案不起作用,我建议使用另一个解决方案:https://stackoverflow.com/questions/57576681/how-can-i-split-by-commas-while-ignoring-any-comma-thats-inside-quotes/57576855#57576855 - Matt123
18个回答

112

这是我会做的事情。

var str = 'a, b, c, "d, e, f", g, h';
var arr = str.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g);

在此输入图片描述 /* 将匹配:

    (
        ".*?"       double quotes + anything but double quotes + double quotes
        |           OR
        [^",\s]+    1 or more characters excl. double quotes, comma or spaces of any kind
    )
    (?=             FOLLOWED BY
        \s*,        0 or more empty spaces and a comma
        |           OR
        \s*$        0 or more empty spaces and nothing else (end of string)
    )
    
*/
arr = arr || [];
// this will prevent JS from throwing an error in
// the below loop when there are no matches
for (var i = 0; i < arr.length; i++) console.log('arr['+i+'] =',arr[i]);

3
太棒了,正则表达式很好。但是/".*"|[^,"\s]+/不已经足够了吗? - user235273
14
这种方法无法处理这样的字符串:'Hello World, b, c, "d, e, f", c'。它返回 ["World","b","c","d, e, f", "c"] - m.spyratos
17
要使其适用于空格之间,请使用更新的形式:(".*?"|[^",]+)(?=\s*,|\s*$),请参见此链接 - arkoak
6
当第一列没有数据时无法正常工作(从Excel导出),col2_val,col3_val - Andrew
2
在“”内部有额外的“”。例如,字符串'a,b,c,“d,e,f”,g,h'变成数组["a","b","c",""d,e,f"","g","h"]。 - zhihong
显示剩余11条评论

41

正则表达式: /,(?=(?:(?:[^"]*"){2})*[^"]*$)/

输入图像描述

const input_line = '"2C95699FFC68","201 S BOULEVARDRICHMOND, VA 23220","8299600062754882","2018-09-23"'

let my_split = input_line.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/)[4]

Output: 
my_split[0]: "2C95699FFC68", 
my_split[1]: "201 S BOULEVARDRICHMOND, VA 23220", 
my_split[2]: "8299600062754882", 
my_split[3]: "2018-09-23"

请参考以下链接以获取解释:regexr.com/44u6o


11
这对我非常有效,但如何更改以不包括结果中的外部引号? - ScottFoster1000
稍微修改了一下,现在可以接受像'http://url-to-something.test/1', "打开,这个消息?", '确定要打开这个垃圾邮件吗?'这样的值。示例在此:https://regex101.com/r/rzacJ7/1 - Satch

11

这里是一个用JavaScript编写的函数:

function splitCSVButIgnoreCommasInDoublequotes(str) {  
    //split the str first  
    //then merge the elments between two double quotes  
    var delimiter = ',';  
    var quotes = '"';  
    var elements = str.split(delimiter);  
    var newElements = [];  
    for (var i = 0; i < elements.length; ++i) {  
        if (elements[i].indexOf(quotes) >= 0) {//the left double quotes is found  
            var indexOfRightQuotes = -1;  
            var tmp = elements[i];  
            //find the right double quotes  
            for (var j = i + 1; j < elements.length; ++j) {  
                if (elements[j].indexOf(quotes) >= 0) {  
                    indexOfRightQuotes = j; 
                    break;
                }  
            }  
            //found the right double quotes  
            //merge all the elements between double quotes  
            if (-1 != indexOfRightQuotes) {   
                for (var j = i + 1; j <= indexOfRightQuotes; ++j) {  
                    tmp = tmp + delimiter + elements[j];  
                }  
                newElements.push(tmp);  
                i = indexOfRightQuotes;  
            }  
            else { //right double quotes is not found  
                newElements.push(elements[i]);  
            }  
        }  
        else {//no left double quotes is found  
            newElements.push(elements[i]);  
        }  
    }  

    return newElements;  
}  

9
这是一个不使用正则表达式的方案,假设双引号成对出现:

function splitCsv(str) {
  return str.split(',').reduce((accum,curr)=>{
    if(accum.isConcatting) {
      accum.soFar[accum.soFar.length-1] += ','+curr
    } else {
      accum.soFar.push(curr)
    }
    if(curr.split('"').length % 2 == 0) {
      accum.isConcatting= !accum.isConcatting
    }
    return accum;
  },{soFar:[],isConcatting:false}).soFar
}

console.log(splitCsv('asdf,"a,d",fdsa'),' should be ',['asdf','"a,d"','fdsa'])
console.log(splitCsv(',asdf,,fds,'),' should be ',['','asdf','','fds',''])
console.log(splitCsv('asdf,"a,,,d",fdsa'),' should be ',['asdf','"a,,,d"','fdsa'])


9
这对我很有效。(我使用分号,这样警告消息就能显示将数组转换为字符串并添加逗号与实际捕获值之间的区别。)
正则表达式
/("[^"]*")|[^;]+/

enter image description here

var str = 'a; b; c; "d; e; f"; g; h; "i"';
var array = str.match(/("[^"]*")|[^;]+/g); 
alert(array);

5
这个不能正确解析空字段,例如a;b;;c - DF_
2
@DFM:这取决于“正确”意味着什么。此外,原始问题暗示没有“;;”情况。 - John Fisher

7
这是我们使用的正则表达式 , 用于从逗号分隔的参数列表中提取有效参数, 支持双引号参数。它适用于所述边缘情况。例如:
- 不包括匹配中的引号 - 在匹配中使用空格 - 在空字段中工作
证明: https://regex101.com/r/UL8kyy/3/tests (注意:目前仅在Chrome中运行,因为正则表达式使用了后顾断言,而后者仅在ECMA2018中受支持)
根据我们的指南,它避免了非捕获组和贪婪匹配。
我相信它可以简化,我也接受建议/附加测试用例。
对于任何感兴趣的人,第一部分匹配双引号、逗号分隔的参数: (?<=")[^"]+?(?="(?:\s*?,|\s*?$)) 第二部分匹配单独的逗号分隔的参数: (?<=(?:^|,)\s*?)(?:[^,"\s][^,"]*[^,"\s])|(?:[^,"\s])(?![^"]*?"(?:\s*?,|\s*?$))(?=\s*?(?:,|$))

我无法使其与空字段 (,,, 或者 ,"","",) 正常工作,所以我必须首先这样做:row = row.split(',').map(p => (p && p || '"_"')).join(','); - Kristian MT
1
你可以将第一部分更改为(?<=")[^"]*?(?="(?:\s*?,|\s*?$))以匹配空参数。例如,"foo", "", "bar"将有3个匹配项。 - thisismydesign
@thisismydesign 这个能否修改一下,使其也能接受 CSV 文件中的空值呢?例如,test,,hello,goodbye 应该有 4 个匹配项。 - Colin Null
@Colin Null 我相信这是可以做到的,但我不建议使用它来解析 CSV。你需要考虑很多边缘情况,比如转义分隔符。最好使用一个库来完成。 - thisismydesign

6

我几乎喜欢那个被接受的答案,但它没有正确解析空格,和/或者它没有修剪掉双引号,所以这里是我的函数:

    /**
     * Splits the given string into components, and returns the components array.
     * Each component must be separated by a comma.
     * If the component contains one or more comma(s), it must be wrapped with double quotes.
     * The double quote must not be used inside components (replace it with a special string like __double__quotes__ for instance, then transform it again into double quotes later...).
     *
     * https://dev59.com/imgu5IYBdhLWcg3wQE8D
     */
    function splitComponentsByComma(str){
        var ret = [];
        var arr = str.match(/(".*?"|[^",]+)(?=\s*,|\s*$)/g);
        for (let i in arr) {
            let element = arr[i];
            if ('"' === element[0]) {
                element = element.substr(1, element.length - 2);
            } else {
                element = arr[i].trim();
            }
            ret.push(element);
        }
        return ret;
    }
    console.log(splitComponentsByComma('Hello World, b, c, "d, e, f", c')); // [ 'Hello World', 'b', 'c', 'd, e, f', 'c' ]

2
这个答案唯一的问题(我复制它后很长时间才发现)是它忽略了空条目,比如“test1,,test2”。在逗号之间没有任何内容会使你的正则表达式跳过它。最终我使用了@f-society的答案。 - Raphael Setin
你好,能否请您指向@f-society的答案。我想将这个'o,"sadasdasd",123123123,"asdasdasd.www.org,123123,link.com",0,,123'拆分成7个字段。 - Dinakar Ullas

3

使用TYPESCRIPT解析任何CSV或CSV字符串代码

public parseCSV(content:string):any[string]{
        return content.split("\n").map(ar=>ar.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/).map(refi=>refi.replace(/[\x00-\x08\x0E-\x1F\x7F-\uFFFF]/g, "").trim()));
    }

var str='"abc",jkl,1000,qwerty6000';

parseCSV(str);

输出:

[
"abc","jkl","1000","qwerty6000"
]

1

1

我知道这有点长,但这是我的看法:

var sample="[a, b, c, \"d, e, f\", g, h]";

var inQuotes = false, items = [], currentItem = '';

for(var i = 0; i < sample.length; i++) {
  if (sample[i] == '"') { 
    inQuotes = !inQuotes; 

    if (!inQuotes) {
      if (currentItem.length) items.push(currentItem);
      currentItem = '';
    }

    continue; 
  }

  if ((/^[\"\[\]\,\s]$/gi).test(sample[i]) && !inQuotes) {
    if (currentItem.length) items.push(currentItem);
    currentItem = '';
    continue;
  }

  currentItem += sample[i];
}

if (currentItem.length) items.push(currentItem);

console.log(items);

作为一个附注,它将在开头和结尾都使用或不使用大括号。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接