【问题标题】:Split a string by commas but ignore commas within double-quotes using Javascript用逗号分割字符串,但使用 Javascript 忽略双引号内的逗号
【发布时间】:2012-07-12 11:59:03
【问题描述】:

我正在寻找 [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”函数的东西将那个变成一个数组。

【问题讨论】:

标签: javascript regex


【解决方案1】:

假设您的字符串确实看起来像 '[a, b, c, "d, e, f", g, h]',我相信这将是“eval() 可接受的用例:

myString = 'var myArr ' + myString;
eval(myString);

console.log(myArr); // will now be an array of elements: a, b, c, "d, e, f", g, h

编辑:正如 Rocket 所指出的,strict 模式消除了eval 将变量注入本地范围的能力,这意味着您想要这样做:

var myArr = eval(myString);

【讨论】:

  • 取决于该字符串的来源。
  • 如果你要使用eval,我建议var myArr = eval(myString)。我认为严格模​​式消除了evals 将变量注入本地范围的能力。
  • 等等。这不会像你想象的那样工作。 eval 将搜索变量 abc 等。它不会使它们成为字符串。
  • 嗯,它不适用于 eval。它将尝试评估变量ab 等。
  • 这不是 OP 想要的吗,@bažmegakapa?除非他想要一个数组["a", "b", "c"...]
【解决方案2】:

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

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);

附带说明,它可以在开头和结尾使用和不使用大括号。

