Regexp匹配n次出现后的每次出现

时间:2018-05-17 17:32:39

标签: javascript regex csv

给出一些csv数据,在最终字段中使用未转义的逗号,如下所示

1, 2, 3, 4, 5
a, b, c, d, foo bar
a, b, c, d, Lorem Ipsum, dolores umbridge, something latin
a, b, c, d, upcoming unescaped commas!, one, two, three, oh no!

我想要一个正则表达式匹配每行第四个逗号之后的所有逗号,这样我就可以用转义的逗号替换它们,

这是我迄今为止的可怕尝试,它似乎只返回了第一次出现之后的最后一次出现。

^([^,]*,){4}([^,]*(,)[^,]*)*

对于某些情境

某些声称与ASS等csv格式部分兼容的格式假设在最后字段中有未转义逗号因为在解析标题行时已注册了字段数。

您可以在ASS规范

中看到这一点
  

格式行指定SSA如何解释所有后续事件行。字段名称必须拼写正确,如下所示:   Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text   最后一个字段将始终是Text字段,因此它可以包含逗号。

在这里

  

每行中的信息字段用逗号分隔。   这使得在字符名称和样式名称中使用逗号是非法的(SSA阻止您在这些中使用逗号)。它还可以很容易地将SSA脚本的块作为CSV文件加载到电子表格中,并删​​除另一个字幕程序所需的信息列。

为了能够解析这样的文件,假设你已经将数据分成了" Chunks",我还需要转义最后一个字段中的所有逗号以使用某些{{ 3}} S上。

6 个答案:

答案 0 :(得分:6)

您可以匹配直到第4次出现逗号,然后使用以下方法捕获剩余的逗号:

^(?:[^,]*,){1,4}|(,)

由于.replace()方法接受第二个参数的回调,您可以在该回调中检查是否存在第一个捕获组。

JS代码:



var str = `1, 2, 3, 4, 5
a, b, c, d, foo bar
a, b, c, d, Lorem Ipsum, dolores umbridge, something latin
a, b, c, d, upcoming unescaped commas!, one, two, three, oh no!`

console.log(
  str.replace(/^(?:[^,]*,){1,4}|(,)/gm, function($0, $1) {
     return $1 ? '\\' + $1 : $0;
  })
);




答案 1 :(得分:3)

如果您对正则表达式的考虑较少,可能更容易找到解决方案。尝试这样的事情:

var result = "";
myCsvString.split('\n').forEach( (line) => {
    var count = 0;
    // /,/g means every occurrence of the character ','
    line = line.replace(/,/g, function(){ 
        if(++count > 4) { 
            return "\\,"
        } 
        return ","
    })
    result += line + '\n';
});

答案 2 :(得分:3)

为什么你需要一个正则表达式? 你可以通过string& amp;数组操作。

const csv = `1, 2, 3, 4, 5
a, b, c, d, foo bar
a, b, c, d, Lorem Ipsum, dolores umbridge, something latin
a, b, c, d, upcoming unescaped commas!, one, two, three, oh no!`;

const lines = csv.split('\n');

const result = lines.map((line) => {
  const commas = line.split(', ');
  const rest = commas.splice(4).join('\\, ');
  return commas.concat(rest).join(', ');
}).join('\n');

console.log(result);

答案 3 :(得分:2)

两种解决方案:

  • 如果您在支持新的lookbehind的环境中执行此操作,并且您有一个字符串数组
  • 如果你不是,或者你有一个大字符串

如果你可以使用lookbehind并拥有一个字符串数组

如果您在支持lookbehind的Node.js等环境中执行此操作(将在ES2018规范中),您可以这样做:

const newData = data.map(line => line.replace(/(?<=(?:.*,){4,}.*),/g, "\\,"));

(如果你有一系列的线条(我认为你有这种情况),我只能让它工作。如果你有一个大字符串,请参阅下面的非后视版本。)

至少有四次出现.*,后跟.*,这是一个积极的看法。它将每个逗号与前面的逗号匹配。

示例(如果您有一个行数组):

const data = [
  "1, 2, 3, 4, 5",
  "a, b, c, d, foo bar",
  "a, b, c, d, Lorem Ipsum, dolores umbridge, something latin",
  "a, b, c, d, upcoming unescaped commas!, one, two, three, oh no!",
];
const newData = data.map(line => line.replace(/(?<=(?:[^,]*,){4,}.*),/g, "\\,"));
console.log(newData);

如果您不能使用lookbehind或有一个大字符串

如果你不能使用lookbehind,你可以在相关逗号之前捕获文本,然后在文本之后使用replace,函数回调版本为replace

const newData = data.map(line =>
    line.replace(/^((?:[^,]*,){4})(.*)$/, (m, c0, c1) => c0 + c1.replace(/,/g, "\\,"))
);

示例(如果data是数组):

const data = [
  "1, 2, 3, 4, 5",
  "a, b, c, d, foo bar",
  "a, b, c, d, Lorem Ipsum, dolores umbridge, something latin",
  "a, b, c, d, upcoming unescaped commas!, one, two, three, oh no!",
];
const newData = data.map(line => line.replace(/^((?:[^,]*,){4})(.*)$/, (m, c0, c1) => c0 + c1.replace(/,/g, "\\,")));
console.log(newData);

或者如果data是一个大字符串:

const newData = data.replace(/^((?:[^,]*,){4})(.*)$/gm, (m, c0, c1) => c0 + c1.replace(/,/g, "\\,"));

示例(如果data是一个大字符串):

const data =
`1, 2, 3, 4, 5
a, b, c, d, foo bar
a, b, c, d, Lorem Ipsum, dolores umbridge, something latin
a, b, c, d, upcoming unescaped commas!, one, two, three, oh no!`;
const newData = data.replace(/^((?:[^,]*,){4})(.*)$/gm, (m, c0, c1) => c0 + c1.replace(/,/g, "\\,"));
console.log(newData);

答案 4 :(得分:2)

如果你没有建议其他建议,那么将计数嵌入更高阶函数可能是有意义的:

&#13;
&#13;
[out] = forecast('This snowy weather is so cold.','cold','awesome')
out => 'This snowy weather is so awesome.
&#13;
&#13;
&#13;

答案 5 :(得分:2)

这里有很多答案,但我认为如果你知道第一个 n 组件将要使用数组解构和...运算符会有点痛苦那里。

&#13;
&#13;
const text = `1, 2, 3, 4, 5
a, b, c, d, foo bar
a, b, c, d, Lorem Ipsum, dolores umbridge, something latin
a, b, c, d, upcoming unescaped commas!, one, two, three, oh no!`

const formatted = text.split('\n').map(line => {
  [a, b, c, d, ...rest] = line.split(', ')
  return [a, b, c, d, rest.join('\\,')]
})

console.log(formatted)
&#13;
&#13;
&#13;