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)
我建议使用经过实战测试的 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)
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)