데이터에 쉼표가 포함 된 JavaScript로 CSV 문자열을 구문 분석하려면 어떻게해야합니까?


93

다음 유형의 문자열이 있습니다.

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

각 쉼표에서 문자열을 배열로 나누고 싶지만 작은 따옴표 밖에있는 쉼표 만 있습니다.

분할에 적합한 정규식을 찾을 수 없습니다.

string.split(/,/)

나에게 줄 것이다

["'string", " duppi", " du'", " 23", " lala"]

그러나 결과는 다음과 같아야합니다.

["string, duppi, du", "23", "lala"]

크로스 브라우저 솔루션이 있습니까?


항상 작은 따옴표입니까? 인용 된 문자열 안에 작은 따옴표가 있습니까? 그렇다면 어떻게 이스케이프됩니까 (백 슬래시, 이중화)?
Phrogz 2011

따옴표 문자가 JavaScript 및 HTML / XML 코드에서와 같이 큰 따옴표와 작은 따옴표 사이에서 완전히 호환된다면 어떻게 될까요? 그렇다면 CSV보다 더 광범위한 파싱 작업이 필요합니다.
austincheney

실제로 예, 내부에 작은 따옴표가있을 수 있으며 백 슬래시로 이스케이프하는 것이 좋습니다.
Hans

값이 큰 따옴표로 묶인 문자열이 될 수 있습니까?
ridgerunner

1
Papa Parse는 훌륭한 일을합니다. 자바 스크립트와 파파 구문 분석과 지역 CSV 파일을 구문 분석 : joyofdata.de/blog/...
라파엘

답변:


214

부인 성명

2014-12-01 업데이트 : 아래 답변은 매우 구체적인 CSV 형식에만 적용됩니다. 의견에서 DG가 올바르게 지적했듯이이 솔루션은 CSV의 RFC 4180 정의에 맞지 않으며 MS Excel 형식에도 맞지 않습니다. 이 솔루션은 문자열에 이스케이프 된 따옴표와 쉼표가 포함될 수있는 문자열 유형의 혼합을 포함하는 하나의 (비표준) CSV 입력 행을 구문 분석하는 방법을 보여줍니다.

비표준 CSV 솔루션

austincheney가 올바르게 지적했듯이, 이스케이프 된 문자를 포함 할 수있는 따옴표로 묶인 문자열을 올바르게 처리하려면 문자열을 처음부터 끝까지 구문 분석해야합니다. 또한 OP는 "CSV 문자열"이 실제로 무엇인지 명확하게 정의하지 않습니다. 먼저 유효한 CSV 문자열과 개별 값을 구성하는 항목을 정의해야합니다.

주어진 : "CSV 문자열"정의

이 설명을 위해 "CSV 문자열"은 0 개 이상의 값으로 구성되며 여러 값은 쉼표로 구분됩니다. 각 값은 다음으로 구성 될 수 있습니다.

  1. 큰 따옴표로 묶인 문자열. (이스케이프 처리되지 않은 작은 따옴표를 포함 할 수 있습니다.)
  2. 작은 따옴표로 묶인 문자열. (이스케이프 처리되지 않은 큰 따옴표를 포함 할 수 있습니다.)
  3. 인용되지 않은 문자열입니다. (따옴표, 쉼표 또는 백 슬래시를 포함 할 수 없습니다.)
  4. 빈 값. (모두 공백 값은 비어있는 것으로 간주됩니다.)

규칙 / 참고 :

  • 인용 된 값에는 쉼표가 포함될 수 있습니다.
  • 인용 된 값은 이스케이프 된 모든 것을 포함 할 수 있습니다 'that\'s cool'.
  • 따옴표, 쉼표 또는 백 슬래시가 포함 된 값은 따옴표로 묶어야합니다.
  • 선행 또는 후행 공백이 포함 된 값은 따옴표로 묶어야합니다.
  • 백 슬래시는 모두에서 제거됩니다 : \'작은 따옴표로 묶인 값.
  • 백 슬래시는 모두에서 제거됩니다 : \"큰 따옴표로 묶인 값.
  • 인용되지 않은 문자열은 선행 및 후행 공백이 제거됩니다.
  • 쉼표 구분 기호에 인접한 공백이있을 수 있습니다 (무시 됨).

