perl 如果数据中不存在分隔符值,则删除双引号

polhcujo  于 2022-11-15  发布在  Perl
关注(0)|答案(4)|浏览(224)

给定一个输入文件,其中每行包含对每列的引号和回车/换行字符。

  • 如果该行包含新行,则必须将其附加到引号内的同一行中,例如第1行
  • 如果不存在分隔符(,),则删除每列的双引号。
  • 删除回车符,即(^M)

例如,给定以下输入文件

"name","address","age"^M
"ram","abcd,^M
def","10"^M
"abhi","xyz","25"^M
"ad","ram,John","35"^M

我希望通过sed/perl/awk脚本/oneliner获得以下输出。

name,address,age
ram,"abcd,def",10
abhi,xyz,25
ad,"ram,John",35

解决方案,我已经厌倦了它到目前为止为附加与前一行

sed '/^[^"]*"[^"]*$/{N;s/\n//}' sample.txt

用于替换control-m字符

perl -pne 's/\\r//g' sample.txt

但是我没有达到我下面所要求的最终输出

2g32fytz

2g32fytz1#

使用一个库来解析CSV文件。除了总是想使用一个库来解析CSV文件外,你还有一些非常具体的原因,比如嵌入的换行符和分隔符。
在Perl中,一个好的库是Text::CSV(如果安装了Text::CSV_XS,它会 Package Text::CSV_XS)。

use warnings;
use strict;
use feature 'say';

use Text::CSV;

my $file = shift or die "Usage: $0 file.csv\n";

my $csv = Text::CSV->new({ binary => 1, auto_diag => 1 }); 

open my $fh, '<', $file  or die "Can't open $file: $!";

while (my $row = $csv->getline($fh)) { 
    s/\n+//g for @$row; 
    $csv->say(\*STDOUT, $row);
}

备注

  • 构造函数中的binary选项用于处理数据中嵌入的换行符
  • 一旦一行被读入数组引用$row,我就用一个简单的正则表达式删除每个字段中的换行符。
  • 修剪$row的工作原理如下:在foreach循环中,每个元素实际上都被循环变量别名化了,所以如果它被改变了,数组也会改变。我使用了默认值,其中元素被别名化了$_,正则表达式改变了,所以$row也改变了。

我喜欢这种紧凑的快捷方式,因为它具有如此独特的外观,以至于我可以从房间的另一端看出阵列正在适当地更改;所以我认为这是一种习惯用法。但如果它实际上是混乱的,请尽一切手段写出一个完整的和适当的循环

  • 处理后的输出将打印到STDOUT。或者,打开一个输出文件并将该文件句柄传递给say(在较早的模块版本中传递给print),以便输出直接转到该文件

上面的打印内容,用于问题中提供的示例输入

name,address,age
ram,"abcd,def",10
abhi,xyz,25
ad,"ram,John",35
o0lyfsai

o0lyfsai2#

这可能对你有用(GNU sed):

sed ':a;/[^"]$/{N;s/\n//;ba};s/"\([^",]*\)"/\1/g' file

解决方案分为两部分:
1.将断开的线连接成完整的线。
1.删除不包含逗号的字段两边的双引号。
如果当前行没有以双引号结尾,则追加下一行,删除换行符并重复。否则:删除不包含双引号或逗号的字段周围的双引号。
注意:假设字段不包含双引号。如果是这样,第一步的条件需要修改,字段中的双引号也需要考虑。

cuxqih21

cuxqih213#

FPAT是使用gnu awk的方法,它处理逗号分隔的文件。
1.删除^m
1.清洁线
1.删除qute

dos2unix sample.txt  

awk '{printf "%s"(/,$/?"":"\n"),$0}' sample.txt > tmp && mv tmp sample.txt
"name","address","age"
"ram","abcd,def","10"
"abhi","xyz","25"
"ad","ram,John","35"

awk -v FPAT="([^,]+)|(\"[^\"]+\")" -v OFS=, '{for (i=1;i<=NF;i++) if($i!~",") $i=substr($i,2,length($i)-2)}1' sample.txt
name,address,age
ram,"abcd,def",10
abhi,xyz,25
ad,"ram,John",35

一气呵成:

dos2unix sample.txt && awk '{printf "%s"(/,$/?"":"\n"),$0}' sample.txt | awk -v FPAT="([^,]+)|(\"[^\"]+\")" -v OFS=, '{for (i=1;i<=NF;i++) if($i!~",") $i=substr($i,2,length($i)-2)}1'

通常你设置文件分隔符FS or F来告诉你文件是如何被分隔的。FPAT="([^,]+)|(\"[^\"]+\")" FPAT告诉你文件看起来像使用一个正则表达式。这个正则表达式很复杂,经常和CSV一起使用。

  • (i=1;i<=NF;i++)通过一个场在线路上循环。
  • if($i!~",")如果不包含逗号,则
  • $i=substr($i,2,length($i)-2)删除第一个和最后一个字符,即"

如果一个字段由于某种原因不包含",这是更健壮的:

awk -v FPAT="([^,]+)|(\"[^\"]+\")" -v OFS=, '{for (i=1;i<=NF;i++) if($i!~",") {n=split($i,a,"\"");$i=(n>1?a[2]:$i)}}1' file

它不会对不包含双引号的字段执行任何操作。

mefy6pfw

mefy6pfw4#

对于perl,请尝试以下操作:

perl -e '
while (<>) {
    s/\r$//;      # remove trailing CR code
    $str .= $_;
}

while ($str =~ /("(("")|[^"])*"\n?)|((^|(?<=,))[^,]*((?=,)|\n))/g) {
    $_ = $&;
    if (/,/) {    # the element contains ","
        s/\n//g;  # then remove newline(s) if any
    } else {      # otherwise remove surrounding double quotes
        s/^"//s; s/"$//s;
    }
    push(@ary, $_);

    if (/\n$/) {  # newline terminates the element
        print join(",", @ary);
        @ary = ();
    }
}' sample.txt

输出量:

name,address,age
ram,"abcd,def",10
abhi,xyz,25
ad,"ram,John",35

相关问题