Làm cách nào để tôi có thể phân tích cú pháp một chuỗi CSV bằng Javascript chứa dấu phẩy trong dữ liệu?


93

Tôi có loại chuỗi sau

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

Tôi muốn chia chuỗi thành một mảng trên mỗi dấu phẩy, nhưng chỉ những dấu phẩy bên ngoài dấu ngoặc kép.

Tôi không thể tìm ra regex phù hợp cho sự phân chia ...

string.split(/,/)

sẽ cho tôi

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

nhưng kết quả sẽ là:

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

có giải pháp trình duyệt chéo nào không?


Nó luôn luôn là dấu ngoặc kép? Có bao giờ có một dấu ngoặc kép bên trong một chuỗi được trích dẫn không? Nếu vậy, nó được thoát như thế nào (dấu gạch chéo ngược, dấu gạch chéo ngược)?
Phrogz

Điều gì sẽ xảy ra nếu các ký tự trích dẫn hoàn toàn có thể hoán đổi cho nhau giữa các ký tự trích dẫn kép và đơn như trong mã JavaScript và HTML / XML? Nếu vậy thì điều này yêu cầu một hoạt động phân tích cú pháp rộng hơn CSV.
austincheney

thực sự có, có thể có một câu trích dẫn duy nhất bên trong, thoát bằng dấu gạch chéo ngược sẽ ổn.
Hans

Giá trị có thể là một chuỗi được trích dẫn kép không?
ridgerunner

1
Papa Parse làm rất tốt. Phân tích cú pháp tệp CSV cục bộ bằng JavaScript và Papa Parse: joyofdata.de/blog/…
Raffael

Câu trả lời:


214

Khước từ

Cập nhật 2014-12-01: Câu trả lời bên dưới chỉ hoạt động cho một định dạng rất cụ thể của CSV. Như đã chỉ ra một cách chính xác bởi DG trong các nhận xét, giải pháp này KHÔNG phù hợp với định nghĩa RFC 4180 của CSV và nó cũng KHÔNG phù hợp với định dạng MS Excel. Giải pháp này chỉ đơn giản là minh họa cách người ta có thể phân tích cú pháp một dòng đầu vào CSV (không chuẩn) chứa hỗn hợp các loại chuỗi, trong đó các chuỗi có thể chứa dấu ngoặc kép và dấu phẩy.

Giải pháp CSV không chuẩn

Như austincheney đã chỉ ra một cách chính xác, bạn thực sự cần phải phân tích cú pháp chuỗi từ đầu đến cuối nếu bạn muốn xử lý đúng các chuỗi được trích dẫn có thể chứa các ký tự thoát. Ngoài ra, OP không xác định rõ ràng "chuỗi CSV" thực sự là gì. Trước tiên, chúng ta phải xác định những gì tạo thành một chuỗi CSV hợp lệ và các giá trị riêng lẻ của nó.

Đưa ra: Định nghĩa "Chuỗi CSV"

Đối với mục đích của cuộc thảo luận này, "chuỗi CSV" bao gồm 0 hoặc nhiều giá trị, trong đó nhiều giá trị được phân tách bằng dấu phẩy. Mỗi giá trị có thể bao gồm:

  1. Một chuỗi được trích dẫn kép. (có thể chứa các dấu nháy đơn không thoát.)
  2. Một chuỗi trích dẫn duy nhất. (có thể chứa dấu ngoặc kép không thoát.)
  3. Một chuỗi không được trích dẫn. (KHÔNG được chứa dấu ngoặc kép, dấu phẩy hoặc dấu gạch chéo ngược.)
  4. Một giá trị trống. (Một giá trị tất cả khoảng trắng được coi là trống.)

Quy tắc / Ghi chú:

  • Giá trị được trích dẫn có thể chứa dấu phẩy.
  • Giá trị niêm yết có thể chứa thoát-bất cứ điều gì, ví dụ 'that\'s cool'.
  • Các giá trị chứa dấu ngoặc kép, dấu phẩy hoặc dấu gạch chéo ngược phải được trích dẫn.
  • Các giá trị chứa khoảng trắng đầu hoặc cuối phải được trích dẫn.
  • Dấu gạch chéo ngược bị xóa khỏi tất cả: \'trong các giá trị được trích dẫn đơn lẻ.
  • Dấu gạch chéo ngược bị xóa khỏi tất cả: \"trong các giá trị được trích dẫn kép.
  • Các chuỗi không được trích dẫn được cắt bỏ mọi khoảng trống ở đầu và cuối.
  • Dấu phân cách bằng dấu phẩy có thể có khoảng trắng liền kề (bị bỏ qua).

