Разделите строку запятыми, но игнорируйте запятые в двойных кавычках, используя Javascript
Я ищу [a, b, c, "d, e, f", g, h]
превратить в массив из 6 элементов: a, b, c, "d,e,f", g, h. Я немного новичок в RegEx, поэтому любая помощь - это здорово. Я пытаюсь сделать это через Javascript. Это то, что я до сих пор:
str = str.split(/,+|"[^"]+"/g);
Но сейчас он разбивает все, что находится в двойных кавычках, что неверно. Спасибо за любую помощь.
Редактировать: Хорошо, извините, я сформулировал этот вопрос очень плохо. Мне дают строку, а не массив.
var str = 'a, b, c, "d, e, f", g, h';
И я хочу превратить это в массив, используя что-то вроде функции "split".
18 ответов
Вот что я бы сделал.
var str = 'a, b, c, "d, e, f", g, h';
var arr = str.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g);
/* will match:
(
".*?" double quotes + anything but double quotes + double quotes
| OR
[^",\s]+ 1 or more characters excl. double quotes, comma or spaces of any kind
)
(?= FOLLOWED BY
\s*, 0 or more empty spaces and a comma
| OR
\s*$ 0 or more empty spaces and nothing else (end of string)
)
*/
arr = arr || [];
// this will prevent JS from throwing an error in
// the below loop when there are no matches
for (var i = 0; i < arr.length; i++) console.log('arr['+i+'] =',arr[i]);
Регулярное выражение: /,(?=(?:(?:[^"]*"){2})*[^"]*$)/
const input_line = '"2C95699FFC68","201 S BOULEVARDRICHMOND, VA 23220","8299600062754882","2018-09-23"'
let my_split = input_line.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/)[4]
Output:
my_split[0]: "2C95699FFC68",
my_split[1]: "201 S BOULEVARDRICHMOND, VA 23220",
my_split[2]: "8299600062754882",
my_split[3]: "2018-09-23"
Ссылка по ссылке для объяснения: https://regexr.com/44u6o
Вот функция JavaScript, чтобы сделать это:
function splitCSVButIgnoreCommasInDoublequotes(str) {
//split the str first
//then merge the elments between two double quotes
var delimiter = ',';
var quotes = '"';
var elements = str.split(delimiter);
var newElements = [];
for (var i = 0; i < elements.length; ++i) {
if (elements[i].indexOf(quotes) >= 0) {//the left double quotes is found
var indexOfRightQuotes = -1;
var tmp = elements[i];
//find the right double quotes
for (var j = i + 1; j < elements.length; ++j) {
if (elements[j].indexOf(quotes) >= 0) {
indexOfRightQuotes = j;
}
}
//found the right double quotes
//merge all the elements between double quotes
if (-1 != indexOfRightQuotes) {
for (var j = i + 1; j <= indexOfRightQuotes; ++j) {
tmp = tmp + delimiter + elements[j];
}
newElements.push(tmp);
i = indexOfRightQuotes;
}
else { //right double quotes is not found
newElements.push(elements[i]);
}
}
else {//no left double quotes is found
newElements.push(elements[i]);
}
}
return newElements;
}
Это хорошо работает для меня. (Я использовал точки с запятой, чтобы в предупреждающем сообщении отображалась разница между запятыми, добавляемыми при превращении массива в строку, и фактическими захваченными значениями.)
var str = 'a; b; c; "d; e; f"; g; h; "i"';
var array = str.match(/("[^"]*")|[^;]+/g);
alert(array);
Вот не-регулярное выражение, которое предполагает двойные кавычки в парах:
function splitCsv(str) {
return str.split(',').reduce((accum,curr)=>{
if(accum.isConcatting) {
accum.soFar[accum.soFar.length-1] += ','+curr
} else {
accum.soFar.push(curr)
}
if(curr.split('"').length % 2 == 0) {
accum.isConcatting= !accum.isConcatting
}
return accum;
},{soFar:[],isConcatting:false}).soFar
}
console.log(splitCsv('asdf,"a,d",fdsa'),' should be ',['asdf','"a,d"','fdsa'])
console.log(splitCsv(',asdf,,fds,'),' should be ',['','asdf','','fds',''])
console.log(splitCsv('asdf,"a,,,d",fdsa'),' should be ',['asdf','"a,,,d"','fdsa'])
Вот регулярное выражение, которое мы используем для извлечения действительных аргументов из списка аргументов, разделенных запятыми, с поддержкой аргументов в двойных кавычках. Это работает для обозначенных крайних случаев. Например
- не включает кавычки в матчах
- работает с пробелами в матчах
- работает с пустыми полями
(?<=")[^"]+?(?="(?:\s*?,|\s*?$))|(?<=(?:^|,)\s*?)(?:[^,"\s][^,"]*[^,"\s])|(?:[^,"\s])(?![^"]*?"(?:\s*?,|\s*?$))(?=\s*?(?:,|$))
Доказательство: https://regex101.com/r/UL8kyy/3/tests (Примечание: в настоящее время работает только в Chrome, потому что регулярное выражение использует ретроспективу, которая поддерживается только в ECMA2018)
Согласно нашим рекомендациям, он избегает групп, не захватывающих данные, и жадного сопоставления.
Я уверен, что это можно упростить, я открыт для предложений / дополнительных тестов.
Для всех, кто заинтересован, первая часть соответствует аргументам, заключенным в двойные кавычки и разделенным запятыми:
(?<=")[^"]+?(?="(?:\s*?,|\s*?$))
Вторая часть соответствует аргументам, разделенным запятыми:
(?<=(?:^|,)\s*?)(?:[^,"\s][^,"]*[^,"\s])|(?:[^,"\s])(?![^"]*?"(?:\s*?,|\s*?$))(?=\s*?(?:,|$))
Мне почти понравился принятый ответ, но он не проанализировал пространство правильно и / или оставил двойные кавычки необрезанными, поэтому вот моя функция:
/**
* Splits the given string into components, and returns the components array.
* Each component must be separated by a comma.
* If the component contains one or more comma(s), it must be wrapped with double quotes.
* The double quote must not be used inside components (replace it with a special string like __double__quotes__ for instance, then transform it again into double quotes later...).
*
* https://stackru.com/questions/11456850/split-a-string-by-commas-but-ignore-commas-within-double-quotes-using-javascript
*/
function splitComponentsByComma(str){
var ret = [];
var arr = str.match(/(".*?"|[^",]+)(?=\s*,|\s*$)/g);
for (let i in arr) {
let element = arr[i];
if ('"' === element[0]) {
element = element.substr(1, element.length - 2);
} else {
element = arr[i].trim();
}
ret.push(element);
}
return ret;
}
console.log(splitComponentsByComma('Hello World, b, c, "d, e, f", c')); // [ 'Hello World', 'b', 'c', 'd, e, f', 'c' ]
Анализируйте любой код CSV или CSV-String на основе TYPESCRIPT
public parseCSV(content:string):any[string]{
return content.split("\n").map(ar=>ar.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/).map(refi=>refi.replace(/[\x00-\x08\x0E-\x1F\x7F-\uFFFF]/g, "").trim()));
}
var str='"abc",jkl,1000,qwerty6000';
parseCSV(str);
выход:
[
"abc","jkl","1000","qwerty6000"
]
Я знаю, что это немного долго, но вот мое мнение:
var sample="[a, b, c, \"d, e, f\", g, h]";
var inQuotes = false, items = [], currentItem = '';
for(var i = 0; i < sample.length; i++) {
if (sample[i] == '"') {
inQuotes = !inQuotes;
if (!inQuotes) {
if (currentItem.length) items.push(currentItem);
currentItem = '';
}
continue;
}
if ((/^[\"\[\]\,\s]$/gi).test(sample[i]) && !inQuotes) {
if (currentItem.length) items.push(currentItem);
currentItem = '';
continue;
}
currentItem += sample[i];
}
if (currentItem.length) items.push(currentItem);
console.log(items);
Как примечание стороны, это будет работать и с, и без скобок в начале и в конце.
Это берет CSV-файл по одной строке за раз и выплевывает массив с запятыми внутри речевых меток без изменений. если речевые метки не обнаружены, это просто.split(",")s как обычно... может пробники заменить второй цикл чем-то, но он делает работу как есть
function parse(str){
if(str.indexOf("\"")>-1){
var aInputSplit = str.split(",");
var aOutput = [];
//var adding = 0;
for(i=0;i<aInputSplit.length;i++){
if(aInputSplit[i].indexOf("\"")>-1){
var sWithCommas = aInputSplit[i];
for(z=i;z<aInputSplit.length;z++){
if(z != i && aInputSplit[z].indexOf("\"") == -1){
sWithCommas+= ","+aInputSplit[z];
}else if(z != i && aInputSplit[z].indexOf("\"") > -1){
sWithCommas+= ","+aInputSplit[z];
sWithCommas.replace(new RegExp("\"", 'g'), "");
aOutput.push(sWithCommas);
i=z;
z=aInputSplit.length+1;
}
}
}else{
aOutput.push(aInputSplit[i]);
}
}
return aOutput;
}else{
return str.split(",");
}
}
Используйте csv-строку библиотеки npm для анализа строк вместо разделения: https://www.npmjs.com/package/csv-string
Это обработает пустые записи
Этот код поддерживает одинарные и двойные кавычки, разделитель-запятая внутри одинарных и двойных кавычек, а также пустые символы внутри запятых.
txtArguments = " 1,2,'asd,123', pepe, \"A,B\",, ";
let acumParam = new Array();
if(txtArguments.trim().length > 0)
{
let inSQuotes = false;
let inDQuotes = false;
let tmpParam = "";
for(let i=0; i<txtArguments.length; ++i)
{
const char = txtArguments.substring(i, i+1);
if(char == "'")
inSQuotes = inSQuotes ? false : !inDQuotes;
else if(char == '"')
inDQuotes = inDQuotes ? false : !inSQuotes;
if(char == ",")
{
if(inSQuotes)
tmpParam += char;
else if(inDQuotes)
tmpParam += char;
else
{
acumParam.push(tmpParam);
tmpParam = "";
}
}
else
tmpParam += char;
}
acumParam.push(tmpParam);
}
Что-то вроде стека должно сделать свое дело. Здесь я смутно использую маркер boolean в качестве стека (просто получая свою цель).
var str = "a,b,c,blah\"d,=,f\"blah,\"g,h,";
var getAttributes = function(str){
var result = [];
var strBuf = '';
var start = 0 ;
var marker = false;
for (var i = 0; i< str.length; i++){
if (str[i] === '"'){
marker = !marker;
}
if (str[i] === ',' && !marker){
result.push(str.substr(start, i - start));
start = i+1;
}
}
if (start <= str.length){
result.push(str.substr(start, i - start));
}
return result;
};
console.log(getAttributes(str));
настройка jsfiddle изображения код выходного изображения
Код работает, если ваша входная строка в формате stringTocompare. Запустите код на https://jsfiddle.net/ чтобы увидеть выходные данные для настройки fiddlejs. Пожалуйста, обратитесь к скриншоту. Вы можете использовать функцию split для одного и того же кода для кода ниже и настроить код в соответствии с вашими потребностями. Удалите жирный шрифт или слово с in ** из кода, если вы не хотите иметь запятую после разделения split attach=attach**+","**+actualString[t+1].
var stringTocompare='"Manufacturer","12345","6001","00",,"Calfe,eto,lin","Calfe,edin","4","20","10","07/01/2018","01/01/2006",,,,,,,,"03/31/2004"';
console.log(stringTocompare);
var actualString=stringTocompare.split(',');
console.log("Before");
for(var i=0;i<actualString.length;i++){
console.log(actualString[i]);
}
//var actualString=stringTocompare.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);
for(var i=0;i<actualString.length;i++){
var flag=0;
var x=actualString[i];
if(x!==null)
{
if(x[0]=='"' && x[x.length-1]!=='"'){
var p=0;
var t=i;
var b=i;
for(var k=i;k<actualString.length;k++){
var y=actualString[k];
if(y[y.length-1]!=='"'){
p++;
}
if(y[y.length-1]=='"'){
flag=1;
}
if(flag==1)
break;
}
var attach=actualString[t];
for(var s=p;s>0;s--){
attach=attach+","+actualString[t+1];
t++;
}
actualString[i]=attach;
actualString.splice(b+1,p);
}
}
}
console.log("After");
for(var i=0;i<actualString.length;i++){
console.log(actualString[i]);
}
[1]: https://stackru.com/images/9c4b61f40892502d724b7f835f0418e2b60d9b5a.png
Я решил это с помощью простого парсера.
Он просто просматривает строку char по char, разделяя сегмент, когда находит split_char (например, запятую), но также имеет флаг включения / выключения, который переключается при нахождении encapsulator_char (например, цитата). Для этого не требуется, чтобы инкапсулятор находился в начале поля / сегмента (a, b, «c, d» будут создавать 3 сегмента с «b», «c» в качестве второго), но он должен работать для хорошо сформированный CSV с ускользнувшими символами инкапсулятора.
function split_except_within(text, split_char, encapsulator_char, escape_char) {
var start = 0
var encapsulated = false
var fields = []
for (var c = 0; c < text.length; c++) {
var char = text[c]
if (char === split_char && ! encapsulated) {
fields.push(text.substring(start, c))
start = c+1
}
if (char === encapsulator_char && (c === 0 || text[c-1] !== escape_char) )
encapsulated = ! encapsulated
}
fields.push(text.substring(start))
return fields
}
const csvSplit = (line) => {
let splitLine = [];
var quotesplit = line.split('"');
var lastindex = quotesplit.length - 1;
// split evens removing outside quotes, push odds
quotesplit.forEach((val, index) => {
if (index % 2 === 0) {
var firstchar = (index == 0) ? 0 : 1;
var trimmed = (index == lastindex)
? val.substring(firstchar)
: val.slice(firstchar, -1);
trimmed.split(",").forEach(v => splitLine.push(v));
} else {
splitLine.push(val);
}
});
return splitLine;
}
это работает до тех пор, пока кавычки всегда находятся за пределами значений, содержащих запятые, которые необходимо исключить (например, файл csv).
если у вас есть такие вещи, как '1,2,4"2,6",8', это не сработает.
Предполагая, что ваша строка действительно выглядит '[a, b, c, "d, e, f", g, h]'
Я считаю, что это будет "приемлемым вариантом использования для eval()
:
myString = 'var myArr ' + myString;
eval(myString);
console.log(myArr); // will now be an array of elements: a, b, c, "d, e, f", g, h
Редактировать: как отметил Ракета, strict
режим удаляет eval
способность вводить переменные в локальную область, а это значит, что вы захотите сделать это:
var myArr = eval(myString);
У меня были похожие проблемы с этим, и я не нашел хорошего решения.net, поэтому пошел DIY. ПРИМЕЧАНИЕ: это также использовалось для ответа на
но кажется более подходящим здесь (но полезно там)
В моем приложении я анализирую CSV, поэтому мои разделенные учетные данные ",". этот метод, я полагаю, работает только для тех случаев, когда у вас есть один аргумент разделения символов.
Итак, я написал функцию, которая игнорирует запятые в двойных кавычках. он делает это путем преобразования входной строки в массив символов и разбора char на char
public static string[] Splitter_IgnoreQuotes(string stringToSplit)
{
char[] CharsOfData = stringToSplit.ToCharArray();
//enter your expected array size here or alloc.
string[] dataArray = new string[37];
int arrayIndex = 0;
bool DoubleQuotesJustSeen = false;
foreach (char theChar in CharsOfData)
{
//did we just see double quotes, and no command? dont split then. you could make ',' a variable for your split parameters I'm working with a csv.
if ((theChar != ',' || DoubleQuotesJustSeen) && theChar != '"')
{
dataArray[arrayIndex] = dataArray[arrayIndex] + theChar;
}
else if (theChar == '"')
{
if (DoubleQuotesJustSeen)
{
DoubleQuotesJustSeen = false;
}
else
{
DoubleQuotesJustSeen = true;
}
}
else if (theChar == ',' && !DoubleQuotesJustSeen)
{
arrayIndex++;
}
}
return dataArray;
}
Эта функция, на мой вкус приложения, также игнорирует ("") при любом вводе, поскольку они не нужны и присутствуют в моем вводе.