我有一个大型的CSV文件(300MB+),我只想使用Perl删除列2、3和6-8,并删除重复的行:
注1:所有列都用, (逗号)分隔,但有时我的单元格值包含一个,或多个,,由"分隔(参见最后一行,第9和第10列);因此,我仍然希望能够处理input.csv文件,即使它在单元格中有,:
注2:我添加了input.csv和output.csv文件的链接:
Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8,Col9,Col10
info 1,info 2,info 3,...,info 10
address 1,address 2,....,address 10
city 1,city 2,city 3,city 4,city 5,city 6,city 7,city 8,"city 9, extra","city 10, new"Col1,Col4,Col5,Col9,Col10
info 1,info 4,info 5,info 9,info 10
address 1,address 4,address 5,address 9,address 10
city 1,city 4,city 5,"city 9, extra","city 10, new"我已经找到了一个Perl命令,它可以使用regex删除最后一列,但不知道它是否足够好,或者如何调整它以适应我的情况(其他任何建议都非常受欢迎!):
perl -pe 's/.*\K,.*//'是否可以使用Perl只删除列2、3和6-8并删除任何重复行?
PS:更新input.csv文件以包括重复行
谢谢!
发布于 2022-03-15 01:36:18
最简单的方法是使用米勒又名mlr,这是一个处理CSV、json和其他几种输入或输出格式的数据的很好工具。例如:
$ mlr --csv --implicit-csv-header --headerless-csv-output \
cut -x -f 2,3,6,7,8 \
then uniq -a input.csv
Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10使用--implicit-csv-header和--headerless-csv-output选项实际上忽略了标题行(即,将其与其他数据行一样对待),并允许我指定要按数字而不是按名称剪切的字段。
我必须编辑您的示例input.csv文件,以便在缺少的字段中添加一些垃圾数据。否则mlr会抱怨的。我还添加了一个重复的输入行来测试dupe消除是否有效。
$ cat input.csv
Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8,Col9,Col10
info 1,info 2,info 3,info 4,5,6,7,8,9,info 10
info 1,info 2,info 3,info 4,5,6,7,8,9,info 10
address 1,address 2,3,4,5,6,7,8,9,address 10
city 1, city 2,3,4,5,6,7,8,9,city 10如果您想用perl来做这件事:
$ perl -F, -lane '
next if $seen{$_}++;
splice @F,5,3;
splice @F,1,2;
print join ",", @F' input.csv
Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10这使用perl的-a选项自动将每个输入行拆分为一个名为@F的数组。-F选项告诉它要使用什么分隔符。
注1: perl数组从零开始,而不是one...so数组元素5是列6。splice @$row, 5, 3从元素5开始从数组中删除三个元素(即列6、7、8)。详情请参见perldoc -f splice。
注2:我在这里以相反的顺序删除这些列(即之前的高编号列<#>)。否则,如果在删除第5、6、7栏之前删除了第2和第3栏,则第一次删除将使这些列重新编号(改为3、4、5)。
$ perl -MText::CSV -e '
my $csv = Text::CSV->new();
while (my $row = $csv->getline(*ARGV)) {
next if $seen{join ",", @$row}++;
splice @$row, 5, 3;
splice @$row, 1, 2;
$csv->say(*STDOUT, $row);
}' input.csv
Col1,Col4,Col5,Col9,Col10
"info 1","info 4",5,9,"info 10"
"address 1",4,5,9,"address 10"
"city 1",4,5,9,"city 10"这里有四件事值得注意:
Text::CSV不是一个核心perl模块,因此需要安装它。它是为大多数(如果不是全部) Linux发行版打包的。例如,在Debian上,您可以使用sudo apt-get install libtext-csv-perl安装它。否则,您可以使用perl附带的cpan命令来安装它。getline()方法(如上面的$row = $csv->getline(*ARGV) )返回对数组或arrayref的引用。这是指向整个数组的标量值(有关更多信息,请参见man perlref和man perldata )。$row包含arrayref。使用/操作$row可以工作于引用本身,而不是它所引用的数据。因此,例如,$row2 = $row复制引用,而不是数据。两个参考文献都指向相同的数据。@$row将arrayref作为数组进行“去引用”,这样就可以像其他数组一样使用它。*ARGV in getline(*ARGV)是一个特殊的文件句柄,它从命令行中给出的所有文件名参数中读取输入(在D42中存储在一个名为@ARGV的数组中)。假设非文件名参数(例如选项,如果脚本有处理选项的代码)已经被处理并从@ARGV中删除。不存在或不能打开的文件名(例如,由于权限)将产生错误消息。简而言之,它从一个或多个文件名中读取您提供的文件名。-的参数被视为stdin,因此它可以读取来自文件(S)、stdin或两者的输入。这是一个非常简单和原始的例子,说明了什么文本::CSV能够和如何使用它。有关更多细节和示例,请阅读手册页。
正如您在上面的示例输出中看到的那样,Text::CSV在默认情况下,如果包含空格,将引用文本字段。如果不希望它这样做,则在使用quote_space方法创建$csv对象时,可以通过将$csv属性设置为zero....either来覆盖该对象:
my $csv = Text::CSV->new({ quote_space => 0 });或事后:
my $csv = Text::CSV->new();
$csv->quote_space(0);然后输出将如下所示:
Col1,Col4,Col5,Col9,Col10
info 1,info 4,5,9,info 10
address 1,4,5,9,address 10
city 1,4,5,9,city 10发布于 2022-03-14 01:10:58
将其转换为数组,仔细考虑,将其重新创建为csv:
perl -pe '@c = split(","); splice(@c, 1, 2); splice(@c, 3, 3); $_ = join(", ", @c)如果引用字段,则可以使用Text::CSV:
$ cat in.csv
Col1,Col2,Col3,Col4,Col5
one,two,three,four,five
six,"se,ven","ei,ght",nine,ten
$ perl -MText::CSV -e 'Text::CSV::csv( in => "in.csv", headers => false, on_in => sub { splice( @{@_[1]}, 1, 2) } )'
Col1,Col4,Col5
one,four,five
six,nine,ten您询问perl,但出于意识的考虑,还可以考虑剪切工具:cut -f '1,4,5,9,10' -d ,。
https://unix.stackexchange.com/questions/694272
复制相似问题