【讨论】:

    【解决方案3】:

    这就是我要做的。

    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]);
    

    【讨论】:

    • 很棒的正则表达式伴侣。但是/".*"|[^,"\s]+/ 还不够吗?
    • 这不适用于像这样的字符串:'Hello World, b, c, "d, e, f", c'。它返回["World","b","c","d, e, f", "c"]
    • 不错,但单词之间的空格分开,我修改为/(".*?"|[^\s",][^",]+[^\s",])(?=\s*,|\s*$)/
    • 要使其与中间的空格一起使用,请使用更新后的表格:(".*?"|[^",]+)(?=\s*,|\s*$),请参阅this
    • 第一列没有数据时不起作用(从excel导出),col2_val,col3_val
    【解决方案4】:

    这对我很有效。 (我使用了分号,因此警报消息会显示将数组转换为字符串时添加的逗号与实际捕获的值之间的差异。)

    正则表达式

    /("[^"]*")|[^;]+/
    

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

    【讨论】:

    • 这个不能正确解析像a;b;;c这样的空字段
    • @DFM:这取决于“正确”的含义。此外,原始问题暗示没有“;;”场景。
    【解决方案5】:

    我也遇到过类似的问题,但我没有找到好的 .net 解决方案,所以就自己动手做了。注意:这也用于回复

    Splitting comma separated string, ignore commas in quotes, but allow strings with one double quotation

    但在这里似乎更适用(但在那里有用)

    在我的应用程序中,我正在解析一个 csv,因此我的拆分凭据是“,”。我想这种方法只适用于你有一个 char split 参数的地方。

    所以,我编写了一个忽略双引号内逗号的函数。它通过将输入字符串转换为字符数组并按字符解析字符来实现

    public static string[] Splitter_IgnoreQuotes(string stringToSplit)
        {   
            char[] CharsOfData = stringToSplit.ToCharArray();
            //enter your expected array size here or alloc.
            string[] dataArray = new string[37];
            int arrayIndex = 0;
            bool DoubleQuotesJustSeen = false;          
            foreach (char theChar in CharsOfData)
            {
                //did we just see double quotes, and no command? dont split then. you could make ',' a variable for your split parameters I'm working with a csv.
                if ((theChar != ',' || DoubleQuotesJustSeen) && theChar != '"')
                {
                    dataArray[arrayIndex] = dataArray[arrayIndex] + theChar;
                }
                else if (theChar == '"')
                {
                    if (DoubleQuotesJustSeen)
                    {
                        DoubleQuotesJustSeen = false;
                    }
                    else
                    {
                        DoubleQuotesJustSeen = true;
                    }
                }
                else if (theChar == ',' && !DoubleQuotesJustSeen)
                {
                    arrayIndex++;
                }
            }
            return dataArray;
        }
    

    根据我的应用程序的口味,这个函数也会忽略任何输入中的 (""),因为这些是不需要的并且出现在我的输入中。

    【讨论】:

    • 此代码在这里不起作用。它是用 Java 编写的,但 this 问题需要 javascript
    【解决方案6】:

    这是一个 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;  
    }  
    

    【讨论】:

      【解决方案7】:

      堆栈之类的东西应该可以解决问题。在这里,我模糊地将标记布尔值用作堆栈(只是为了达到我的目的)。

      var str = "a,b,c,blah\"d,=,f\"blah,\"g,h,";
      var getAttributes = function(str){
        var result = [];
        var strBuf = '';
        var start = 0 ;
        var marker = false;
        for (var i = 0; i< str.length; i++){
      
          if (str[i] === '"'){
            marker = !marker;
          }
          if (str[i] === ',' && !marker){
            result.push(str.substr(start, i - start));
            start = i+1;
          }
        }
        if (start <= str.length){
          result.push(str.substr(start, i - start));
        }
        return result;
      };
      
      console.log(getAttributes(str));
      

      【讨论】:

        【解决方案8】:

        这是一个假设双引号成对出现的非正则表达式:

        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】:

          jsfiddle setting imagecode output image

          如果您的输入字符串格式为 stringTocompare,则该代码有效。 在https://jsfiddle.net/ 上运行代码以查看 fiddlejs 设置的输出。 请参考截图。 您可以对其下面的代码使用相同的拆分功能,并根据需要调整代码。 如果您不想在 split attach=attach**+","**+actualString[t+1] 之后使用逗号,请从代码中删除粗体或带 in 的单词。

          var stringTocompare='"Manufacturer","12345","6001","00",,"Calfe,eto,lin","Calfe,edin","4","20","10","07/01/2018","01/01/2006",,,,,,,,"03/31/2004"';
          
          console.log(stringTocompare);
          
          var actualString=stringTocompare.split(',');
          console.log("Before");
          for(var i=0;i<actualString.length;i++){
          console.log(actualString[i]);
          }
          //var actualString=stringTocompare.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);
          for(var i=0;i<actualString.length;i++){
          var flag=0;
          var x=actualString[i];
          if(x!==null)
          {
          if(x[0]=='"' && x[x.length-1]!=='"'){
             var p=0;
             var t=i;
             var b=i;
             for(var k=i;k<actualString.length;k++){
             var y=actualString[k];
                  if(y[y.length-1]!=='"'){        
                  p++;
                  }
                  if(y[y.length-1]=='"'){
          
                          flag=1;
                  }
                  if(flag==1)
                  break;
             }
             var attach=actualString[t];
          for(var s=p;s>0;s--){
          
            attach=attach+","+actualString[t+1];
            t++;
          }
          actualString[i]=attach;
          actualString.splice(b+1,p);
          }
          }
          
          
          }
          console.log("After");
          for(var i=0;i<actualString.length;i++){
          console.log(actualString[i]);
          }
          
          
          
          
            [1]: https://i.stack.imgur.com/3FcxM.png
          

          【讨论】:

          【解决方案10】:

          正则表达式:/,(?=(?:(?:[^"]*"){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】:

          这一次获取一个 csv 文件,然后返回一个数组,其中包含完整的语音标记内的逗号。如果没有检测到语音标记,它只是 .split(",")s 正常...可能会用某些东西替换第二个循环,但它可以按原样完成工作

          function parseCSVLine(str){
              if(str.indexOf("\"")>-1){
                  var aInputSplit = str.split(",");
                  var aOutput = [];
                  var iMatch = 0;
                  //var adding = 0;
                  for(var i=0;i<aInputSplit.length;i++){
                      if(aInputSplit[i].indexOf("\"")>-1){
                          var sWithCommas = aInputSplit[i];
                          for(var z=i;z<aInputSplit.length;z++){
                              if(z !== i && aInputSplit[z].indexOf("\"") === -1){
                                  sWithCommas+= ","+aInputSplit[z];
                              }else if(z !== i && aInputSplit[z].indexOf("\"") > -1){
                                  sWithCommas+= ","+aInputSplit[z];
                                  sWithCommas.replace(new RegExp("\"", 'g'), "");
                                  aOutput.push(sWithCommas);
                                  i=z;
                                  z=aInputSplit.length+1;
                                  iMatch++;
                              }
                              if(z === aInputSplit.length-1){
                                  if(iMatch === 0){
                                      aOutput.push(aInputSplit[z]);
                                  }                  
                                  iMatch = 0;
                              }
                          }
                      }else{
                          aOutput.push(aInputSplit[i]);
                      }
                  }
                  return aOutput
              }else{
                  return str.split(",")
              }
          }
          

          【讨论】:

            【解决方案12】:

            这是正则表达式we're using,用于从逗号分隔的参数列表中提取有效参数,支持双引号参数。它适用于概述的边缘情况。例如

            • 匹配中不包含引号
            • 在匹配中使用空格
            • 适用于空字段

            (?&lt;=")[^"]+?(?="(?:\s*?,|\s*?$))|(?&lt;=(?:^|,)\s*?)(?:[^,"\s][^,"]*[^,"\s])|(?:[^,"\s])(?![^"]*?"(?:\s*?,|\s*?$))(?=\s*?(?:,|$))

            证明:https://regex101.com/r/UL8kyy/3/tests注意:目前仅适用于 Chrome,因为正则表达式使用仅在 ECMA2018 中支持的lookbehinds

            根据our guidelines,它避免了非捕获组和贪婪匹配。

            我确信它可以被简化,我愿意接受建议/额外的测试用例。

            对于任何感兴趣的人,第一部分匹配双引号、逗号分隔的参数:

            (?&lt;=")[^"]+?(?="(?:\s*?,|\s*?$))

            第二部分自己匹配逗号分隔的参数:

            (?&lt;=(?:^|,)\s*?)(?:[^,"\s][^,"]*[^,"\s])|(?:[^,"\s])(?![^"]*?"(?:\s*?,|\s*?$))(?=\s*?(?:,|$))

            【讨论】:

            • 我无法让它与空字段(,,, or ,"","",)一起工作,所以我必须先这样做:row = row.split(',') .map(p => (p && p || '"_"')).join(',');
            • 您可以将第一部分更改为(?&lt;=")[^"]*?(?="(?:\s*?,|\s*?$)) 以匹配空参数。例如。 "foo", "", "bar" 将有 3 个匹配项。
            • @thisismydesign 是否可以修改它以接受 CSV 文件中的空值?例如,test,,hello,goodbye 应该有 4 个匹配项。
            • @Colin Null 我确信可以做到,但我不建议使用它来解析 CSV。您必须对边缘情况进行很多思考,例如转义分隔符。改用库。
            • 有人在 Safari / 非 Chrome 中使用此功能有什么运气吗?
            【解决方案13】:

            我几乎喜欢接受的答案,但它没有正确解析空格,和/或它没有修剪双引号,所以这是我的功能:

                /**
                 * 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://stackoverflow.com/questions/11456850/split-a-string-by-commas-but-ignore-commas-within-double-quotes-using-javascript
                 */
                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' ]
            

            【讨论】:

              【解决方案14】:

              根据 TYPESCRIPT 解析任何 CSV 或 CSV-String 代码

              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"
              ]
              

              【讨论】:

                【解决方案15】:

                我用一个简单的解析器解决了这个问题。

                它只是逐个字符地遍历字符串,当它找到 split_char(例如逗号)时拆分一个段,但也有一个通过找到 encapsulator_char(例如引号)来切换的开/关标志。它不需要封装器位于字段/段的开头(a,b","c,d 会产生 3 个段,第二个是 'b","c'),但它应该适用于格式良好的 CSV,带有转义的封装字符。

                function split_except_within(text, split_char, encapsulator_char, escape_char) {
                    var start = 0
                    var encapsulated = false
                    var fields = []
                    for (var c = 0; c < text.length; c++) {
                        var char = text[c]
                        if (char === split_char && ! encapsulated) {
                            fields.push(text.substring(start, c))
                            start = c+1
                        }
                        if (char === encapsulator_char && (c === 0 || text[c-1] !== escape_char) )             
                            encapsulated = ! encapsulated
                    }
                    fields.push(text.substring(start))
                    return fields
                }
                

                https://jsfiddle.net/7hty8Lvr/1/

                【讨论】:

                  【解决方案16】:
                  const csvSplit = (line) => {
                      let splitLine = [];
                  
                      var quotesplit = line.split('"');
                      var lastindex = quotesplit.length - 1;
                      // split evens removing outside quotes, push odds
                      quotesplit.forEach((val, index) => {
                          if (index % 2 === 0) {
                              var firstchar = (index == 0) ? 0 : 1;
                              var trimmed = (index == lastindex) 
                                  ? val.substring(firstchar)
                                  : val.slice(firstchar, -1);
                              trimmed.split(",").forEach(v => splitLine.push(v));
                          } else {
                              splitLine.push(val);
                          }
                      });
                      return splitLine;
                  }
                  

                  只要引号始终出现在包含需要排除的逗号的值的外部(即 csv 文件),这将起作用。

                  如果你有像 '1,2,4"2,6",8' 这样的东西 它不会工作。

                  【讨论】:

                    【解决方案17】:

                    使用 npm 库 csv-string 来解析字符串而不是拆分:https://www.npmjs.com/package/csv-string

                    这将处理空条目

                    【讨论】:

                      猜你喜欢
                      • 2020-04-05
                      • 1970-01-01
                      • 2012-05-23
                      • 2020-08-03
                      • 1970-01-01
                      • 1970-01-01
                      • 2017-04-07
                      • 2013-10-14
                      • 1970-01-01
                      相关资源
                      最近更新 更多