一个白痴客户正在生成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中删除额外的逗号。
我们没有告诉愚蠢的客户改变他们的代码。
加
我不反对只删除一个虚假逗号的解决方案,我必须多次运行。
答案 0 :(得分:3)
,
在这里你使用SED单行:
sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\1'"$(sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\2/' <<< $myInput | sed 's/,//g')"'\3/' <<< $myInput
您必须将<<< $myInput
替换为您实际输入的内容
当您使用CSV时,您可能需要调整(同时出现)正则表达式以匹配CSV表格的每一行。
如果您的前三个和后两个字段大于一个字符,请将[^,]
替换为[^,]*
。
<强>解释强>:
我们使用这个正则表达式
/([^,],[^,],[^,],)(.*)(,.+,.+)/
为我们捕获字符串的第一个(F,G,H,
),第二个(.*
)和最后一个部分(,D,E
)。
第一个和第三个捕获组将保持不变,而第二个捕获组将被替换
对于替换,我们称sed
为第二(实际上是第三)时间。首先我们只捕获第二组,然后我们用任何东西替换每个,
(仅在捕获组中!)。
<强>证明强>:
当然,如果没有不需要的逗号,则不会替换任何内容:
<小时/>
,
如果您只想指定文件,则可以使用该文件的每一行进行替换
while read line; do sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\1'"$(sed -r 's/([^,],[^,],[^,],)(.*)(,.+,.+)/\2/' <<< $line | sed 's/,//g')"'\3/' <<< $line; done < input.txt
最后input.txt
显然是 - 你的文件
我只是在while
- 循环中使用上面的SED命令来读取文本的每一行。这是必要的,因为您必须跟踪正在阅读的行,因为您在同一输入上调用了sed
两次。
<小时/>
"
正如@Łukasz L.在对OP的评论中所指出的,根据描述CSV文件格式的RFC1480,最好在{{1}中包含逗号的字段。 }}。
这比其他解决方案更简单:
"
我们再次拥有三个捕获组。这允许我们简单地将第二组包装在sed -r 's/([^,],[^,],[^,],)(.*)(,.*,.*)/\1"\2"\3/' input.txt
!
答案 1 :(得分:2)
如果列数是固定的,我们可以尝试使用前瞻?:
剪切前三列和后两列,并匹配行中其余部分的逗号(这是描述)。我有类似的东西:
(?:^(?:[^,]*,){3})(?:(?:[^,]*(,))*[^,]*)(?:(?:,[^,]*){2}$)
[^,]*
是字段(不带逗号),因此(?:^(?:[^,]*,){3})
会删除前3列(包括以下逗号)。 (?:(?:,[^,]*){2}$)
将删除最后2列,包括尾随逗号。 (?:(?:[^,]*(,))*[^,]*)
匹配内部。
在JavaScript中,整个表达式将完整描述(使用逗号)作为第一个匹配,并将其中的commans作为第二个匹配。它根据Regex引擎提供了espace和替换描述(如果引擎给出匹配表达式的范围)或将(,)
表达式匹配逗号与替换语法对齐的可能性。
我现在没有机会用sed运行和测试,但是正则表达式应该非常接近你需要的解决方案。
答案 2 :(得分:0)
我尝试使用sed解决此问题,但无法在匹配组内执行替换。相反,我设法用一个可以从终端运行的红宝石单线程来做到这一点:
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进行测试。
答案 3 :(得分:0)
我的方法是根据列总数和您必须修改的列来计算要删除的逗号。它接受三个参数,输入文件,列总数和带有额外逗号的奇怪列。
下一个perl脚本会进行一些计算。当存在额外的逗号时,会有其他列,因此它可以定位那些额外的列并将它们连接起来。
#!/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
也许它会因边缘情况(第一个和最后一个字段)而失败。我没有深入测试它,但我希望你能得到这个想法。我试图尽可能地做到这一点。