首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从大型CSV文件中删除特定的CSV列,并使用Perl复制行。

从大型CSV文件中删除特定的CSV列,并使用Perl复制行。
EN

Unix & Linux用户
提问于 2022-03-14 00:29:54
回答 2查看 1K关注 0票数 1

我有一个大型的CSV文件(300MB+),我只想使用Perl删除列2、3和6-8,并删除重复的行:

注1:所有列都用, (逗号)分隔,但有时我的单元格值包含一个,或多个,,由"分隔(参见最后一行,第9和第10列);因此,我仍然希望能够处理input.csv文件,即使它在单元格中有,

注2:我添加了input.csv和output.csv文件的链接:

input.csv

代码语言:javascript
复制
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"

output.csv

代码语言:javascript
复制
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删除最后一列,但不知道它是否足够好,或者如何调整它以适应我的情况(其他任何建议都非常受欢迎!):

代码语言:javascript
复制
perl -pe 's/.*\K,.*//'

是否可以使用Perl只删除列2、3和6-8并删除任何重复行?

PS:更新input.csv文件以包括重复行

谢谢!

EN

回答 2

Unix & Linux用户

回答已采纳

发布于 2022-03-15 01:36:18

最简单的方法是使用米勒又名mlr,这是一个处理CSV、json和其他几种输入或输出格式的数据的很好工具。例如:

代码语言:javascript
复制
$ 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消除是否有效。

代码语言:javascript
复制
$ 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来做这件事:

  1. 如果您只需要处理简单的逗号分隔输入:
代码语言:javascript
复制
$ 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)。

  1. 使用案文:CSV处理任何有效的CSV (包括包含逗号的多行引号列):
代码语言:javascript
复制
$ 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"

这里有四件事值得注意:

  1. Text::CSV不是一个核心perl模块,因此需要安装它。它是为大多数(如果不是全部) Linux发行版打包的。例如,在Debian上,您可以使用sudo apt-get install libtext-csv-perl安装它。否则,您可以使用perl附带的cpan命令来安装它。
  2. CSV的getline()方法(如上面的$row = $csv->getline(*ARGV) )返回对数组或arrayref的引用。这是指向整个数组的标量值(有关更多信息,请参见man perlrefman perldata )。
  3. 上面代码中的$row包含arrayref。使用/操作$row可以工作于引用本身,而不是它所引用的数据。因此,例如,$row2 = $row复制引用,而不是数据。两个参考文献都指向相同的数据。@$row将arrayref作为数组进行“去引用”,这样就可以像其他数组一样使用它。
  4. *ARGV in getline(*ARGV)是一个特殊的文件句柄,它从命令行中给出的所有文件名参数中读取输入(在D42中存储在一个名为@ARGV的数组中)。假设非文件名参数(例如选项,如果脚本有处理选项的代码)已经被处理并从@ARGV中删除。不存在或不能打开的文件名(例如,由于权限)将产生错误消息。简而言之,它从一个或多个文件名中读取您提供的文件名。-的参数被视为stdin,因此它可以读取来自文件(S)、stdin或两者的输入。

这是一个非常简单和原始的例子,说明了什么文本::CSV能够和如何使用它。有关更多细节和示例,请阅读手册页。

正如您在上面的示例输出中看到的那样,Text::CSV在默认情况下,如果包含空格,将引用文本字段。如果不希望它这样做,则在使用quote_space方法创建$csv对象时,可以通过将$csv属性设置为zero....either来覆盖该对象:

代码语言:javascript
复制
my $csv = Text::CSV->new({ quote_space => 0 });

或事后:

代码语言:javascript
复制
my $csv = Text::CSV->new();
$csv->quote_space(0);

然后输出将如下所示:

代码语言:javascript
复制
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
票数 2
EN

Unix & Linux用户

发布于 2022-03-14 01:10:58

将其转换为数组,仔细考虑,将其重新创建为csv:

代码语言:javascript
复制
perl -pe '@c = split(","); splice(@c, 1, 2); splice(@c, 3, 3); $_ = join(", ", @c)

如果引用字段,则可以使用Text::CSV

代码语言:javascript
复制
$ 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 ,

票数 2
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/694272

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档