Tìm thấy:

Một hàm JavaScript chuyển đổi một chuỗi CSV hợp lệ (như được định nghĩa ở trên) thành một mảng các giá trị chuỗi.

Giải pháp:

Các biểu thức chính quy được sử dụng bởi giải pháp này rất phức tạp. Và (IMHO) tất cả các regex không tầm thường nên được trình bày ở chế độ khoảng cách tự do với nhiều chú thích và thụt lề. Thật không may, JavaScript không cho phép chế độ giãn cách tự do. Do đó, các biểu thức chính quy được triển khai bởi giải pháp này lần đầu tiên được trình bày trong cú pháp regex gốc (được thể hiện bằng cách sử dụng tiện dụng của Python:r'''...''' cú pháp chuỗi thô-đa-dòng-chuỗi ).

Đầu tiên, đây là một biểu thức chính quy xác nhận rằng một chuỗi CVS đáp ứng các yêu cầu trên:

Regex để xác thực "chuỗi 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.
"""

Nếu một chuỗi khớp với regex ở trên, thì chuỗi đó là một chuỗi CSV hợp lệ (theo các quy tắc đã nêu trước đó) và có thể được phân tích cú pháp bằng cách sử dụng regex sau. Sau đó, regex sau được sử dụng để khớp với một giá trị từ chuỗi CSV. Nó được áp dụng lặp đi lặp lại cho đến khi không tìm thấy kết quả phù hợp nào nữa (và tất cả các giá trị đã được phân tích cú pháp).

Regex để phân tích cú pháp một giá trị từ chuỗi CSV hợp lệ:

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

Lưu ý rằng có một giá trị trường hợp đặc biệt mà regex này không khớp - giá trị cuối cùng khi giá trị đó trống. Trường hợp "giá trị cuối cùng trống" đặc biệt này được kiểm tra và xử lý bởi hàm js theo sau.

Hàm JavaScript để phân tích cú pháp chuỗi CSV:

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

Ví dụ đầu vào và đầu ra:

Trong các ví dụ sau, dấu ngoặc nhọn được sử dụng để phân cách {result strings}. (Điều này là để giúp hình dung các khoảng trống ở đầu / cuối và các chuỗi có độ dài bằng không.)

// 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] = {} */

Ghi chú bổ sung:

Giải pháp này yêu cầu chuỗi CSV phải "hợp lệ". Ví dụ: các giá trị không được trích dẫn có thể không chứa dấu gạch chéo ngược hoặc dấu ngoặc kép, ví dụ: chuỗi CSV sau KHÔNG hợp lệ:

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

Đây không thực sự là một giới hạn vì bất kỳ chuỗi con nào cũng có thể được biểu diễn dưới dạng giá trị trích dẫn đơn hoặc kép. Cũng lưu ý rằng giải pháp này chỉ đại diện cho một định nghĩa khả thi cho: "Các giá trị được phân tách bằng dấu phẩy".

Chỉnh sửa: 2014-05-19: Đã thêm tuyên bố từ chối trách nhiệm. Chỉnh sửa: 2014-12-01: Đã chuyển tuyên bố từ chối trách nhiệm lên đầu.


1
@Evan Plaice - Cảm ơn vì những lời tốt đẹp. Chắc chắn bạn có thể sử dụng bất kỳ dấu phân tách nào. Chỉ cần thay thế mọi dấu phẩy trong regex của tôi bằng dấu phân cách được lựa chọn (nhưng dấu phân cách không được là khoảng trắng). Chúc mừng.
ridgerunner

2
@Evan Plaice - Bạn có thể sử dụng bất kỳ regexes nào của tôi cho bất kỳ mục đích nào bạn muốn. Một ghi chú công nhận sẽ tốt nhưng không cần thiết. Chúc may mắn với plugin của bạn. Chúc mừng!
ridgerunner

1
Tuyệt vời, đây là mã dự án.google.com/p/jquery-csv . Cuối cùng, tôi muốn thêm một định dạng tiện ích mở rộng vào CSV được gọi là SSV (Giá trị được phân tách có cấu trúc), chỉ đơn giản là CSV có bao gồm siêu dữ liệu (tức là dấu phân cách, dấu phân tách, kết thúc dòng, v.v.).
Evan Plaice

1
Cảm ơn rất nhiều vì sự triển khai tuyệt vời này - tôi đã sử dụng nó làm cơ sở cho mô-đun Node.js ( csv-iterator ).
mirkokiefer

3
Tôi hoan nghênh sự chi tiết và rõ ràng của câu trả lời của bạn, nhưng cần lưu ý ở đâu đó rằng định nghĩa của bạn về CSV không phù hợp với RFC 4180, đây là điều kết thúc tiêu chuẩn cho CSV và tôi có thể nói rằng nó thường được sử dụng. Đặc biệt, đây sẽ là cách bình thường để "thoát" ký tự dấu ngoặc kép trong trường chuỗi: "field one", "field two", "a ""final"" field containing two double quote marks"Tôi chưa thử nghiệm câu trả lời của Trevor Dixon trên trang này, nhưng đó là câu trả lời đề cập đến định nghĩa RFC 4180 của CSV.
DG.

53

Giải pháp RFC 4180

Điều này không giải quyết được chuỗi trong câu hỏi vì định dạng của nó không phù hợp với RFC 4180; mã hóa được chấp nhận đang thoát khỏi dấu ngoặc kép với dấu ngoặc kép. Giải pháp bên dưới hoạt động chính xác với các tệp CSV d / l từ bảng tính google.

CẬP NHẬT (3/2017)

Phân tích cú pháp một dòng sẽ là sai. Theo RFC 4180, các trường có thể chứa CRLF sẽ khiến bất kỳ trình đọc dòng nào phá vỡ tệp CSV. Đây là phiên bản cập nhật phân tích cú pháp chuỗi 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));

CÂU TRẢ LỜI CŨ

(Giải pháp dòng đơn)

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

Và để giải trí, đây là cách bạn tạo CSV từ mảng:

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
cái này đã làm công việc cho tôi, không phải cái khác
WtFudgE

7

Ngữ pháp PEG (.js) xử lý các ví dụ RFC 4180 tại http://en.wikipedia.org/wiki/Comma-separated_values :

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 '"'; }
  / [^"]

Kiểm tra tại http://jsfiddle.net/knvzk/10 hoặc https://pegjs.org/online .

Tải xuống trình phân tích cú pháp đã tạo tại https://gist.github.com/3362830 .


6

Tôi có một trường hợp sử dụng rất cụ thể mà tôi muốn sao chép các ô từ Google Trang tính vào ứng dụng web của mình. Các ô có thể bao gồm dấu ngoặc kép và ký tự dòng mới. Sử dụng tính năng sao chép và dán, các ô được phân tách bằng ký tự tab và các ô có dữ liệu lẻ được trích dẫn kép. Tôi đã thử giải pháp chính này, bài viết được liên kết sử dụng regexp, Jquery-CSV và CSVToArray. http://papaparse.com/ Là trang duy nhất hoạt động hiệu quả. Sao chép và dán liền mạch với Google Trang tính với các tùy chọn tự động phát hiện mặc định.


1
Điều này sẽ được xếp hạng cao hơn nhiều, đừng bao giờ cố gắng cuộn trình phân tích cú pháp CSV của riêng bạn, nó sẽ không hoạt động chính xác - đặc biệt là khi sử dụng regexes. Papaparse thật tuyệt vời - hãy sử dụng nó!
cbley

6

Tôi thích câu trả lời của FakeRainBrigand, tuy nhiên nó có một số vấn đề: Nó không thể xử lý khoảng trắng giữa dấu ngoặc kép và dấu phẩy và không hỗ trợ 2 dấu phẩy liên tiếp. Tôi đã thử chỉnh sửa câu trả lời của anh ấy nhưng chỉnh sửa của tôi đã bị từ chối bởi những người đánh giá dường như không hiểu mã của tôi. Đây là phiên bản mã của FakeRainBrigand của tôi. Ngoài ra còn có một fiddle: 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

Mọi người dường như chống lại RegEx vì điều này. Tại sao?

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

Đây là mã. Tôi cũng làm một trò đùa .

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
Rất tiếc, regexp của bạn có một số vấn đề: Nó không thể xử lý khoảng trắng giữa dấu ngoặc kép và dấu phẩy và không hỗ trợ 2 dấu phẩy liên tiếp. Tôi đã cập nhật câu trả lời của bạn với mã khắc phục cả hai vấn đề và tạo một fiddle mới: jsfiddle.net/xTezm/43
HammerNL

Vì một số lý do, chỉnh sửa của tôi đối với mã của bạn đã bị từ chối vì nó sẽ "đi chệch khỏi ý định ban đầu của bài đăng". Rất lạ!? Tôi vừa lấy mã của bạn và khắc phục hai sự cố với nó. Làm thế nào mà điều đó thay đổi ý định của bài viết !? Dù sao ... tôi chỉ đơn giản là thêm một câu trả lời mới cho câu hỏi này.
HammerNL

Câu trả lời hay trong câu trả lời của bạn, @FakeRainBrigand. Tôi vì regex, và vì điều đó, tôi thừa nhận rằng đó là công cụ sai lầm cho công việc.
niry

2
@niry mã của tôi ở đây là khủng khiếp. Tôi hứa tôi đã nhận được tốt hơn trong 6 năm qua :-p
Brigand

4

Thêm một cái nữa vào danh sách, bởi vì tôi thấy tất cả những điều trên vẫn chưa đủ "KISS".

Cái này sử dụng regex để tìm dấu phẩy hoặc dòng mới trong khi bỏ qua các mục được trích dẫn. Hy vọng rằng đây là một cái gì đó mà noobies có thể tự đọc qua. Các splitFinderregexp có ba điều nó (chia bởi một |):

  1. , - tìm dấu phẩy
  2. \r?\n - tìm các dòng mới, (có khả năng có ký tự xuống dòng nếu nhà xuất khẩu tốt)
  3. "(\\"|[^"])*?"- bỏ qua bất kỳ phần nào được bao quanh trong dấu ngoặc kép, vì dấu phẩy và dòng mới không quan trọng trong đó. Nếu có một trích dẫn thoát \\"trong mục được trích dẫn, nó sẽ được ghi lại trước khi có thể tìm thấy trích dẫn kết thúc.

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


Nếu tôi đọc tệp của mình qua fileReader và kết quả của tôi: Id, Name, Age 1, John Smith, 65 2, Jane Doe, 30 làm cách nào tôi có thể phân tích cú pháp dựa trên các cột mà tôi chỉ định?
bluePearl

Sau khi bạn nhận được mảng 2d, hãy xóa chỉ mục đầu tiên (đó là những tên hỗ trợ của bạn), sau đó lặp lại phần còn lại của mảng, tạo các đối tượng với mỗi giá trị dưới dạng thuộc tính. Nó sẽ giống như thế này:[{Id: 1, Name: "John Smith", Age: 65}, {Id: 2, Name: "Jane Doe", Age: 30}]
Seph Reed

3

Nếu bạn có thể đặt dấu phân cách trong dấu ngoặc kép, thì đây là bản sao của Mã JavaScript để phân tích cú pháp dữ liệu CSV .

Trước tiên, bạn có thể dịch tất cả các dấu ngoặc đơn sang dấu ngoặc kép:

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

... hoặc bạn có thể chỉnh sửa regex trong câu hỏi đó để nhận ra dấu ngoặc đơn thay vì dấu ngoặc kép:

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

Tuy nhiên, điều này giả định đánh dấu nhất định không rõ ràng từ câu hỏi của bạn. Vui lòng làm rõ tất cả các khả năng khác nhau của đánh dấu có thể là gì, theo nhận xét của tôi về câu hỏi của bạn.


2

Câu trả lời của tôi giả định thông tin đầu vào của bạn là sự phản ánh mã / nội dung từ các nguồn web nơi các ký tự trích dẫn đơn và kép hoàn toàn có thể hoán đổi cho nhau miễn là chúng xuất hiện dưới dạng một tập hợp đối sánh không thoát.

Bạn không thể sử dụng regex cho việc này. Bạn thực sự phải viết một trình phân tích cú pháp vi mô để phân tích chuỗi mà bạn muốn tách. Vì lợi ích của câu trả lời này, tôi sẽ gọi các phần được trích dẫn trong chuỗi của bạn là chuỗi con. Bạn cần phải đặc biệt đi qua chuỗi. Hãy xem xét trường hợp sau:

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.";

Trong trường hợp này, bạn hoàn toàn không biết chuỗi con bắt đầu hay kết thúc ở đâu bằng cách đơn giản phân tích đầu vào cho một mẫu ký tự. Thay vào đó, bạn phải viết logic để đưa ra quyết định xem một ký tự trích dẫn có được sử dụng một ký tự trích dẫn hay không, bản thân nó có được trích dẫn không và ký tự trích dẫn đó không theo sau một lối thoát.

Tôi sẽ không viết mã mức độ phức tạp đó cho bạn, nhưng bạn có thể xem một thứ mà tôi đã viết gần đây có mẫu mà bạn cần. Mã này không liên quan gì đến dấu phẩy, nhưng ngược lại nó là một trình phân tích cú pháp vi mô đủ hợp lệ để bạn viết mã của riêng mình. Xem xét chức năng asifix của ứng dụng sau:

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


2

Trong khi đọc csv thành chuỗi, nó chứa giá trị null ở giữa chuỗi, vì vậy hãy thử nó \ 0 Từng dòng một, nó hoạt động với tôi.

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

2

Để bổ sung câu trả lời này

Nếu bạn cần phân tích cú pháp các dấu ngoặc kép đã thoát với một trích dẫn khác, ví dụ:

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

Bạn có thể dùng

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

Tôi thấy rằng điều này vẫn không phân tích được"jjj "" kkk""","123"
niry

