Bagaimana cara mengurai string CSV dengan JavaScript, yang berisi koma dalam data?

93

Saya memiliki jenis string berikut

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

Saya ingin membagi string menjadi array pada setiap koma, tetapi hanya koma di luar tanda kutip tunggal.

Saya tidak tahu ekspresi reguler yang tepat untuk perpecahan ...

string.split(/,/)

akan memberi saya

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

tetapi hasilnya harus:

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

Apakah ada solusi lintas-browser?

Hans
sumber
Apakah selalu tanda kutip tunggal? Apakah pernah ada kutipan tunggal di dalam string yang dikutip? Jika demikian, bagaimana cara lolos (garis miring terbalik, digandakan)?
Phrogz
Bagaimana jika karakter kutipan benar-benar dapat dipertukarkan antara karakter tanda kutip ganda dan tunggal seperti dalam JavaScript dan kode HTML / XML? Jika demikian maka ini membutuhkan operasi penguraian yang lebih ekstensif daripada CSV.
austincheney
sebenarnya ya, mungkin ada satu kutipan di dalamnya, melarikan diri dengan garis miring terbalik akan baik-baik saja.
Hans
Bisakah nilai menjadi string yang dikutip ganda?
ridgerunner
1
Papa Parse melakukan pekerjaan dengan baik. Parsing File CSV Lokal dengan JavaScript dan Papa Parse: joyofdata.de/blog/…
Raffael

Jawaban:

215

Penolakan

Pembaruan 2014-12-01: Jawaban di bawah ini hanya berfungsi untuk satu format CSV yang sangat spesifik. Seperti yang ditunjukkan dengan benar oleh DG di komentar , solusi ini tidak sesuai dengan definisi CSV RFC 4180 dan juga tidak sesuai dengan format Microsoft Excel. Solusi ini hanya mendemonstrasikan bagaimana seseorang dapat mengurai satu baris input CSV (non-standar) yang berisi campuran jenis string, di mana string mungkin berisi tanda kutip dan koma yang lolos.

Solusi CSV non-standar

Seperti yang ditunjukkan austincheney dengan benar , Anda benar-benar perlu mengurai string dari awal hingga akhir jika Anda ingin menangani string yang dikutip dengan benar yang mungkin berisi karakter yang lolos. Selain itu, OP tidak secara jelas mendefinisikan apa itu "string CSV" sebenarnya. Pertama kita harus menentukan apa yang merupakan string CSV yang valid dan nilai individualnya.

Diberikan: Definisi "CSV String"

Untuk tujuan diskusi ini, "string CSV" terdiri dari nol atau lebih nilai, di mana beberapa nilai dipisahkan oleh koma. Setiap nilai dapat terdiri dari:

  1. String kutip ganda (mungkin berisi tanda kutip tunggal yang tidak di-escape).
  2. String kutipan tunggal (mungkin berisi tanda kutip ganda yang tidak di-escape).
  3. String yang tidak dikutip ( tidak boleh berisi tanda kutip, koma, atau garis miring terbalik).
  4. Nilai kosong. (Nilai semua spasi kosong dianggap kosong.)

Aturan / Catatan:

  • Nilai yang dikutip mungkin berisi koma.
  • Nilai yang dikutip dapat berisi apa pun yang lolos, mis 'that\'s cool'.
  • Nilai yang mengandung kutipan, koma, atau garis miring terbalik harus dikutip.
  • Nilai yang mengandung spasi kosong di depan atau di belakang harus dikutip.
  • Garis miring terbalik dihapus dari semua: \'dalam nilai kutip tunggal.
  • Garis miring terbalik dihapus dari semua: \"dalam nilai kutip ganda.
  • String yang tidak dikutip akan dipangkas dari spasi di depan dan di belakangnya.
  • Pemisah koma mungkin memiliki spasi yang berdekatan (yang diabaikan).

Temukan:

Fungsi JavaScript yang mengubah string CSV yang valid (seperti yang didefinisikan di atas) menjadi larik nilai string.

Larutan:

Ekspresi reguler yang digunakan oleh solusi ini kompleks. Dan (IMHO) semua ekspresi reguler non-sepele harus disajikan dalam mode spasi bebas dengan banyak komentar dan lekukan. Sayangnya, JavaScript tidak mengizinkan mode spasi bebas. Dengan demikian, ekspresi reguler yang diterapkan oleh solusi ini pertama kali disajikan dalam sintaks ekspresi reguler asli (diekspresikan menggunakan handy Pythonr'''...''' sintaks string mentah-multi-baris ).

Pertama di sini adalah ekspresi reguler yang memvalidasi bahwa string CVS memenuhi persyaratan di atas:

Ekspresi reguler untuk memvalidasi "string 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.
"""

Jika sebuah string cocok dengan ekspresi reguler di atas, string tersebut adalah string CSV yang valid (sesuai dengan aturan yang dinyatakan sebelumnya) dan dapat diurai menggunakan ekspresi reguler berikut. Ekspresi reguler berikut kemudian digunakan untuk mencocokkan satu nilai dari string CSV. Ini diterapkan berulang kali hingga tidak ada lagi kecocokan yang ditemukan (dan semua nilai telah diuraikan).

Ekspresi reguler untuk mengurai satu nilai dari string CSV yang valid:

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

Perhatikan bahwa ada satu nilai kasus khusus yang tidak cocok dengan ekspresi reguler ini - nilai terakhir bila nilai itu kosong. Kasus khusus "nilai terakhir kosong" ini diuji dan ditangani oleh fungsi JavaScript yang mengikuti.

Fungsi JavaScript untuk mengurai string 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;
};

Contoh masukan dan keluaran:

Dalam contoh berikut, kurung kurawal digunakan untuk membatasi {result strings}. (Ini untuk membantu memvisualisasikan spasi depan / belakang dan string panjang-nol.)

// Test 1: Test string from original question.
var test = "'string, duppi, du', 23, lala";
var a = CSVtoArray(test);
/* Array has three elements:
    a[0] = {string, duppi, du}
    a[1] = {23}
    a[2] = {lala} */
// Test 2: Empty CSV string.
var test = "";
var a = CSVtoArray(test);
/* Array has zero elements: */
// Test 3: CSV string with two empty values.
var test = ",";
var a = CSVtoArray(test);
/* Array has two 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 has three 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 has three 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 has eight elements:
    a[0] = {one}
    a[1] = {two}
    a[2] = {}
    a[3] = { four}
    a[4] = {}
    a[5] = {six }
    a[6] = { seven }
    a[7] = {} */

Catatan tambahan:

Solusi ini mengharuskan string CSV menjadi "valid". Misalnya, nilai tanpa tanda kutip tidak boleh berisi garis miring terbalik atau tanda kutip, misalnya string CSV berikut tidak valid:

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

Ini sebenarnya bukan batasan karena setiap sub-string dapat direpresentasikan sebagai nilai kutip tunggal atau ganda. Perhatikan juga bahwa solusi ini hanya mewakili satu kemungkinan definisi untuk "nilai yang dipisahkan koma".

Edit riwayat

  • 2014-05-19: Penafian tambahan.
  • 2014-12-01: Penafian dipindahkan ke atas.
ridgerunner
sumber
1
@ Evan Plaice - Terima kasih atas kata-kata yang bagus. Tentu Anda dapat menggunakan pemisah apa pun. Cukup ganti setiap koma di regex saya dengan pemisah pilihan (tetapi pemisah tidak boleh spasi). Bersulang.
ridgerunner
2
@ Evan Plaice - Anda dipersilakan untuk menggunakan regex saya untuk tujuan apa pun yang Anda inginkan. Sebuah catatan pengakuan akan menyenangkan tetapi tidak perlu. Semoga berhasil dengan plugin Anda. Bersulang!
ridgerunner
1
Keren, inilah project code.google.com/p/jquery-csv . Akhirnya, saya ingin menambahkan format ekstensi ke CSV yang disebut SSV (Structured Separated Values) yang hanya CSV dengan metadata (yaitu, pembatas, pemisah, akhir baris, dll) disertakan.
Evan Plaice
1
Terima kasih banyak untuk implementasi yang hebat ini - Saya menggunakannya sebagai dasar untuk modul Node.js ( csv-iterator ).
mirkokiefer
3
Saya memuji detail dan mengklarifikasi jawaban Anda, tetapi perlu dicatat di suatu tempat bahwa definisi CSV Anda tidak sesuai dengan RFC 4180 yang merupakan hal yang paling dekat dengan standar untuk CSV, dan yang dapat saya katakan secara anekdot biasanya digunakan. Secara khusus ini akan menjadi cara normal untuk "melarikan diri" karakter kutip ganda dalam bidang string: "field one", "field two", "a ""final"" field containing two double quote marks"Saya belum menguji jawaban Trevor Dixon di halaman ini, tetapi ini adalah jawaban yang membahas definisi CSV RFC 4180.
DG.
53

Solusi RFC 4180

Ini tidak menyelesaikan string dalam pertanyaan karena formatnya tidak sesuai dengan RFC 4180; pengkodean yang dapat diterima keluar dari tanda kutip ganda dengan tanda kutip ganda. Solusi di bawah ini berfungsi dengan benar dengan file CSV d / l dari spreadsheet google.

PEMBARUAN (3/2017)

Mengurai satu baris akan salah. Menurut bidang RFC 4180 mungkin berisi CRLF yang akan menyebabkan pembaca baris apa pun merusak file CSV. Berikut adalah versi terbaru yang mengurai string 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));

JAWABAN LAMA

(Solusi garis tunggal)

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

Dan untuk bersenang-senang, berikut adalah cara Anda membuat CSV dari array:

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

niry
sumber
1
yang satu ini melakukan pekerjaan untuk saya, bukan yang lain
WtFudgE
7

Tata bahasa PEG (.js) yang menangani contoh RFC 4180 di 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 '"'; }
  / [^"]

Uji di http://jsfiddle.net/knvzk/10 atau https://pegjs.org/online .

Unduh parser yang dibuat di https://gist.github.com/3362830 .

Trevor Dixon
sumber
6

Saya memiliki kasus penggunaan yang sangat spesifik di mana saya ingin menyalin sel dari Google Sheets ke aplikasi web saya. Sel dapat menyertakan tanda kutip ganda dan karakter baris baru. Menggunakan salin dan tempel, sel dibatasi oleh karakter tab, dan sel dengan data ganjil dikutip ganda. Saya mencoba solusi utama ini, artikel tertaut menggunakan regexp, dan Jquery-CSV, dan CSVToArray. http://papaparse.com/ Adalah satu-satunya yang berhasil di luar kotak. Salin dan tempel mulus dengan Google Sheets dengan opsi deteksi otomatis default.

bjcullinan.dll
sumber
1
Ini harus memiliki peringkat yang jauh lebih tinggi, jangan pernah mencoba menggulung pengurai CSV Anda sendiri, ini tidak akan berfungsi dengan benar - terutama saat menggunakan regex. Papaparse luar biasa - gunakanlah!
cbley
6

Saya menyukai jawaban FakeRainBrigand, namun berisi beberapa masalah: Jawaban ini tidak dapat menangani spasi kosong antara kutipan dan koma, dan tidak mendukung 2 koma yang berurutan. Saya mencoba mengedit jawabannya tetapi suntingan saya ditolak oleh pengulas yang tampaknya tidak memahami kode saya. Ini adalah versi kode FakeRainBrigand saya. Ada juga biola: 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('|'));
HammerNL
sumber
4

Orang-orang sepertinya menentang RegEx untuk ini. Mengapa?

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

Ini kodenya. Saya juga membuat biola .

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('|'));
Perampok
sumber
3
Hmm, regexp Anda memang memiliki beberapa masalah: regexp Anda tidak dapat menangani spasi antara kutipan dan koma, dan tidak mendukung 2 koma yang berurutan. Saya telah memperbarui jawaban Anda dengan kode yang memperbaiki kedua masalah dan membuat biola baru: jsfiddle.net/xTezm/43
HammerNL
Untuk beberapa alasan pengeditan saya pada kode Anda ditolak karena akan "menyimpang dari maksud asli posting". Sangat aneh!? Saya baru saja mengambil kode Anda dan memperbaiki dua masalah dengannya. Bagaimana itu bisa mengubah maksud dari posting tersebut !? Bagaimanapun ... Saya hanya menambahkan jawaban baru untuk pertanyaan ini.
HammerNL
Pertanyaan bagus dalam jawaban Anda, @FakeRainBrigand. Saya untuk satu semua untuk regex, dan karena itu, saya mengakui bahwa itu adalah alat yang salah untuk pekerjaan itu.
niry
2
@niry kode saya di sini mengerikan. Saya berjanji saya telah menjadi lebih baik selama 6 tahun terakhir :-p
Brigand
4

Menambahkan satu lagi ke daftar, karena menurut saya semua hal di atas tidak cukup "KISS".

Yang ini menggunakan regex untuk menemukan koma atau baris baru sambil melewati item yang dikutip. Semoga ini adalah sesuatu yang bisa dibaca oleh noobies sendiri. The splitFinderregexp memiliki tiga hal itu tidak (split oleh |):

  1. , - menemukan koma
  2. \r?\n - menemukan jalur baru, (berpotensi dengan carriage return jika eksportir bersikap baik)
  3. "(\\"|[^"])*?"- melewatkan apa pun yang diapit tanda kutip, karena koma dan baris baru tidak penting di sana. Jika ada kutipan yang lolos \\"dalam item yang dikutip, itu akan ditangkap sebelum kutipan akhir dapat ditemukan.

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

Seph Reed
sumber
Jika saya membaca file saya melalui fileReader dan hasilnya: Id, Name, Age 1, John Smith, 65 2, Jane Doe, 30 bagaimana saya bisa mengurai berdasarkan kolom yang saya tentukan?
bluePearl
Setelah Anda mendapatkan array 2d, hapus indeks pertama (itu adalah nama prop Anda), lalu ulangi sisa array, buat objek dengan masing-masing nilai sebagai properti. Ini akan terlihat seperti ini:[{Id: 1, Name: "John Smith", Age: 65}, {Id: 2, Name: "Jane Doe", Age: 30}]
Seph Reed
3

Jika Anda dapat membuat pembatas kutipan menjadi tanda kutip ganda, maka ini adalah duplikat kode Contoh JavaScript untuk mengurai data CSV .

Anda dapat menerjemahkan semua tanda kutip tunggal menjadi tanda kutip ganda terlebih dahulu:

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

... atau Anda dapat mengedit ekspresi reguler dalam pertanyaan itu untuk mengenali tanda kutip tunggal, bukan tanda kutip ganda:

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

Namun, ini mengasumsikan markup tertentu yang tidak jelas dari pertanyaan Anda. Harap klarifikasi tentang berbagai kemungkinan markup, sesuai komentar saya atas pertanyaan Anda.

Phrogz
sumber
2

Jawaban saya menganggap masukan Anda adalah cerminan kode / konten dari sumber web di mana karakter tanda kutip tunggal dan ganda sepenuhnya dapat dipertukarkan asalkan terjadi sebagai kumpulan pencocokan yang tidak lolos.

Anda tidak dapat menggunakan ekspresi reguler untuk ini. Anda sebenarnya harus menulis parser mikro untuk menganalisis string yang ingin Anda pisahkan. Demi jawaban ini, saya akan menyebut bagian yang dikutip dari string Anda sebagai sub-string. Anda harus berjalan melintasi tali secara khusus. Pertimbangkan kasus berikut:

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

Dalam hal ini Anda sama sekali tidak tahu di mana sub-string dimulai atau diakhiri hanya dengan menganalisis input untuk pola karakter. Sebaliknya, Anda harus menulis logika untuk membuat keputusan tentang apakah karakter kutipan digunakan karakter kutipan, tidak dikutip, dan karakter kutipan tidak mengikuti pelarian.

Saya tidak akan menulis tingkat kerumitan kode itu untuk Anda, tetapi Anda dapat melihat sesuatu yang baru-baru ini saya tulis yang memiliki pola yang Anda butuhkan. Kode ini tidak ada hubungannya dengan koma, tetapi merupakan micro-parser yang cukup valid untuk Anda ikuti saat menulis kode Anda sendiri. Perhatikan fungsi asifix dari aplikasi berikut:

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

austincheney.dll
sumber
2

Untuk melengkapi jawaban ini

Jika Anda perlu mengurai kutipan yang lolos dengan kutipan lain, contoh:

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

Kamu bisa memakai

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;
}
BrunoLM
sumber
Saya menemukan bahwa penguraian ini masih gagal"jjj "" kkk""","123"
pada
2

