如何用另一个文件中的映射值替换 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 文件中的字段 234 进行比较。结果产生:

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 我知道!但这个问题会有一套全面的答案:awkBashPerl。这种方式很好。 :).【参考方案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 中显示。如图中的两列数据

如何用数据框中的字符串值替换 int 值[重复]

如何用另一个文件中的组(已知正则表达式)替换一个文件中的空白空间?

如何用另一个数组值替换javascript中的数组值?

使用tohash.select时,如何用字符串替换空值?

请教高手:csv文件用excel打开另存后,如何把单元格格式中的“”双引号去掉,直接转换为文本格式