【问题标题】:Remove spurious commas删除虚假逗号
【发布时间】:2014-01-21 17:39:37
【问题描述】:

一个白痴客户正在生成 csv 文件,但一个字段有时在(描述字段)中有多余的逗号。

是否有一个整洁的正则表达式来查找这些不良记录并将多余的逗号替换为其他内容。 SED 命令行就可以了。

例子:

A,B,C,This is a description,D,E
F,G,H,This is a description with a comma (,) in it,D,E

我需要一个 SED 来判断行中有太多逗号,并从字段 4 中删除多余的逗号。

我们没有资格告诉愚蠢的客户更改他们的代码。

已添加

我不会反对仅删除 一个 必须多次运行的虚假逗号的解决方案。

【问题讨论】:

  • 两个问题:每一行有固定数量的字段吗?还有,哪些字段可以有额外的逗号?
  • 我猜测您不能将分隔符更改为管道或其他东西吗?老实说,用逗号进行的描述并不少见。
  • @Birei - 是的,每一行都有固定数量的字段。假设现在只有一个字段可以有额外的逗号,但如果您可以调整该字段,那么您将获得 +1 或更多。
  • @Jonathan - 你是对的 - 我们无法更改文件格式。我们只能对文件进行后处理。
  • 与其删除逗号,不如用引号转义描述:tools.ietf.org/html/rfc4180。这需要编写一些 bash 脚本逐行处理文件。

标签: regex csv


【解决方案1】:

方案一:单行,删除,

这里是 SED 单线:

sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\1'"$(sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\2/' <<< $myInput | sed 's/,//g')"'\3/' <<< $myInput

您必须将 &lt;&lt;&lt; $myInput 替换为您的实际输入。
当您使用 CSV 时,您可能需要调整(两次出现)正则表达式以匹配 CSV 表的每一行。
如果您的前三个和后两个字段大于一个字符,请将 [^,] 替换为 [^,]*

说明
我们使用这个正则表达式

/([^,],[^,],[^,],)(.*)(,.+,.+)/

它为我们捕获字符串的第一部分 (F,G,H,)、第二部分 (.*) 和最后一部分 (,D,E)。
第一个和第三个捕获组将保持不变,而第二个将被替换。
对于替换,我们第二次(实际上是第三次)调用sed。首先我们只捕获第二组,然后我们将每个 , 替换为空(仅在捕获组中!)。

证明

当然,如果没有多余的逗号,则不会替换任何内容:


## 解决方案 2:整个文件,逐行,删除 `,` ## 如果您只想指定 **一个文件** 并且替换应该发生在您可以使用的文件的每一行
while read line; do sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\1'"$(sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\2/' <<< $line | sed 's/,//g')"'\3/' <<< $line; done < input.txt

最后的input.txt 显然是你的文件。
我只是在while-loop 中使用上面的 SED 命令,它读取文本的每一行。这是必要的,因为您必须跟踪正在阅读的行,因为您在同一输入上调用了两次 sed


## 解决方案 3:整个文件,将字段包含在 `"` ## 正如 [@Łukasz L.][4] 在 OP 的 cmets 中指出的那样,根据 [RFC1480][5],它描述了 CSV 文件的格式,最好将包含逗号的字段包含在 ` “`。 这比其他解决方案更简单:
sed -r 's/([^,],[^,],[^,],)(.*)(,.*,.*)/\1"\2"\3/' input.txt

我们再次拥有三个捕获组。这让我们可以简单地将第二组包裹在"!

【讨论】:

  • 我最喜欢你的解决方案 3 - 我可以在 TextCrawler 中使用等效的 ((?:[^,],){3})(.*)((?:,.*){2}),它似乎工作正常。
  • @OldCurmudgeon 当然,这取决于你最终使用哪个正则表达式,但据我所知sed 不支持非捕获组,所以我尝试尽可能少地分组与sed合作。
【解决方案2】:

如果列数是固定的,我们可以尝试用前瞻?: 剪掉前三列和后两列,并匹配该行其余部分中的逗号(这是描述)。我有类似的东西:

(?:^(?:[^,]*,){3})(?:(?:[^,]*(,))*[^,]*)(?:(?:,[^,]*){2}$)