Saat membaca file CSV menjadi string, file ini berisi nilai null di antara string, jadi cobalah dengan \ 0 baris demi baris. Ini bekerja untuk saya.

stringLine = stringLine.replace(/\0/g, "" );
Sharathi RB
sumber
2

Saya juga menghadapi masalah yang sama ketika saya harus mengurai file CSV.

File tersebut berisi alamat kolom yang berisi ','.

Setelah mem-parsing file CSV itu ke JSON, saya mendapatkan pemetaan kunci yang tidak cocok saat mengonversinya menjadi file JSON.

Saya menggunakan Node.js untuk mem -parsing file dan perpustakaan seperti baby parse dan csvtojson .

Contoh file -

address,pincode
foo,baar , 123456

Saat saya mem-parsing secara langsung tanpa menggunakan baby parse di JSON, saya mendapatkan:

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

Jadi saya menulis kode yang menghapus koma (,) dengan pembatas lain dengan setiap bidang:

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

Fungsi yang dikembalikan bisa diteruskan ke pustaka csvtojson dan dengan demikian hasilnya bisa digunakan.

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

Sekarang Anda bisa mendapatkan output seperti:

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

Tidak ada regexp, dapat dibaca, dan sesuai dengan 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;
}
Bachor
sumber
1

Menurut posting blog ini , fungsi ini harus melakukannya:

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