찾기:

유효한 CSV 문자열 (위에 정의 된대로)을 문자열 값의 배열로 변환하는 JavaScript 함수입니다.

해결책:

이 솔루션에서 사용하는 정규식은 복잡합니다. 그리고 (IMHO) 모든 중요하지 않은 정규식은 많은 주석과 들여 쓰기와 함께 자유 간격 모드로 표시되어야합니다. 안타깝게도 JavaScript는 자유 간격 모드를 허용하지 않습니다. 따라서이 솔루션에 의해 구현 된 정규식은 먼저 기본 정규식 구문으로 표시됩니다 (Python의 편리한 : r'''...'''raw-multi-line-string 구문을 사용하여 표현됨 ).

먼저 여기에 CVS 문자열이 위의 요구 사항을 충족하는지 확인하는 정규식이 있습니다.

"CSV 문자열"의 유효성을 검사하는 정규식 :

re_valid = r"""
# Validate a CSV string having single, double or un-quoted values.
^                                   # Anchor to start of string.
\s*                                 # Allow whitespace before value.
(?:                                 # Group for value alternatives.
  '[^'\\]*(?:\\[\S\s][^'\\]*)*'     # Either Single quoted string,
| "[^"\\]*(?:\\[\S\s][^"\\]*)*"     # or Double quoted string,
| [^,'"\s\\]*(?:\s+[^,'"\s\\]+)*    # or Non-comma, non-quote stuff.
)                                   # End group of value alternatives.
\s*                                 # Allow whitespace after value.
(?:                                 # Zero or more additional values
  ,                                 # Values separated by a comma.
  \s*                               # Allow whitespace before value.
  (?:                               # Group for value alternatives.
    '[^'\\]*(?:\\[\S\s][^'\\]*)*'   # Either Single quoted string,
  | "[^"\\]*(?:\\[\S\s][^"\\]*)*"   # or Double quoted string,
  | [^,'"\s\\]*(?:\s+[^,'"\s\\]+)*  # or Non-comma, non-quote stuff.
  )                                 # End group of value alternatives.
  \s*                               # Allow whitespace after value.
)*                                  # Zero or more additional values
$                                   # Anchor to end of string.
"""

문자열이 위의 정규식과 일치하면 해당 문자열은 유효한 CSV 문자열 (이전에 언급 한 규칙에 따라)이며 다음 정규식을 사용하여 구문 분석 할 수 있습니다. 그런 다음 다음 정규식을 사용하여 CSV 문자열에서 하나의 값을 일치시킵니다. 더 이상 일치하는 항목이없고 모든 값이 구문 분석 될 때까지 반복적으로 적용됩니다.

유효한 CSV 문자열에서 하나의 값을 구문 분석하는 정규식 :

re_value = r"""
# Match one value in valid CSV string.
(?!\s*$)                            # Don't match empty last value.
\s*                                 # Strip whitespace before value.
(?:                                 # Group for value alternatives.
  '([^'\\]*(?:\\[\S\s][^'\\]*)*)'   # Either $1: Single quoted string,
| "([^"\\]*(?:\\[\S\s][^"\\]*)*)"   # or $2: Double quoted string,
| ([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)  # or $3: Non-comma, non-quote stuff.
)                                   # End group of value alternatives.
\s*                                 # Strip whitespace after value.
(?:,|$)                             # Field ends on comma or EOS.
"""

이 정규식이 일치하지 않는 특별한 경우 값이 하나 있습니다. 해당 값이 비어있을 때 가장 마지막 값입니다. 이 특별한 "빈 마지막 값" 케이스는 뒤에 오는 js 함수에 의해 테스트되고 처리됩니다.

CSV 문자열을 구문 분석하는 JavaScript 함수 :

