如何用另一个文件中的映射值替换 csv 文件中的第 n 个字段?
Posted
技术标签:
【中文标题】如何用另一个文件中的映射值替换 csv 文件中的第 n 个字段?【英文标题】:How to replace nth field in a csv file with mapped value from another file? 【发布时间】:2013-11-16 02:48:41 【问题描述】:我有一个格式如下的 csv 文件:
23:56:00,5,1,7,99,100,101
23:56:30,5,1,7,98,199,191
23:57:00,6,1,6,99,99,98
23:57:30,5,2,6,97,99,199
...
还有一个如下格式的地图文件:
1:10
2:12
3:30
4:aa
5:16
6:11
7:bb
我想要完成的是将第一个 csv 文件中第 2,3 和 4 列中的字段替换为它们在映射文件中映射到的值。
例如在上述情况下,我想要的最终输出是这样的:
23:56:00,16,10,bb,99,100,101
23:56:30,16,10,bb,98,199,191
23:57:00,11,10,11,99,99,98
23:57:30,16,12,11,97,99,199
最好的方法是什么?我试图找出一种使用 awk/sed 的方法,但我不确定如何访问 awk 中的多个文件,以及这是否是最好的方法。因为它是一个大文件,所以会有很多重复,所以我不认为每次检查映射都是正确的方法。
有没有办法将映射存储到 shell 脚本内的哈希表中,然后使用哈希映射替换?
【问题讨论】:
【参考方案1】:尝试:
awk '
BEGIN FS = OFS = ","
FNR == NR
split($0, f, /:/)
map[f[1]] = f[2]
next
for (i=2; i<=4; i++)
if ($i in map) $i = map[$i]
print
' mapfile csvfile
它首先读取 map 文件并将数据保存在一个关联数组中,该数组与 csv
文件中的字段 2
、3
和 4
进行比较。结果产生:
23:56:00,16,10,bb,99,100,101
23:56:30,16,10,bb,98,199,191
23:57:00,11,10,11,99,99,98
23:57:30,16,12,11,97,99,199
【讨论】:
像魅力一样工作:-)【参考方案2】:一种纯 Bash 可能性(Bash 版本≥4):
将地图文件放入关联数组并处理您的 csv 文件:
#!/bin/bash
declare -A map=()
while IFS=: read -r k v; do
[[ -z "$k$v" ]] && continue # ignore empty lines
map[$k]=$v
done < mapfile.txt
IFS=,
while read -r -a ary; do
[[ -z "$ary[@]" ]] && continue # ignore empty lines
ary[1]=$map[$ary[1]]
ary[2]=$map[$ary[2]]
ary[3]=$map[$ary[3]]
echo "$ary[*]"
done < csvfile.txt
如果映射文件中的键是非负整数,则不需要关联数组,只需将行 declare -A map=()
替换为 map=()
。
它可能不是最有效的,因为 Bash 处理数据的速度不是最快,但它运行良好!
顺便说一句,没有任何错误检查,因此请确保将此脚本应用于格式正确的文件。
在您的示例中,这会产生:
23:56:00,16,10,bb,99,100,101
23:56:30,16,10,bb,98,199,191
23:57:00,11,10,11,99,99,98
23:57:30,16,12,11,97,99,199
【讨论】:
您的方法有效发现 Bur awk 在单个命令中效果很好(查看 Birei 的答案) @Ashish 我知道!但这个问题会有一套全面的答案:awk
、Bash
、Perl
。这种方式很好。 :)
.【参考方案3】:
Perl 解决方案。最近版本的 bash 中存在哈希,但在使用它们时我更喜欢真正的编程语言。
#!/usr/bin/perl
use warnings;
use strict;
open my $MAP, '<', '1.map' or die $!;
my %map;
while (<$MAP>)
chomp;
my ($key, $value) = split /:/;
$map$key = $value;
open my $CSV, '<', '1.csv' or die $!;
while (<$CSV>)
my @fields = split /,/;
s/(.*)/$map$1/ for @fields[1, 2, 3];
print join ',' => @fields;
【讨论】:
【参考方案4】:另一个awk
awk -F",|:" 'FNR==NR a[$1]=$2;next print $1":"$2":"$3,a[$4],a[$5],a[$6],$7,$8,$9' OFS=, map csv
23:56:00,16,10,bb,99,100,101
23:56:30,16,10,bb,98,199,191
23:57:00,11,10,11,99,99,98
23:57:30,16,12,11,97,99,199
【讨论】:
以上是关于如何用另一个文件中的映射值替换 csv 文件中的第 n 个字段?的主要内容,如果未能解决你的问题,请参考以下文章
如何用python读取csv文件,并把csv文件的第3,4列形成一个列表在python 中显示。如图中的两列数据