使用 unix 命令从 csv 文件中提取第 4 列

ags*_*udy 6 awk perl text-processing csv

我有一个格式如下的 csv 文件。

"col1","col2","col3","col4"
"1","text1","<p>big
      html
     text</p>
","4th column"
"2","text2","<p>big2
      html2
     text2</p>
","4th column2"
Run Code Online (Sandbox Code Playgroud)

我想使用提取第 4 列。我认为这awk是最好的工具(如果我错了,请告诉我)。我试过这个

awk -F, '{print $4}' myFile.csv 
Run Code Online (Sandbox Code Playgroud)

但它失败了。我认为因为第三列是多行一列。我如何使用awk或任何其他 unix 命令来提取第 4 列。我正在寻找一个有效的解决方案,因为我的真实文件很大(> 2GB)

ter*_*don 10

更新:

实际上,更简单的方法是在 中设置记录分隔符gawk

$ gawk 'BEGIN{RS="\"\n"; FS=","}{print $4}' myFile.csv
"col4
"4th column
"4th column2
Run Code Online (Sandbox Code Playgroud)

但是,这将删除"每列末尾的尾随。要解决此问题,您可以自己打印:

$ gawk 'BEGIN{RS="\"\n"; FS=","}{print $4"\""}' myFile.csv
"col4"
"4th column"
"4th column2"
Run Code Online (Sandbox Code Playgroud)

如果您根本不需要引号,则可以将字段分隔符设置为","

$ gawk 'BEGIN{RS="\"\n"; FS="\",\""}{print $3}' myFile.csv
col3
4th column
4th column2
Run Code Online (Sandbox Code Playgroud)

我能想到的唯一方法一种方法是首先修改文件,然后解析它。在您的示例中,实际分隔两条记录的换行符始终跟在 a 之后"

"col1","col2","col3","col4"   <-- here 
1,"text1","<p>big             <-- no "
Run Code Online (Sandbox Code Playgroud)

如果整个文件都是这种情况,您可以用"占位符替换所有不在 a 之后的换行符,从而将所有内容都放在一行中。然后您可以正常解析gawk并最终再次用换行符替换占位符。我将使用该字符串&%&作为占位符,因为它不太可能存在于您的文件中:

$ perl -pe 's/"\s*\n/"&%&/; s/\n//g; s/&%&/\n/;' myFile.csv | awk -F, '{print $4}'
"col4"
"4th column"
"4th column2"
Run Code Online (Sandbox Code Playgroud)

-p标志为perl手段print each line of the input file将给出脚本之后-e。然后有 3 个替换 ( s/foo/bar/) 命令:

  • s/"\s*\n/"&%&/:这将找到任何"后跟 0 个或多个空白字符 ( \s*) 和换行符 ( \n) 的内容。它将用"&%&. 添加引号是为了保留格式&%&,它只是一个随机占位符,它可以是任何未出现在您的文件中的内容。

  • s/\n//g;:由于真正的换行符已被占位符替换,我们现在可以安全地删除此记录中所有剩余的换行符。这意味着当前记录的所有行现在都已连接到当前行中。

  • s/&%&/\n/ :这会将占位符变回正常的新行。

要了解命令的输出,请在不使用的情况下运行它gawk

$ perl -pe 's/"\s*\n/"&%&/; s/\n//g; s/&%&/\n/;' myFile.csv 
"col1","col2","col3","col4"
1,"text1","<p>big      html     text</p>","4th column"
2,"text2","<p>big2      html2     text2</p>","4th column2"
Run Code Online (Sandbox Code Playgroud)

因此,您现在在单行上拥有很长的记录,这是gawk.

你也可以直接在 Perl 中完成:

perl -ne '$/="\"\n"; chomp;@a=split(/,/);print "$a[3]\"\n"' myFile.csv
"col4"
"4th column"
"4th column2"
Run Code Online (Sandbox Code Playgroud)

这使用了更多的 Perl 魔法。该$/特殊变量是输入记录分隔符。通过将它设置为"\n我们告诉 Perl 不是在 at\n而是只在 at拆分行,"\n"这样每个记录都将被视为单行。完成后,chomp从行尾删除换行符(用于稍后打印)并split拆分每条记录(on ,)并将其保存在 array 中@a。最后,我们打印数组的第 4 个元素(数组从 0 开始编号,因此是$a[3]),即第 4 列。

更神奇的是,打开自动吐痰 ( -a) 并用逗号分隔 ( F",")。这会将每条记录拆分为特殊@F数组,您可以打印数组的第 4 个元素:

$ perl -F"," -ane '$/="\"\n";chomp;print "$F[3]"' myFile.csv
"col4"
"4th column"
"4th column2"
Run Code Online (Sandbox Code Playgroud)


gle*_*man 6

我建议使用经过实战测试的 CSV 解析模块。例如:

perl -MText::CSV -E '
    $csv = Text::CSV->new({binary=>1}); 
    while ($row = $csv->getline(STDIN)) {say $row->[3]}
' < file.csv
Run Code Online (Sandbox Code Playgroud)
col4
4th column
4th column2
Run Code Online (Sandbox Code Playgroud)

或者这会产生相同的结果:

ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[3]}' file.csv
Run Code Online (Sandbox Code Playgroud)


Rah*_*til 5

Python :

python -c "import csv,sys; print '\n'.join([ r[3] for r in csv.reader(open(sys.argv[1]))])" myfile.csv
Run Code Online (Sandbox Code Playgroud)

与上述通过列表将文件内容加载到内存的方法不同,大文件的内存保守解决方案一次遍历文件一行

#!/usr/bin/env python
import sys
import csv
with open(sys.argv[1]) as f:
  for row in csv.reader(f):
    print(row[3])
Run Code Online (Sandbox Code Playgroud)

所有解决方案的测试结果:

操作系统:Ubuntu 12.04

公共 CSV 数据下载自:http : //seanlahman.com/baseball-archive/statistics/

版本详情

root@ubuntu:~# python --version
Python 2.7.3
root@ubuntu:~# ruby --version
ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
root@ubuntu:~# perl --version

This is perl 5, version 14, subversion 2 (v5.14.2) built for i686-linux-gnu-thread-multi-64int
Run Code Online (Sandbox Code Playgroud)

结果与 time

root@ubuntu:~# time python -c "import csv,sys; print '\n'.join([ r[3] for r in csv.reader(open(sys.argv[1]))])" Master.csv > /tmp/python

real    0m1.112s
user    0m0.056s
sys     0m0.316s
root@ubuntu:~# time ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[3]}' Master.csv > /tmp/ruby

real    0m24.582s
user    0m23.397s
sys     0m0.448s
root@ubuntu:~# time perl -MText::CSV -E '
>     $csv = Text::CSV->new({binary=>1});
>     while ($row = $csv->getline(STDIN)) {say $row->[3]}
> ' < Master.csv > /tmp/perl

real    0m7.049s
user    0m5.876s
sys     0m0.468s
Run Code Online (Sandbox Code Playgroud)