2

Tôi cũng đã gặp phải một loại vấn đề tương tự khi phải phân tích cú pháp Tệp CSV. Tệp chứa Địa chỉ cột chứa ','.
Sau khi phân tích cú pháp CSV đó thành JSON, tôi nhận được ánh xạ không khớp của các khóa trong khi chuyển đổi nó thành Tệp JSON.
Tôi đã sử dụng nút để phân tích cú pháp tệp và Thư viện như phân tích cú pháp concsvtojson
Ví dụ về tệp -

address,pincode
foo,baar , 123456

Trong khi tôi phân tích cú pháp trực tiếp mà không sử dụng phân tích cú pháp con trong JSON, tôi đã nhận được

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

Vì vậy, tôi đã viết một mã loại bỏ dấu phẩy (,) với bất kỳ dấu phân cách nào khác với mọi trường

/*
 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;
    },'')
}

Hàm trả về có thể được chuyển vào thư viện csvtojson và do đó kết quả có thể được sử dụng.

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 */
  })
Bây giờ bạn có thể nhận được đầu ra như

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

2

không có regexp, có thể đọc được, theo 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;
}

1

Theo bài đăng trên blog này , chức năng này sẽ làm điều đó:

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

Bạn sẽ gọi nó như vậy:

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

Loại jsfiddle này hoạt động, nhưng có vẻ như một số phần tử có khoảng trắng trước chúng.