Anda akan menyebutnya seperti ini:

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

Jenis jsfiddle ini berfungsi, tetapi sepertinya beberapa elemen memiliki spasi di depannya.

CanSpice
sumber
Bayangkan harus melakukan semua itu dalam regex. Inilah sebabnya mengapa ekspresi reguler terkadang tidak cocok untuk penguraian.
CanSpice
Solusi ini tidak berhasil. Diberikan string uji asli "'string, duppi, du', 23, lala"["'string"," duppi"," du'"," 23"," lala"]
:,
@ridgerunner: Benar. Saya telah mengedit jawabannya dan jsfiddle untuk memperbaiki fungsinya. Pada dasarnya, saya beralih "'"ke '"'dan sebaliknya.
CanSpice
Itu membantu, tetapi sekarang fungsinya salah menangani string CSV bertanda kutip tunggal yang memiliki nilai kutip ganda. Misalnya, Membalik jenis kutipan dari string uji asli seperti ini: '"string, duppi, du", 23, lala'menghasilkan:['"string',' duppi'.' du"',' 23',' lala']
ridgerunner
@CanSpice, komentar Anda menginspirasi saya untuk mencoba dengan RegEx. Tidak memiliki banyak fitur, tetapi dapat dengan mudah ditambahkan. (Jawaban saya ada di halaman ini, jika Anda tertarik.)
Brigand
0