// Return array of string values, or NULL if CSV string not well formed.
function CSVtoArray(text) {
    var re_valid = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;
    var re_value = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
    // Return NULL if input string is not well formed CSV string.
    if (!re_valid.test(text)) return null;
    var a = [];                     // Initialize array to receive values.
    text.replace(re_value, // "Walk" the string using replace with callback.
        function(m0, m1, m2, m3) {
            // Remove backslash from \' in single quoted values.
            if      (m1 !== undefined) a.push(m1.replace(/\\'/g, "'"));
            // Remove backslash from \" in double quoted values.
            else if (m2 !== undefined) a.push(m2.replace(/\\"/g, '"'));
            else if (m3 !== undefined) a.push(m3);
            return ''; // Return empty string.
        });
    // Handle special case of empty last value.
    if (/,\s*$/.test(text)) a.push('');
    return a;
};

입력 및 출력 예 :

다음 예에서는 중괄호를 사용하여 {result strings}. (이것은 선행 / 후행 공백과 길이가 0 인 문자열을 시각화하는 데 도움이됩니다.)

// Test 1: Test string from original question.
var test = "'string, duppi, du', 23, lala";
var a = CSVtoArray(test);
/* Array hes 3 elements:
    a[0] = {string, duppi, du}
    a[1] = {23}
    a[2] = {lala} */
// Test 2: Empty CSV string.
var test = "";
var a = CSVtoArray(test);
/* Array hes 0 elements: */
// Test 3: CSV string with two empty values.
var test = ",";
var a = CSVtoArray(test);
/* Array hes 2 elements:
    a[0] = {}
    a[1] = {} */
// Test 4: Double quoted CSV string having single quoted values.
var test = "'one','two with escaped \' single quote', 'three, with, commas'";
var a = CSVtoArray(test);
/* Array hes 3 elements:
    a[0] = {one}
    a[1] = {two with escaped ' single quote}
    a[2] = {three, with, commas} */
// Test 5: Single quoted CSV string having double quoted values.
var test = '"one","two with escaped \" double quote", "three, with, commas"';
var a = CSVtoArray(test);
/* Array hes 3 elements:
    a[0] = {one}
    a[1] = {two with escaped " double quote}
    a[2] = {three, with, commas} */
// Test 6: CSV string with whitespace in and around empty and non-empty values.
var test = "   one  ,  'two'  ,  , ' four' ,, 'six ', ' seven ' ,  ";
var a = CSVtoArray(test);
/* Array hes 8 elements:
    a[0] = {one}
    a[1] = {two}
    a[2] = {}
    a[3] = { four}
    a[4] = {}
    a[5] = {six }
    a[6] = { seven }
    a[7] = {} */

추가 참고 사항 :

이 솔루션을 사용하려면 CSV 문자열이 "유효"해야합니다. 예를 들어 따옴표가없는 값에는 백 슬래시 또는 따옴표가 포함될 수 없습니다. 예를 들어 다음 CSV 문자열은 유효하지 않습니다.

var invalid1 = "one, that's me!, escaped \, comma"

하위 문자열이 작은 따옴표 또는 큰 따옴표 값으로 표현 될 수 있기 때문에 이것은 실제로 제한이 아닙니다. 또한이 솔루션은 "쉼표로 구분 된 값"에 대한 하나의 가능한 정의 만 나타냅니다.

편집 : 2014-05-19 : 면책 조항 추가. 편집 : 2014-12-01 : 면책 조항을 맨 위로 이동했습니다.


1
@Evan Plaice-좋은 말에 감사드립니다. 물론 모든 구분 기호를 사용할 수 있습니다. 내 정규식의 모든 쉼표를 선택한 구분 기호로 바꾸십시오 (하지만 구분 기호는 공백이 될 수 없습니다). 건배.
ridgerunner 2012

2
@Evan Plaice-원하는 목적으로 내 정규식을 사용할 수 있습니다. 인정 메모는 좋지만 필요하지는 않습니다. 플러그인에 행운을 빕니다. 건배!
ridgerunner

1
좋습니다. 여기 프로젝트 code.google.com/p/jquery-csv 입니다. 결국 메타 데이터 (예 : 구분 기호, 구분 기호, 줄 끝 등)가 포함 된 CSV 인 SSV (Structured Separated Values)라는 확장 형식을 CSV에 추가하고 싶습니다.
Evan Plaice

1
이 훌륭한 구현에 감사드립니다. Node.js 모듈 ( csv-iterator ) 의 기초로 사용했습니다 .
mirkokiefer

3
나는 세부 사항에 박수를 보내고 귀하의 답변을 명확하게하지만 CSV의 정의가 CSV 표준에 가까운 RFC 4180에 맞지 않으며 일화로 말할 수있는 것이 일반적으로 사용된다는 점에 주목해야합니다. 특히 이것은 문자열 필드 내에서 큰 따옴표 문자를 "이스케이프"하는 일반적인 방법입니다. "field one", "field two", "a ""final"" field containing two double quote marks"이 페이지에서 Trevor Dixon의 답변을 테스트하지는 않았지만 CSV의 RFC 4180 정의를 다루는 답변입니다.
DG.

53

RFC 4180 솔루션

형식이 RFC 4180을 따르지 않기 때문에 문제의 문자열은 해결되지 않습니다. 허용되는 인코딩은 큰 따옴표를 사용하여 큰 따옴표를 이스케이프하는 것입니다. 아래 솔루션은 Google 스프레드 시트의 CSV 파일 d / l에서 올바르게 작동합니다.

업데이트 (2017 년 3 월)

한 줄을 구문 분석하는 것은 잘못되었습니다. RFC 4180에 따르면 필드에 CRLF가 포함될 수 있으며 이로 인해 모든 행 판독기가 CSV 파일을 중단하게됩니다. 다음은 CSV 문자열을 구문 분석하는 업데이트 된 버전입니다.

'use strict';

function csvToArray(text) {
    let p = '', row = [''], ret = [row], i = 0, r = 0, s = !0, l;
    for (l of text) {
        if ('"' === l) {
            if (s && l === p) row[i] += l;
            s = !s;
        } else if (',' === l && s) l = row[++i] = '';
        else if ('\n' === l && s) {
            if ('\r' === p) row[i] = row[i].slice(0, -1);
            row = ret[++r] = [l = '']; i = 0;
        } else row[i] += l;
        p = l;
    }
    return ret;
};

let test = '"one","two with escaped """" double quotes""","three, with, commas",four with no quotes,"five with CRLF\r\n"\r\n"2nd line one","two with escaped """" double quotes""","three, with, commas",four with no quotes,"five with CRLF\r\n"';
console.log(csvToArray(test));

오래된 답변

(단일 라인 솔루션)

function CSVtoArray(text) {
    let ret = [''], i = 0, p = '', s = true;
    for (let l in text) {
        l = text[l];
        if ('"' === l) {
            s = !s;
            if ('"' === p) {
                ret[i] += '"';
                l = '-';
            } else if ('' === p)
                l = '-';
        } else if (s && ',' === l)
            l = ret[++i] = '';
        else
            ret[i] += l;
        p = l;
    }
    return ret;
}
let test = '"one","two with escaped """" double quotes""","three, with, commas",four with no quotes,five for fun';
console.log(CSVtoArray(test));

재미를 위해 배열에서 CSV를 만드는 방법은 다음과 같습니다.

function arrayToCSV(row) {
    for (let i in row) {
        row[i] = row[i].replace(/"/g, '""');
    }
    return '"' + row.join('","') + '"';
}

let row = [
  "one",
  "two with escaped \" double quote",
  "three, with, commas",
  "four with no quotes (now has)",
  "five for fun"
];
let text = arrayToCSV(row);
console.log(text);


1
이것은 다른 사람이 아니라 나를 위해 일을했습니다
WtFudgE

7

http://en.wikipedia.org/wiki/Comma-separated_values 에서 RFC 4180 예제를 처리하는 PEG (.js) 문법 :

start
  = [\n\r]* first:line rest:([\n\r]+ data:line { return data; })* [\n\r]* { rest.unshift(first); return rest; }

line
  = first:field rest:("," text:field { return text; })*
    & { return !!first || rest.length; } // ignore blank lines
    { rest.unshift(first); return rest; }

field
  = '"' text:char* '"' { return text.join(''); }
  / text:[^\n\r,]* { return text.join(''); }

char
  = '"' '"' { return '"'; }
  / [^"]

http://jsfiddle.net/knvzk/10 또는 https://pegjs.org/online 에서 테스트 하십시오 .

https://gist.github.com/3362830 에서 생성 된 파서를 다운로드합니다 .


6

Google 스프레드 시트의 셀을 웹 앱으로 복사하려는 매우 구체적인 사용 사례가있었습니다. 셀에는 큰 따옴표와 개행 문자가 포함될 수 있습니다. 복사 및 붙여 넣기를 사용하면 셀이 탭 문자로 구분되고 홀수 데이터가있는 셀은 큰 따옴표로 묶입니다. 이 주요 솔루션, regexp를 사용하는 링크 된 기사, Jquery-CSV 및 CSVToArray를 시도했습니다. http://papaparse.com/ 바로 사용할 수있는 유일한 방법입니다. 기본 자동 감지 옵션이있는 Google 스프레드 시트를 사용하면 복사 및 붙여 넣기가 원활합니다.


1
이것은 훨씬 더 높은 순위를 가져야하며, 자신의 CSV 파서를 롤링 하지 마십시오 . 특히 정규식을 사용할 때 올바르게 작동하지 않습니다 . Papaparse는 굉장합니다 -그것을 사용하십시오!
cbley

6

나는 FakeRainBrigand의 대답을 좋아했지만 몇 가지 문제가 있습니다. 따옴표와 쉼표 사이의 공백을 처리 할 수 ​​없으며 2 개의 연속 쉼표를 지원하지 않습니다. 나는 그의 답변을 편집하려고 시도했지만 내 코드를 이해하지 못한 리뷰어가 내 편집을 거부했습니다. 다음은 FakeRainBrigand 코드의 내 버전입니다. 바이올린도 있습니다 : http://jsfiddle.net/xTezm/46/

String.prototype.splitCSV = function() {
        var matches = this.match(/(\s*"[^"]+"\s*|\s*[^,]+|,)(?=,|$)/g);
        for (var n = 0; n < matches.length; ++n) {
            matches[n] = matches[n].trim();
            if (matches[n] == ',') matches[n] = '';
        }
        if (this[0] == ',') matches.unshift("");
        return matches;
}

var string = ',"string, duppi, du" , 23 ,,, "string, duppi, du",dup,"", , lala';
var parsed = string.splitCSV();
alert(parsed.join('|'));

4

사람들은 이에 대해 RegEx에 반대하는 것처럼 보였습니다. 왜?

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

여기에 코드가 있습니다. 나는 또한 바이올린을 만들었다 .

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

3
흠, 정규 표현식에는 몇 가지 문제가 있습니다. 따옴표와 쉼표 사이의 공백을 처리 할 수 ​​없으며 2 개의 연속 쉼표를 지원하지 않습니다. 두 문제를 모두 수정하고 새로운 바이올린을 만드는 코드로 답변을 업데이트했습니다. jsfiddle.net/xTezm/43
HammerNL

어떤 이유로 "게시물의 원래 의도에서 벗어나기"때문에 귀하의 코드에 대한 편집이 거부되었습니다. 아주 이상한!? 방금 귀하의 코드를 가져와 두 가지 문제를 해결했습니다. 그게 게시물의 의도를 어떻게 바꾸나요!? 어쨌든 ...이 질문에 대한 새로운 답변을 추가했습니다.
HammerNL 2016

귀하의 답변에 좋은 질문 @FakeRainBrigand. 나는 정규식을 위해 모두를 위해, 그 때문에 그것이 작업에 대한 잘못된 도구임을 인정합니다.
niry 2011

2
@niry 여기 내 코드는 끔찍합니다. 나는 지난 6 년 동안 더 나아 졌다고 약속합니다 :-p
Brigand

4

목록에 하나 더 추가하면 위의 모든 것이 충분히 "KISS"가 아니라는 것을 알기 때문입니다.

이것은 정규식을 사용하여 쉼표 또는 줄 바꿈을 찾고 인용 된 항목을 건너 뜁니다. 바라건대 이것은 누비들이 스스로 읽을 수있는 내용입니다. splitFinder정규 표현식은 (바이 분할 않는 세 가지가 있습니다 |)

  1. , -쉼표를 찾습니다.
  2. \r?\n -새 줄을 찾습니다 (수출자가 좋은 경우 캐리지 리턴 포함)
  3. "(\\"|[^"])*?"-쉼표와 줄 바꿈은 중요하지 않기 때문에 따옴표로 묶인 것은 건너 뜁니다. \\"인용 된 항목에 이스케이프 된 인용문 이 있으면 끝 인용문을 찾기 전에 캡처됩니다.

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


fileReader를 통해 내 파일을 읽고 결과 : Id, Name, Age 1, John Smith, 65 2, Jane Doe, 30 지정한 열을 기반으로 구문 분석하려면 어떻게해야합니까?
bluePearl

2d 배열을 얻은 후 첫 번째 인덱스 (prop 이름)를 제거한 다음 나머지 배열을 반복하여 각 값을 속성으로 사용하는 객체를 만듭니다. : 그것은 다음과 같을 것이다[{Id: 1, Name: "John Smith", Age: 65}, {Id: 2, Name: "Jane Doe", Age: 30}]
Seph 리드

3

따옴표 구분 기호를 큰 따옴표로 지정할 수있는 경우 이는 CSV 데이터 구문 분석 을위한 JavaScript 코드 의 복제본입니다 .

먼저 모든 작은 따옴표를 큰 따옴표로 번역 할 수 있습니다.

string = string.replace( /'/g, '"' );

... 또는 큰 따옴표 대신 작은 따옴표를 인식하도록 해당 질문의 정규식을 편집 할 수 있습니다.

// Quoted fields.
"(?:'([^']*(?:''[^']*)*)'|" +

그러나 이것은 귀하의 질문에서 명확하지 않은 특정 마크 업을 가정합니다. 귀하의 질문에 대한 저의 의견에 따라 마크 업의 다양한 가능성이 무엇인지 명확히하십시오.


2

내 대답은 귀하의 입력이 웹 소스의 코드 / 콘텐츠를 반영한 ​​것으로 가정합니다. 여기서 작은 따옴표와 큰 따옴표 문자는 이스케이프되지 않은 일치 집합으로 발생하는 경우 완전히 호환됩니다.

이를 위해 정규식을 사용할 수 없습니다. 분할하려는 문자열을 분석하려면 실제로 마이크로 파서를 작성해야합니다. 이 답변을 위해 문자열에서 인용 된 부분을 하위 문자열이라고 부를 것입니다. 특별히 줄을 가로 질러 걸어야합니다. 다음 경우를 고려하십시오.

var a = "some sample string with \"double quotes\" and 'single quotes' and some craziness like this: \\\" or \\'",
    b = "sample of code from JavaScript with a regex containing a comma /\,/ that should probably be ignored.";

이 경우 문자 패턴에 대한 입력을 분석하여 하위 문자열이 시작되거나 끝나는 위치를 전혀 알 수 없습니다. 대신 따옴표 문자가 따옴표 문자로 사용되는지, 자체적으로 따옴표가 없는지, 따옴표 문자가 이스케이프 뒤에 있지 않은지 여부를 결정하는 논리를 작성해야합니다.

나는 당신을 위해 복잡한 수준의 코드를 작성하지 않을 것이지만, 당신이 필요로하는 패턴을 가진 내가 최근에 작성한 것을 볼 수 있습니다. 이 코드는 쉼표와 관련이 없지만, 그렇지 않으면 코드를 작성할 때 따라갈 수있을만큼 유효한 마이크로 파서입니다. 다음 애플리케이션의 asifix 기능을 살펴보십시오.

https://github.com/austincheney/Pretty-Diff/blob/master/fulljsmin.js


2

이 답변 을 보완하기 위해

다른 따옴표로 이스케이프 된 따옴표를 구문 분석해야하는 경우 예를 들면 다음과 같습니다.

"some ""value"" that is on xlsx file",123

당신이 사용할 수있는

function parse(text) {
  const csvExp = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|"([^""]*(?:"[\S\s][^""]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;

  const values = [];

  text.replace(csvExp, (m0, m1, m2, m3, m4) => {
    if (m1 !== undefined) {
      values.push(m1.replace(/\\'/g, "'"));
    }
    else if (m2 !== undefined) {
      values.push(m2.replace(/\\"/g, '"'));
    }
    else if (m3 !== undefined) {
      values.push(m3.replace(/""/g, '"'));
    }
    else if (m4 !== undefined) {
      values.push(m4);
    }
    return '';
  });

  if (/,\s*$/.test(text)) {
    values.push('');
  }

  return values;
}

나는 이것이 여전히 파싱에 실패한다는 것을 발견했다"jjj "" kkk""","123"
niry

2

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

2

CSV 파일을 문자열로 읽는 동안 문자열 사이에 null 값이 포함되어 있으므로 한 줄씩 \ 0으로 시도하십시오 . 그것은 나를 위해 작동합니다.

stringLine = stringLine.replace(/\0/g, "" );

2

CSV 파일을 구문 분석해야 할 때도 동일한 유형의 문제에 직면했습니다.

파일에는 ','를 포함하는 열 주소가 있습니다.

해당 CSV 파일을 JSON으로 구문 분석 한 후 JSON 파일로 변환하는 동안 키 매핑이 일치하지 않습니다.

baby parsecsvtojson 과 같은 파일 및 라이브러리를 구문 분석하는 데 Node.js 를 사용 했습니다 .

파일 예-

address,pincode
foo,baar , 123456

JSON에서 baby parse를 사용하지 않고 직접 구문 분석하는 동안 다음과 같은 결과를 얻었습니다.

[{
 address: 'foo',
 pincode: 'baar',
 'field3': '123456'
}]

그래서 모든 필드에서 다른 구분 기호로 쉼표 (,)를 제거하는 코드를 작성했습니다.

/*
 csvString(input) = "address, pincode\\nfoo, bar, 123456\\n"
 output = "address, pincode\\nfoo {YOUR DELIMITER} bar, 123455\\n"
*/
const removeComma = function(csvString){
    let delimiter = '|'
    let Baby = require('babyparse')
    let arrRow = Baby.parse(csvString).data;
    /*
      arrRow = [
      [ 'address', 'pincode' ],
      [ 'foo, bar', '123456']
      ]
    */
    return arrRow.map((singleRow, index) => {
        //the data will include
        /*
        singleRow = [ 'address', 'pincode' ]
        */
        return singleRow.map(singleField => {
            //for removing the comma in the feild
            return singleField.split(',').join(delimiter)
        })
    }).reduce((acc, value, key) => {
        acc = acc +(Array.isArray(value) ?
         value.reduce((acc1, val)=> {
            acc1 = acc1+ val + ','
            return acc1
        }, '') : '') + '\n';
        return acc;
    },'')
}

반환 된 함수는 csvtojson 라이브러리에 전달 될 수 있으므로 결과를 사용할 수 있습니다.

const csv = require('csvtojson')

let csvString = "address, pincode\\nfoo, bar, 123456\\n"
let jsonArray = []
modifiedCsvString = removeComma(csvString)
csv()
  .fromString(modifiedCsvString)
  .on('json', json => jsonArray.push(json))
  .on('end', () => {
    /* do any thing with the json Array */
  })

이제 다음과 같은 출력을 얻을 수 있습니다.

[{
  address: 'foo, bar',
  pincode: 123456
}]

1

이 블로그 게시물 에 따르면 이 함수는 다음을 수행해야합니다.

String.prototype.splitCSV = function(sep) {
  for (var foo = this.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
    if (foo[x].replace(/'\s+$/, "'").charAt(foo[x].length - 1) == "'") {
      if ((tl = foo[x].replace(/^\s+'/, "'")).length > 1 && tl.charAt(0) == "'") {
        foo[x] = foo[x].replace(/^\s*'|'\s*$/g, '').replace(/''/g, "'");
      } else if (x) {
        foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
      } else foo = foo.shift().split(sep).concat(foo);
    } else foo[x].replace(/''/g, "'");
  } return foo;
};

다음과 같이 부를 것입니다.

var string = "'string, duppi, du', 23, lala";
var parsed = string.splitCSV();
alert(parsed.join("|"));

이 jsfiddle 종류의 작동하지만 일부 요소 앞에 공백이있는 것처럼 보입니다.


정규식에서 모든 작업을 수행해야한다고 상상해보십시오. 이것이 정규식이 때때로 구문 분석에 적합하지 않은 이유입니다.
CanSpice 2011-12-13

이 솔루션은 단순히 작동하지 않습니다. 원래 테스트 문자열이 주어지면 "'string, duppi, du', 23, lala"이 함수는 다음을 반환합니다.["'string"," duppi"," du'"," 23"," lala"]
ridgerunner

@ridgerunner : 맞아요. 기능을 수정하기 위해 대답과 jsfiddle을 편집했습니다. 기본적으로, 전환 "'"'"' 그 반대의 경우도 마찬가지입니다.
CanSpice 2011-12-13

도움이되었지만 이제이 함수는 큰 따옴표 값이있는 작은 따옴표 CSV 문자열을 잘못 처리합니다. 예를 들어 원래 테스트 문자열의 따옴표 유형을 다음과 같이 바꾸면 다음과 같은 '"string, duppi, du", 23, lala'결과가 발생합니다.['"string',' duppi'.' du"',' 23',' lala']
ridgerunner

@CanSpice, 귀하의 의견은 RegEx를 사용해 보도록 영감을주었습니다. 그다지 많은 기능은 없지만 쉽게 추가 할 수 있습니다. (당신이 관심이 있다면 내 대답은,이 페이지에 있습니다.)
산적

0

구조에 정규 표현식! 이 몇 줄의 코드는 RFC 4180 표준을 기반으로 쉼표, 따옴표 및 줄 바꿈이 포함 된 올바르게 인용 된 필드를 처리합니다.

function parseCsv(data, fieldSep, newLine) {
    fieldSep = fieldSep || ',';
    newLine = newLine || '\n';
    var nSep = '\x1D';
    var qSep = '\x1E';
    var cSep = '\x1F';
    var nSepRe = new RegExp(nSep, 'g');
    var qSepRe = new RegExp(qSep, 'g');
    var cSepRe = new RegExp(cSep, 'g');
    var fieldRe = new RegExp('(?<=(^|[' + fieldSep + '\\n]))"(|[\\s\\S]+?(?<![^"]"))"(?=($|[' + fieldSep + '\\n]))', 'g');
    var grid = [];
    data.replace(/\r/g, '').replace(/\n+$/, '').replace(fieldRe, function(match, p1, p2) {
        return p2.replace(/\n/g, nSep).replace(/""/g, qSep).replace(/,/g, cSep);
    }).split(/\n/).forEach(function(line) {
        var row = line.split(fieldSep).map(function(cell) {
            return cell.replace(nSepRe, newLine).replace(qSepRe, '"').replace(cSepRe, ',');
        });
        grid.push(row);
    });
    return grid;
}

const csv = 'A1,B1,C1\n"A ""2""","B, 2","C\n2"';
const separator = ',';      // field separator, default: ','
const newline = ' <br /> '; // newline representation in case a field contains newlines, default: '\n' 
var grid = parseCsv(csv, separator, newline);
// expected: [ [ 'A1', 'B1', 'C1' ], [ 'A "2"', 'B, 2', 'C <br /> 2' ] ]

다른 곳에서 언급하지 않는 한 유한 상태 머신이 필요하지 않습니다. 정규식은 긍정 룩 비하인드, 부정 룩 비하인드 및 긍정적 인 룩어 헤드 덕분에 RFC 4180을 올바르게 처리합니다.

https://github.com/peterthoeny/parse-csv-js 에서 코드 복제 / 다운로드


0

훌륭하고 완전한 것 외에도 ridgerunner의 훌륭하고 답변 백엔드가 PHP를 실행할 때 매우 간단한 해결 방법을 생각했습니다.

도메인의 백엔드이 PHP 파일을 추가합니다 (예를 들어 : 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")));
?>

이제이 함수를 JavaScript 툴킷에 추가하십시오 (내가 믿는 크로스 브라우저를 만들기 위해 약간 수정해야 함).

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

한 번의 Ajax 호출 비용이 들지만 적어도 코드를 복제하거나 외부 라이브러리를 포함하지 않습니다.

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


0

아래 예제와 같이 papaparse.js 를 사용할 수 있습니다 .

<!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>

동일한 폴더에 papaparse.js를 포함하는 것을 잊지 마십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.