Hãy tưởng tượng bạn phải làm tất cả những điều đó trong một regex. Đây là lý do tại sao regexes đôi khi không thực sự thích hợp để phân tích cú pháp.
CanSpice

Giải pháp này chỉ đơn giản là không hoạt động. Với chuỗi thử nghiệm ban đầu: "'string, duppi, du', 23, lala", điều này trở về chức năng:["'string"," duppi"," du'"," 23"," lala"]
ridgerunner

@ridgerunner: Đúng vậy bạn. Tôi đã chỉnh sửa câu trả lời và jsfiddle để sửa chức năng. Về cơ bản, tôi chuyển "'"đến '"'và ngược lại.
CanSpice

Điều đó đã giúp, nhưng bây giờ hàm xử lý không chính xác các chuỗi CSV được trích dẫn đơn có giá trị được trích dẫn kép. ví dụ như Đảo ngược các loại quote của chuỗi thử nghiệm ban đầu như sau: '"string, duppi, du", 23, lala'kết quả trong:['"string',' duppi'.' du"',' 23',' lala']
ridgerunner

@CanSpice, nhận xét của bạn đã truyền cảm hứng cho tôi để thử với RegEx. Nó không có nhiều tính năng, nhưng chúng có thể dễ dàng thêm vào. (Câu trả lời của tôi là trên trang này, nếu bạn quan tâm.)
Brigand

