How can I parse a CSV string with JavaScript, which contains comma in data?

前端 未结 17 914
不知归路
不知归路 2020-11-22 01:52

I have the following type of string

var string = "\'string, duppi, du\', 23, lala"

I want to split the string into an array on each

相关标签:
17条回答
  • 2020-11-22 02:16

    People seemed to be against RegEx for this. Why?

    (\s*'[^']+'|\s*[^,]+)(?=,|$)
    

    Here's the code. I also made a fiddle.

    String.prototype.splitCSV = function(sep) {
      var regex = /(\s*'[^']+'|\s*[^,]+)(?=,|$)/g;
      return matches = this.match(regex);    
    }
    
    var string = "'string, duppi, du', 23, 'string, duppi, du', lala";
    var parsed = string.splitCSV();
    alert(parsed.join('|'));
    
    0 讨论(0)
  • 2020-11-22 02:16

    Adding one more to the list, because I find all of the above not quite "KISS" enough.

    This one uses regex to find either commas or newlines while skipping over quoted items. Hopefully this is something noobies can read through on their own. The splitFinder regexp has three things it does (split by a |):

    1. , - finds commas
    2. \r?\n - finds new lines, (potentially with carriage return if the exporter was nice)
    3. "(\\"|[^"])*?" - skips anynthing surrounded in quotes, because commas and newlines don't matter in there. If there is an escaped quote \\" in the quoted item, it will get captured before an end quote can be found.

    const splitFinder = /,|\r?\n|"(\\"|[^"])*?"/g;
    
    function csvTo2dArray(parseMe) {
      let currentRow = [];
      const rowsOut = [currentRow];
      let lastIndex = splitFinder.lastIndex = 0;
      
      // add text from lastIndex to before a found newline or comma
      const pushCell = (endIndex) => {
        endIndex = endIndex || parseMe.length;
        const addMe = parseMe.substring(lastIndex, endIndex);
        // remove quotes around the item
        currentRow.push(addMe.replace(/^"|"$/g, ""));
        lastIndex = splitFinder.lastIndex;
      }
    
    
      let regexResp;
      // for each regexp match (either comma, newline, or quoted item)
      while (regexResp = splitFinder.exec(parseMe)) {
        const split = regexResp[0];
    
        // if it's not a quote capture, add an item to the current row
        // (quote captures will be pushed by the newline or comma following)
        if (split.startsWith(`"`) === false) {
          const splitStartIndex = splitFinder.lastIndex - split.length;
          pushCell(splitStartIndex);
    
          // then start a new row if newline
          const isNewLine = /^\r?\n$/.test(split);
          if (isNewLine) { rowsOut.push(currentRow = []); }
        }
      }
      // make sure to add the trailing text (no commas or newlines after)
      pushCell();
      return rowsOut;
    }
    
    const rawCsv = `a,b,c\n"test\r\n","comma, test","\r\n",",",\nsecond,row,ends,with,empty\n"quote\"test"`
    const rows = csvTo2dArray(rawCsv);
    console.log(rows);

    0 讨论(0)
  • 2020-11-22 02:18

    Aside from the excellent and complete answer from ridgerunner, I thought of a very simple workaround for when your backend runs PHP.

    Add this PHP file to your domain's backend (say: csv.php)

    <?php
        session_start(); // Optional
        header("content-type: text/xml");
        header("charset=UTF-8");
        // Set the delimiter and the End of Line character of your CSV content:
        echo json_encode(array_map('str_getcsv', str_getcsv($_POST["csv"], "\n")));
    ?>
    

    Now add this function to your JavaScript toolkit (should be revised a bit to make crossbrowser I believe).

    function csvToArray(csv) {
        var oXhr = new XMLHttpRequest;
        oXhr.addEventListener("readystatechange",
            function () {
                if (this.readyState == 4 && this.status == 200) {
                    console.log(this.responseText);
                    console.log(JSON.parse(this.responseText));
                }
            }
        );
        oXhr.open("POST","path/to/csv.php",true);
        oXhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
        oXhr.send("csv=" + encodeURIComponent(csv));
    }
    

    It will cost you one Ajax call, but at least you won't duplicate code nor include any external library.

    Ref: http://php.net/manual/en/function.str-getcsv.php

    0 讨论(0)
  • 2020-11-22 02:24

    No regexp, readable, and according to https://en.wikipedia.org/wiki/Comma-separated_values#Basic_rules:

    function csv2arr(str: string) {
        let line = ["",];
        const ret = [line,];
        let quote = false;
    
        for (let i = 0; i < str.length; i++) {
            const cur = str[i];
            const next = str[i + 1];
    
            if (!quote) {
                const cellIsEmpty = line[line.length - 1].length === 0;
                if (cur === '"' && cellIsEmpty) quote = true;
                else if (cur === ",") line.push("");
                else if (cur === "\r" && next === "\n") { line = ["",]; ret.push(line); i++; }
                else if (cur === "\n" || cur === "\r") { line = ["",]; ret.push(line); }
                else line[line.length - 1] += cur;
            } else {
                if (cur === '"' && next === '"') { line[line.length - 1] += cur; i++; }
                else if (cur === '"') quote = false;
                else line[line.length - 1] += cur;
            }
        }
        return ret;
    }
    
    0 讨论(0)
  • 2020-11-22 02:24

    You can use papaparse.js like the example below:

    <!DOCTYPE html>
    <html lang="en">
    
        <head>
            <title>CSV</title>
        </head>
    
        <body>
            <input type="file" id="files" multiple="">
            <button onclick="csvGetter()">CSV Getter</button>
            <h3>The Result will be in the Console.</h3>
    
            <script src="papaparse.min.js"></script>
    
            <script>
                function csvGetter() {
    
                    var file = document.getElementById('files').files[0];
                    Papa.parse(file, {
                        complete: function(results) {
                            console.log(results.data);
                        }
                    });
                }
              </script>
        </body>
    
    </html>
    

    Don't forget to include papaparse.js in the same folder.

    0 讨论(0)
提交回复
热议问题