Как я могу проанализировать строку CSV с Javascript, который содержит запятую в данных?
У меня есть следующий тип строки
var string = "'string, duppi, du', 23, lala"
Я хочу разбить строку на массив в каждой запятой, но только запятые вне одинарных кавычек.
Я не могу понять правильное регулярное выражение для раскола...
string.split(/,/)
даст мне
["'string", " duppi", " du'", " 23", " lala"]
но результат должен быть:
["string, duppi, du", "23", "lala"]
Есть ли кросс-браузерное решение?
21 ответ
отказ
2014-12-01 Обновление: ответ ниже работает только для одного очень специфического формата CSV. Как правильно указал Д.Г. в комментариях, это решение НЕ соответствует определению CSV в RFC 4180, а также НЕ подходит для формата MS Excel. Это решение просто демонстрирует, как можно проанализировать одну (нестандартную) строку ввода CSV, которая содержит комбинацию типов строк, где строки могут содержать экранированные кавычки и запятые.
Нестандартное решение CSV
Как правильно указывает austincheney, вам действительно нужно анализировать строку от начала до конца, если вы хотите правильно обрабатывать строки в кавычках, которые могут содержать экранированные символы. Кроме того, OP не дает четкого определения, что такое "строка CSV". Сначала мы должны определить, что составляет действительную строку CSV и ее отдельные значения.
Дано: определение "CSV String"
Для целей этого обсуждения "строка CSV" состоит из нуля или более значений, где несколько значений разделены запятой. Каждое значение может состоять из:
- Строка в двойных кавычках. (может содержать неэкранированные одинарные кавычки.)
- Одна строка в кавычках. (может содержать неэкранированные двойные кавычки.)
- Строка без кавычек. (НЕ может содержать кавычки, запятые или обратную косую черту.)
- Пустое значение (Все пустые значения считаются пустыми.)
Правила / Примечания:
- Значения в кавычках могут содержать запятые.
- Значения в кавычках могут содержать что-либо, например
'that\'s cool'
, - Значения, содержащие кавычки, запятые или обратную косую черту, должны быть заключены в кавычки.
- Значения, содержащие начальные или конечные пробелы, должны быть заключены в кавычки.
- Обратная косая черта удаляется из всех:
\'
в единичных кавычках. - Обратная косая черта удаляется из всех:
\"
в двойных кавычках. - Строки без кавычек обрезаются на любые начальные и конечные пробелы.
- Разделитель запятых может иметь соседний пробел (который игнорируется).
Найти:
Функция JavaScript, которая преобразует допустимую строку CSV (как определено выше) в массив строковых значений.
Решение:
Регулярные выражения, используемые этим решением, являются сложными. И (ИМХО) все нетривиальные регулярные выражения должны быть представлены в свободном интервале с большим количеством комментариев и отступов. К сожалению, JavaScript не поддерживает режим свободного пробела. Таким образом, регулярные выражения, реализованные этим решением, сначала представлены в собственном синтаксисе регулярных выражений (выражается с помощью удобного Python: r'''...'''
синтаксис raw-multi-line-string).
Сначала вот регулярное выражение, которое проверяет, что строка CVS соответствует вышеуказанным требованиям:
Regex для проверки "строки 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. Он применяется повторно до тех пор, пока больше не будет найдено совпадений (и все значения были проанализированы).
Regex для анализа одного значения из допустимой строки 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.
Функция JavaScript для анализа строки 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;
};
Пример ввода и вывода:
В следующих примерах фигурные скобки используются для разграничения {result strings}
, (Это должно помочь визуализировать начальные / конечные пробелы и строки нулевой длины.)
// 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: перемещен отказ от ответственности наверх.
RFC 4180 решение
Это не решает строку в вопросе, так как ее формат не соответствует RFC 4180; допустимая кодировка - это двойная кавычка. Приведенное ниже решение правильно работает с CSV-файлами d/l из электронных таблиц Google.
ОБНОВЛЕНИЕ (3/2017)
Разбор единственной строки был бы неправильным. Согласно 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);
Мне понравился ответ 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('|'));
Люди, казалось, были против 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('|'));
PEG(.js) грамматика, которая обрабатывает примеры RFC 4180 на 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 '"'; }
/ [^"]
Проверьте на http://jsfiddle.net/knvzk/10 или https://pegjs.org/online.
Загрузите созданный синтаксический анализатор по адресу https://gist.github.com/3362830.
У меня был очень конкретный случай использования, когда я хотел скопировать ячейки из Google Sheets в свое веб-приложение. Ячейки могут включать двойные кавычки и символы новой строки. С помощью копирования и вставки ячейки разделяются символами табуляции, а ячейки с нечетными данными заключаются в двойные кавычки. Я попробовал это основное решение, связанную статью, используя regexp, Jquery-CSV и CSVToArray. http://papaparse.com/ Это единственный, который работал из коробки. Скопируйте и вставьте без проблем с Google Sheets с опциями автоматического определения по умолчанию.
Я использовал регулярное выражение несколько раз, но мне всегда приходится каждый раз заново учить его, что расстраивает:-)
Итак, вот решение без регулярного выражения:
function csvRowToArray(row, delimiter = ',', quoteChar = '"'){
let nStart = 0, nEnd = 0, a=[], nRowLen=row.length, bQuotedValue;
while (nStart <= nRowLen) {
bQuotedValue = (row.charAt(nStart) === quoteChar);
if (bQuotedValue) {
nStart++;
nEnd = row.indexOf(quoteChar + delimiter, nStart)
} else {
nEnd = row.indexOf(delimiter, nStart)
}
if (nEnd < 0) nEnd = nRowLen;
a.push(row.substring(nStart,nEnd));
nStart = nEnd + delimiter.length + (bQuotedValue ? 1 : 0)
}
return a;
}
Как это устроено:
- Передайте строку csv в
row
. - Пока начальная позиция следующего значения находится внутри строки, сделайте следующее:
- Если это значение было указано, установите
nEnd
к заключительной цитате. - Иначе, если значение НЕ было указано, установите
nEnd
к следующему разделителю. - Добавьте значение в массив.
- Набор
nStart
кnEnd
плюс длина разделителя.
- Если это значение было указано, установите
Иногда лучше написать свою небольшую функцию, чем использовать библиотеку. Ваш собственный код будет хорошо работать и будет занимать мало места. Кроме того, вы можете легко настроить его под свои нужды.
На помощь приходят регулярные выражения! Эти несколько строк кода обрабатывают поля с правильными кавычками со встроенными запятыми, кавычками и символами новой строки на основе стандарта 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
Нет регулярного выражения, читается и согласно https://en.wikipedia.org/wiki/Comma-separated_values:
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;
}
Добавление еще одного в список, потому что я считаю, что все вышеперечисленное не совсем "поцелуй" достаточно.
Этот использует регулярное выражение, чтобы найти запятые или новые строки при пропуске цитируемых элементов. Надеюсь, это то, что новички могут прочитать самостоятельно. splitFinder
регулярное выражение имеет три вещи, которые он делает (разделить на |
):
,
- находит запятые\r?\n
- находит новые строки (потенциально с возвратом каретки, если экспортер был хорош)"(\\"|[^"])+?"
- пропускает все, что заключено в кавычки, потому что запятые и переводы строк там не имеют значения. Если есть экранированная цитата\\"
в цитируемом элементе он будет захвачен до того, как будет найдена конечная цитата.
const splitFinder = /,|\r?\n|"(\\"|[^"])+?"/g;
function csvTo2dArray(parseMe) {
let currentRow = [];
const rowsOut = [currentRow];
let lastIndex = splitFinder.lastIndex = 0;
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
if (split.startsWith(`"`) === false) {
const splitStartIndex = splitFinder.lastIndex - split.length;
const addMe = parseMe.substring(lastIndex, splitStartIndex);
// remove quotes around the item
currentRow.push(addMe.replace(/^"|"$/g, ""));
lastIndex = splitFinder.lastIndex;
// 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), removing quotes
currentRow.push(parseMe.slice(lastIndex).replace(/^"|"$/g, ""));
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);
Если ваш разделитель цитат может быть двойным, то это дубликат кода JavaScript для анализа данных CSV.
Вы можете сначала перевести все одинарные кавычки в двойные:
string = string.replace( /'/g, '"' );
... или вы можете отредактировать регулярное выражение в этом вопросе, чтобы распознавать одинарные кавычки вместо двойных кавычек:
// Quoted fields.
"(?:'([^']*(?:''[^']*)*)'|" +
Однако это предполагает определенную разметку, которая не ясна из вашего вопроса. Просьба уточнить, какие могут быть все возможные варианты разметки, в соответствии с моим комментарием к вашему вопросу.
Я также столкнулся с такой же проблемой, когда мне нужно проанализировать файл CSV. Файл содержит столбец Address, который содержит ',' .
После анализа этого CSV в JSON я получаю несоответствующее отображение ключей при преобразовании его в файл JSON.
Я использовал узел для разбора файла и библиотеки, как baby parse и csvtojson
Пример файла -
address,pincode
foo,baar , 123456
Пока я выполнял синтаксический анализ напрямую без использования детского разбора в JSON, я получал
[{
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
}]
Мой ответ предполагает, что ваши входные данные являются отражением кода / контента из веб-источников, где одинарные и двойные кавычки полностью взаимозаменяемы при условии, что они встречаются как набор без совпадений.
Вы не можете использовать регулярные выражения для этого. На самом деле вам нужно написать микро-парсер для анализа строки, которую вы хотите разделить. Ради этого ответа я буду называть процитированные части ваших строк как подстроки. Вам нужно специально пройтись по струне. Рассмотрим следующий случай:
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.";
В этом случае вы абсолютно не представляете, где начинается или заканчивается подстрока, просто анализируя ввод для шаблона символа. Вместо этого вы должны написать логику, чтобы принять решение о том, используется ли символ кавычки как символ кавычки, сам по себе не заключен в кавычки и что символ кавычки не следует за escape.
Я не собираюсь писать такой уровень сложности кода для вас, но вы можете взглянуть на то, что я недавно написал и имеет шаблон, который вам нужен. Этот код не имеет ничего общего с запятыми, но в остальном он является достаточно допустимым микропарсером, чтобы вы могли следовать при написании собственного кода. Посмотрите на функцию asifix следующего приложения:
https://github.com/austincheney/Pretty-Diff/blob/master/fulljsmin.js
Чтобы дополнить этот ответ
Если вам нужно разобрать кавычки с другой кавычкой, например:
"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;
}
Попробуй это.
function parseCSV(csv) {
let quotes = [];
let token = /(?:(['"`])([\s\S]*?)\1)|([^\t,\r\n]+)\3?|([\r\n])/gm;
let text = csv.replace(/\\?(['"`])\1?/gm, s => s.length != 2 ? s : `_r#${quotes.push(s) - 1}`);
return [...text.matchAll(token)]
.map(t => (t[2] || t[3] || t[4])
.replace(/^_r#\d+$/, "")
.replace(/_r#\d+/g, q => quotes[q.replace(/\D+/, '')][1]))
.reduce((a, b) => /^[\r\n]$/g.test(b)
? a.push([]) && a
: a[a.length - 1].push(b) && a, [[]])
.filter(d => d.length);
}
Читая csv в строку, она содержит нулевое значение между строк, поэтому попробуйте \0 Строка за строкой, это работает мне.
stringLine = stringLine.replace( /\0/g, "" );
Согласно этому сообщению в блоге, эта функция должна делать это:
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 работает, но, похоже, перед некоторыми элементами есть пробелы.
Используйте csv-строку библиотеки npm для анализа строк вместо разделения: https://www.npmjs.com/package/csv-string
Это обработает запятую в кавычках и пустые записи
Этот основан на ответе niry, но для точки с запятой:
'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 and semicolons";four with no quotes;"five with CRLF\r\n"';
console.log(csvToArray(test));
Вы можете использовать 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>
Не забудьте включить файл papaparse.js в ту же папку.
Помимо отличного и полного ответа от 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));
}
Это обойдется вам в 1 ajax-вызов, но, по крайней мере, вы не будете дублировать код или включать какую-либо внешнюю библиотеку.