0

Ngoài câu trả lời tuyệt vời và đầy đủ từ ridgerunner, tôi đã nghĩ ra một cách giải quyết rất đơn giản khi chương trình phụ trợ của bạn chạy php.

Thêm tệp php này vào chương trình phụ trợ của miền của bạn (giả sử 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")));
?>

Bây giờ hãy thêm chức năng này vào bộ công cụ javascript của bạn (nên sửa đổi một chút để tạo trình duyệt chéo mà tôi tin tưởng.)

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

Bạn sẽ mất 1 cuộc gọi ajax, nhưng ít nhất bạn sẽ không trùng lặp mã cũng như không bao gồm bất kỳ thư viện bên ngoài nào.

Tham khảo: http://php.net/manual/en/ Chức năng.str-getcsv.php


0

Bạn có thể sử dụng papaparse.js như ví dụ dưới đây:

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

Đừng quên bao gồm papaparse.js trong cùng một thư mục.


0

Biểu thức chính quy để giải cứu! Một vài dòng mã này xử lý các trường được trích dẫn đúng cách có nhúng dấu phẩy, dấu ngoặc kép và dòng mới dựa trên tiêu chuẩn 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' ] ]

Trừ khi được nêu ở nơi khác, bạn không cần một máy trạng thái hữu hạn. Biểu thức chính quy xử lý RFC 4180 đúng cách nhờ cái nhìn tích cực, cái nhìn tiêu cực và cái nhìn tích cực.

Sao chép / tải xuống mã tại https://github.com/peterthoeny/parse-csv-js

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.