Ekspresi reguler untuk menyelamatkan! Beberapa baris kode ini menangani bidang yang dikutip dengan benar dengan koma, tanda kutip, dan baris baru yang disematkan berdasarkan standar 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' ] ]

Kecuali dinyatakan di tempat lain, Anda tidak memerlukan mesin negara hingga. Ekspresi reguler menangani RFC 4180 dengan baik berkat tampilan positif di belakang, tampilan negatif, dan tampilan positif.

Clone / unduh kode di https://github.com/peterthoeny/parse-csv-js

Peter Thoeny
sumber
0

Selain dari jawaban yang sangat bagus dan lengkap dari ridgerunner , saya memikirkan solusi yang sangat sederhana ketika backend Anda menjalankan PHP.

Menambahkan file PHP ini untuk domain Anda backend (katakanlah: 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")));
?>

Sekarang tambahkan fungsi ini ke toolkit JavaScript Anda (saya harus direvisi sedikit untuk membuat crossbrowser saya percaya).

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

Anda akan dikenakan biaya satu panggilan Ajax, tetapi setidaknya Anda tidak akan menduplikasi kode atau menyertakan pustaka eksternal apa pun.

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

Sebas
sumber
0

Anda bisa menggunakan papaparse.js seperti contoh di bawah ini:

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

Jangan lupa untuk memasukkan papaparse.js di folder yang sama.

Tahseen Alaa
sumber