我有一个CSV文件my.csv
,其中的字段由;
分隔。每个字段包含由|
分隔的任意数量(有时为零)的子字段,如下所示:
aa5|xb1;xc3;ba7|cx2|xx3|da2;ed1
xa2|bx9;ab5;;af2|xb5
xb7;xa6|fc5;fd6|xb5;xc3|ax9
df3;ab5|xc7|de2;da5;ax2|xd8|bb1
我想删除除|
以外的所有内容的所有子字段(包含相应的x
),即得到如下输出:
xb1;xc3;xx3;
xa2;;;xb5
xb7;xa6;xb5;xc3
;xc7;;xd8
现在我使用sed
:
sed -i 's/^[^;x]*;/;/g' my.csv #In 1st fields without x.
sed -i 's/;[^;x]*;/;;/g' my.csv #In middle field without x.
sed -i 's/;[^;x]*$/;/g' my.csv #In last field without x.
sed -i 's/^[^;x][^;]*|x/x/g' my.csv #In 1st fields with x. before x.
sed -i 's/;[^;x][^;]*|x/;x/g' my.csv #In non-1st fields with x. before x.
sed -i 's/|[^x][^;]*//g' my.csv #In fields with x. after x.
有没有办法做到一行或至少更简单?我遇到了如何匹配“行开始OR';'”的问题。
在我的情况下,保证不会有多个以x
开头的子字段。然而,从理论上讲,如果不是这样,如何解决问题也是有用的(例如,将字段ab1|xa2|bc3|xd4|ex5
转换为xa2|xd4
)。
答案 0 :(得分:1)
使用sed
sed ':;s/\(^\||\|;\)[^x;|][^;|]*/\1/;t;s/|//g' file
只需循环删除不以x开头的字段,然后删除条形码。
xb1;xc3;xx3;
xa2;;;xb5
xb7;xa6;xb5;xc3
;xc7;;xd8
答案 1 :(得分:1)
你可以使用这个awk:
awk 'BEGIN{FS=OFS=";"} {for (i=1; i<=NF; i++) {
gsub(/(^|\|)[^x][^|]*/, "", $i); sub(/^\|/, "", $i)}} 1' file
xb1;xc3;xx3;
xa2;;;xb5
xb7;xa6;xb5;xc3
;xc7;;xd8
这也会将ab1|xa2|bc3|xd4|ex5
转换为xa2|xd4
,即从<{1}}开始的多个字段。
答案 2 :(得分:1)
考虑使用Perl:
perl -ple '$_ = join(";", map { join "|", grep /^x/, split /\|/ } split(/;/, $_, -1))'
这从split(/;/, $_, -1)
开始,将行(此时为$_
)拆分为以分号分隔的字段数组。负限制参数使得尾随空字段(如果存在)不会被丢弃。
该数组的元素是
map
表达式和join
再次使用分号。 map
表达式中的转换是
split
沿着|
,grep
ping /^x/
(即删除那些与正则表达式不匹配的内容),join
再次使用|
。我认为这种结构化方法比正则表达式魔法更强大。
在一行末尾丢失空字段的旧代码:
perl -F\; -aple '$_=join(";", map { join("|", grep(/^x/, split(/\|/, $_))) } @F)'
这使用-a
进行自动拆分,看起来更好但没有对所需的字段拆分进行细粒度控制。
答案 3 :(得分:0)
要求救援!
awk -F";" -vOFS=";" '
{line=sep="";
for(i=1;i<=NF;i++) {
c=split($i,s,"|");
for(j=1;j<=c;j++)
if(s[j]~/^x/) {
line=line sep s[j];
sep=OFS
}
}
print line}'
进一步拆分每个元素进行模式检查,将结果组合成一行,在每一行设置第一个元素后设置分隔符。