使用sed处理带分隔符的文本文件

时间:2015-09-02 08:47:40

标签: regex sed

我有一个“;”分隔文件:

aa;;;;aa
rgg;;;;fdg
aff;sfg;;;fasg
sfaf;sdfas;;;           
ASFGF;;;;fasg
QFA;DSGS;;DSFAG;fagf

我想用\N处理它来替换缺失的值。 结果应该是:

aa;\N;\N;\N;aa
rgg;\N;\N;\N;fdg
aff;sfg;\N;\N;fasg
sfaf;sdfas;\N;\N;\N         
ASFGF;\N;\N;\N;fasg
QFA;DSGS;\N;DSFAG;fagf

我正在尝试使用sed脚本:

sed "s/;\(;\)/;\\N\1/g" file1.txt  >file2.txt

但我得到的是

aa;\N;;\N;aa
rgg;\N;;\N;fdg
aff;sfg;\N;;fasg
sfaf;sdfas;\N;;         
ASFGF;\N;;\N;fasg
QFA;DSGS;\N;DSFAG;fagf

6 个答案:

答案 0 :(得分:5)

您不需要将第二个分号括在括号中,只是在替换字符串中将其用作\1。您可以在替换字符串中使用;

sed 's/;;/;\\N;/g'

正如您所注意到的,当它找到一对分号时,它会用所需的字符串替换它,然后跳过它,而不是再次读取第二个分号,这使得它在每两个分号后插入\N

解决方案是使用积极的前瞻; regex/;(?=;)/sed不支持。

但是可以使用sed以简单的方式解决问题:复制搜索命令;第一个命令用;;替换;\N的奇怪外观,第二个命令用于处理偶数外观。最终结果是你需要的结果。

命令很简单:

sed 's/;;/;\\N;/g;s/;;/;\\N;/g'

它复制了上一个命令,并使用;g之间的s来分隔它们。或者,您可以为每个搜索表达式使用-e命令行选项:

sed -e 's/;;/;\\N;/g' -e 's/;;/;\\N;/g'

<强>更新

OP在评论中提出&#34;如果我的文件有100列怎么办?&#34;

让我们试试看它是否有效:

$ echo "0;1;;2;;;3;;;;4;;;;;5;;;;;;6;;;;;;;" | sed 's/;;/;\\N;/g;s/;;/;\\N;/g'
0;1;\N;2;\N;\N;3;\N;\N;\N;4;\N;\N;\N;\N;5;\N;\N;\N;\N;\N;6;\N;\N;\N;\N;\N;\N;

看,妈!有用! : - )

更新#2

我忽略了这样一个事实,即问题并没有要求用其他内容替换;;,而是替换使用;分隔列的文件中的空/缺少值。因此,当表达式出现在行的开头或结尾时,我的表达式不会修复缺失值。

正如OP在评论中添加的那样,完整的sed命令是:

sed 's/;;/;\\N;/g;s/;;/;\\N;/g;s/^;/\\N;/g;s/;$/;\\N/g'

或(为了便于阅读):

sed -e 's/;;/;\\N;/g;' -e 's/;;/;\\N;/g;' -e 's/^;/\\N;/g' -e 's/;$/;\\N/g'

另外两个步骤取代了&#39 ;;&#39;当他们在开始或结束时找到它时。

答案 1 :(得分:3)

您可以将此sed命令与2 s(替换)命令一起使用:

sed 's/;;/;\\N;/g; s/;;/;\\N;/g;' file
aa;\N;\N;\N;aa
rgg;\N;\N;\N;fdg
aff;sfg;\N;\N;fasg
sfaf;sdfas;\N;\N;
ASFGF;\N;\N;\N;fasg
QFA;DSGS;\N;DSFAG;fagf

或在perl命令中使用 lookarounds regex

perl -pe 's/(?<=;)(?=;)/\\N/g' file
aa;\N;\N;\N;aa
rgg;\N;\N;\N;fdg
aff;sfg;\N;\N;fasg
sfaf;sdfas;\N;\N;
ASFGF;\N;\N;\N;fasg
QFA;DSGS;\N;DSFAG;fagf

答案 2 :(得分:2)

主要问题是,对于一次替换,您不能多次使用相同的字符:

s/;;/..../g:第二个;无法重复用于;;;

等字符串中的下一场比赛

如果你想使用sed而不使用类似Perl的正则表达式模式,你可以使用带有条件命令t的循环:

sed ':a;s/;;/;\\N;/g;ta;' file

:a定义了标签“a”,ta只有在替换了某些内容时才会转到此标签。

对于行尾的;(并处理最终的尾随空格):

sed ':a;s/;;/;\\N;/g;ta; s/;[ \t\r]*$/;\\N/1' file

答案 3 :(得分:1)

这个awk one-liner会给你你想要的东西:

awk -F';' -v OFS=';' '{for(i=1;i<=NF;i++)if($i=="")$i="\\N"}7' file

如果你真的想要这一行:sfaf;sdfas;\N;\N;\N,这条线适合你:

awk -F';' -v OFS=';' '{for(i=1;i<=NF;i++)if($i=="")$i="\\N";sub(/;$/,";\\N")}7' file

答案 4 :(得分:1)

sed 's/;/;\\N/g;s/;\\N\([^;]\)/;\1/g;s/;[[:blank:]]*$/;\\N/' YourFile
  • 非递归,在线,符合posix

概念:

  • 更改所有;
  • 放回无与伦比的
  • 添加最后;的特殊情况,最后在行尾
  • 之前添加空格

答案 5 :(得分:1)

这可能适合你(GNU sed):

sed -r ':;s/^(;)|(;);|(;)$/\2\3\\N\1\2/g;t' file

有4种情况可能会出现空字段:在记录的开头,2个字段分隔符之间,空字段后面的空字段和记录的结尾。可以采用轮换来满足上述情况1,2和4,并且可以通过使用循环(:;...;t)的第二次传递来满足senario 3。使用g标志可以在两次传递中替换多个senarios。