[^,]* 是字段(不带逗号),因此(?:^(?:[^,]*,){3}) 将剪切前 3 列(包括以下逗号)。 (?:(?:,[^,]*){2}$) 将删除最后 2 列,包括尾随逗号。 (?:(?:[^,]*(,))*[^,]*) 匹配内部。

在 JavaScript 中,整个表达式返回完整的描述(带逗号)作为第一个匹配项,其中的逗号作为第二个匹配项。根据正则表达式引擎,它可以使用空格键替换描述(如果引擎给出匹配表达式的范围)或使用替换语法定位(,) 表达式匹配逗号。

我现在无法使用 sed 运行和测试,但该正则表达式应该非常接近您需要的解决方案。

【讨论】:

  • 我的尝试到达了(^(?:[^,]*?,){3})(.*?),(.*?)((?:,[^,\n]*){2}) -> $1$2$3$4 这几乎就是你所拥有的。它在开始时剥离n 列,然后在最后需要xxx,yyy 然后m 列。然后它把它放回原处,把中间的逗号留在外面。可悲的是,它似乎以某种方式不一致。
【解决方案3】:

我的方法是根据总列数和必须修改的列来计算要删除的逗号。它接受三个参数,输入文件,列总数和带有额外逗号的奇怪列。

接下来 脚本会进行一些计算。当存在额外的逗号时,会有额外的列,因此它会获取这些额外列的位置并将它们连接起来。

#!/usr/bin/env perl 

use warnings;
use strict;
use Text::CSV_XS;

my (@columns);

open my $fh, '<', shift or die;
my ($total_columns, $weird_column) = (shift, shift);

my $csv = Text::CSV_XS->new or die;
while ( my $row = $csv->getline( $fh ) ) { 
    undef @columns;
    if ( @$row == $total_columns ) { 
        @columns = @$row;
        next;
    }   

    my $extra_columns = @$row - $total_columns;
    my $post_columns_index = $weird_column + $extra_columns;
    @columns = ( 
        @$row[0..($weird_column-2)], 
        join( '', @$row[($weird_column-1)..($post_columns_index-1)]),  
        @$row[$post_columns_index..$#$row] 
    );  
}
continue {
    $csv->print( \*STDOUT, \@columns );
    printf "\n";
}

假设输入文件如下:

A,B,C,This is a description,D,E
F,G,H,This is a description with a comma (,) in it,D,E
F,G,H,This is, a description with two commas (,) in it,D,E
F,G,H,This is, a description with, three commas (,) in it,D,E

像这样运行它:

perl script.pl infile 6 4

产生:

A,B,C,"This is a description",D,E
F,G,H,"This is a description with a comma () in it",D,E
F,G,H,"This is a description with two commas () in it",D,E
F,G,H,"This is a description with three commas () in it",D,E       

也许它可能会因边缘情况(第一个和最后一个字段)而失败。我没有深入测试它,但我希望你能明白。我尽量做到一般。

【讨论】:

    【解决方案4】:

    我尝试使用 sed 解决此问题,但无法在匹配组内执行替换。相反,我设法使用可以从终端运行的 ruby​​ 单线来做到这一点:

    cat your_file | ruby -ne '$_.scan(/^(\w+,\w+,\w+,)([^$]+)(,\w,\w)$/).each{|m|puts m[0]+m[1].gsub(",","")+m[2]}'
    

    这假设总是有 6 列,并且第 4 列可能包含逗号。

    代码已经用 ruby​​ 1.8.7、1.9.1 和 2.1.0 测试过。

    【讨论】:

    • 我将不得不等待其他人对这是否好的投票 - 我无法访问 Ruby。任何其他免费的 s&r 工具都是可以接受的 - 我经常使用 TextCrawler,所以使用 grep 或任何其他免费工具都可以。
    • @OldCurmudgeon 我会在这里留下答案;它可能会帮助某人。我记得几年前遇到过同样的问题。
    猜你喜欢
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    • 2021-01-25
    • 1970-01-01
    • 2019-05-23
    • 2012-12-24
    • 1970-01-01
    相关资源
    最近更新 更多