使用唯一标识符连接两个文件

使用唯一标识符连接两个文件

我有两个文件,分别包含大约 12900 和 4400 个条目,我想加入它们。这些文件包含全球所有陆基气象观测站的位置信息。最大的文件每两周更新一次,较小的文件每年左右更新一次。原始文件可以在这里找到(http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htmhttp://weather.rap.ucar.edu/surface/stations.txt)。我已经使用一些混合的 awk、sed 和 bash 脚本操作了我拥有的文件。我使用这些文件通过 GEMPAK 包来可视化数据,该包可以从 Unidata 免费获得。最大的文件可以与 GEMPAK 一起使用,但只是不能发挥其全部功能。为此,需要加入。

文件1包含气象观测站的位置信息,其中前6位数字是唯一的站点标识符。不同的参数(站点编号、站点名称、国家代码、纬度经度和站点海拔)仅由其在行中的位置定义,即没有制表符。

         060090 AKRABERG FYR                        DN  6138   -666     101
         060100 VAGA FLOGHAVN                       DN  6205   -728      88
         060110 TORSHAVN                            DN  6201   -675      55
         060120 KIRKJA                              DN  6231   -631      55
         060130 KLAKSVIK HELIPORT                   DN  6221   -656      75
         060160 HORNS REV A                         DN  5550    786      21
         060170 HORNS REV B                         DN  5558    761      10
         060190 SILSTRUP                            DN  5691    863       0
         060210 HANSTHOLM                           DN  5711    858       0
         060220 TYRA OEST                           DN  5571    480      43
         060240 THISTED LUFTHAVN                    DN  5706    870       8
         060290 GROENLANDSHAVNEN                    DN  5703   1005       0
         060300 FLYVESTATION AALBORG                DN  5708    985      13
         060310 TYLSTRUP                            DN  5718    995       0
         060320 STENHOEJ                            DN  5736   1033      56
         060330 HIRTSHALS                           DN  5758    995       0
         060340 SINDAL FLYVEPLADS                   DN  5750   1021      28

文件 2 包含文件 1 中的唯一标识符以及第二个 4 个字符的标识符(ICAO 定位符)。

060100 EKVG
060220 EKGF
060240 EKTS
060300 EKYT
060340 EKSN
060480 EKHS
060540 EKHO
060600 EKKA
060620 EKSV
060660 EKVJ
060700 EKAH
060780 EKAT

我想连接这两个文件,以便生成的文件在该行的前 4 个位置中具有 4 个字符标识符,即标识符应替换 4 个空格。

         060090 AKRABERG FYR                        DN  6138   -666     101
EKVG     060100 VAGA FLOGHAVN                       DN  6205   -728      88
         060110 TORSHAVN                            DN  6201   -675      55
         060120 KIRKJA                              DN  6231   -631      55
         060130 KLAKSVIK HELIPORT                   DN  6221   -656      75
         060160 HORNS REV A                         DN  5550    786      21
         060170 HORNS REV B                         DN  5558    761      10
         060190 SILSTRUP                            DN  5691    863       0
         060210 HANSTHOLM                           DN  5711    858       0
EKGF     060220 TYRA OEST                           DN  5571    480      43
EKTS     060240 THISTED LUFTHAVN                    DN  5706    870       8
         060290 GROENLANDSHAVNEN                    DN  5703   1005       0
EKYT     060300 FLYVESTATION AALBORG                DN  5708    985      13
         060310 TYLSTRUP                            DN  5718    995       0
         060320 STENHOEJ                            DN  5736   1033      56
         060330 HIRTSHALS                           DN  5758    995       0
EKSN     060340 SINDAL FLYVEPLADS                   DN  5750   1021      28

是否可以使用一些 bash 和/或 awk 脚本来完成此任务?

答案1

awk 'BEGIN { while(getline < "file2" ) { codes[$1] = $2 } }
     { printf "%4s%s\n", codes[$1], substr($0, 5) }' file1

答案2

我们几个人想看看是否可以join仅使用 来解决这个问题。这是我的尝试。因为它部分有效,@Terdon 欠我一顿晚餐 8-)。

命令

$ join -a1 -1 1 -2 1 -o 2.2 1.1 1.2 1.3 1.4 1.5 1.6 1.7 -e "N/A" \
     <(sort file1) <(sort file2)

例子

$ join -a1 -1 1 -2 1 -o 2.2 1.1 1.2 1.3 1.4 1.5 1.6 1.7 -e "N/A" <(sort file1) <(sort file2) | column -t
N/A   060090  AKRABERG          FYR         DN    6138  -666  101
EKVG  060100  VAGA              FLOGHAVN    DN    6205  -728  88
N/A   060110  TORSHAVN          DN          6201  -675  55    N/A
N/A   060120  KIRKJA            DN          6231  -631  55    N/A
N/A   060130  KLAKSVIK          HELIPORT    DN    6221  -656  75
N/A   060160  HORNS             REV         A     DN    5550  786
N/A   060170  HORNS             REV         B     DN    5558  761
N/A   060190  SILSTRUP          DN          5691  863   0     N/A
N/A   060210  HANSTHOLM         DN          5711  858   0     N/A
EKGF  060220  TYRA              OEST        DN    5571  480   43
EKTS  060240  THISTED           LUFTHAVN    DN    5706  870   8
N/A   060290  GROENLANDSHAVNEN  DN          5703  1005  0     N/A
EKYT  060300  FLYVESTATION      AALBORG     DN    5708  985   13
N/A   060310  TYLSTRUP          DN          5718  995   0     N/A
N/A   060320  STENHOEJ          DN          5736  1033  56    N/A
N/A   060330  HIRTSHALS         DN          5758  995   0     N/A
EKSN  060340  SINDAL            FLYVEPLADS  DN    5750  1021  28

细节

上面几乎使用了所有可用的选项,join这些选项告诉我的直觉,我们使用它是错误的,就像某种类型的弗兰肯斯坦方式一样,但我们都在这里学习,所以没关系......我想。

该开关-a1告诉 join 将与 file2 中没有相应匹配的任何行包含在 file1 中。这就是驱动这些行显示的原因:

N/A   060330  HIRTSHALS         DN          5758  995   0     N/A

-1 1表示-2 1将 2 个文件中的行连接到哪些列,主要是它们的第一列。表示-o ...要显示 2 个文件中的哪些列以及按什么顺序显示。

表示-e "N/A"使用字符串“N/A”作为占位符值来打印被 视为空的字段join

最后 2 个参数将输入 2 个文件,file1并按file2排序放入 join 命令中。

请友善,因为这是一项正在进行的工作,我们正在尝试演示如何使用该join命令解决此类问题,因为这似乎就是它所要解决的问题类型。

显着的问题

  1. 第三栏

    主要的问题是如何处理第三列,因为它是 1 个单词和 2 个单词值的混合。这似乎是一个主要的绊脚石join,我无法找到解决方法。任何指导将不胜感激。

  2. 间距

    所有原始间距都丢失了join,我也没有找到保留它的方法。因此,这join可能不是处理此类问题的正确方法。

  3. 似乎有效?

    经过多次使用命令行后,通用解决方案就在那里,因此这看起来确实至少可以部分工作,因此可以将其用作解决方案的核心,然后使用其他工具(例如awk和 )sed来清理它。但这引出了一个问题:“如果你用awk&任何方式清理它sed,那么你还不如直接使用它们?”。

答案3

应该可以使用,join但我不知道如何使其正确打印空格和空字段。无论如何,这个 Perl 小脚本就可以解决问题:

#!/usr/bin/env perl

## Open file2, the one that contains the codes
## it is expected to be the 1st argument given to the script.
open($a,"$ARGV[0]"); 

## Read the number<=>code pairs into a hash (an associative array)
## called 'k'
while (<$a>) {
    chomp; @f=split(/\s+/); $k{$f[0]}=$f[1];
}

## Open file1, the one that contains the data
## it is expected to be the 2nd argument given to the script.
open($b,"$ARGV[1]"); 
## Go through the file
while (<$b>) {
    ## Split each line at white space into the array @f
    @f=split(/\s+/);  

    ## $f[1] is the 6 digit number that defines the different stations.
    ## If this number has an entry in the hash %k, if it was found
    ## in file2, replace the first 4 spaces with its value from the hash.
    s/^\s{4}/$k{$f[1]}/ if defined($k{$f[1]});

    ## Print each line of the file
    print; 
}

将其保存为foo.pl并运行如下:

$ perl foo.pl file2 file1
         060090 AKRABERG FYR                        DN  6138   -666     101
EKVG     060100 VAGA FLOGHAVN                       DN  6205   -728      88
         060110 TORSHAVN                            DN  6201   -675      55
         060120 KIRKJA                              DN  6231   -631      55
         060130 KLAKSVIK HELIPORT                   DN  6221   -656      75
         060160 HORNS REV A                         DN  5550    786      21
         060170 HORNS REV B                         DN  5558    761      10
         060190 SILSTRUP                            DN  5691    863       0
         060210 HANSTHOLM                           DN  5711    858       0
EKGF     060220 TYRA OEST                           DN  5571    480      43
EKTS     060240 THISTED LUFTHAVN                    DN  5706    870       8
         060290 GROENLANDSHAVNEN                    DN  5703   1005       0
EKYT     060300 FLYVESTATION AALBORG                DN  5708    985      13
         060310 TYLSTRUP                            DN  5718    995       0
         060320 STENHOEJ                            DN  5736   1033      56
         060330 HIRTSHALS                           DN  5758    995       0
EKSN     060340 SINDAL FLYVEPLADS                   DN  5750   1021      28

答案4

join这是一种简单的方法,可以使用(+更多工具)来完成此操作并保留间距。这两个文件似乎都是按站号排序的,因此不需要额外的排序:

join -j1 -a1 -o 2.2 -e "    " file1 file2 | paste -d' ' - <(cut -c6- file1)

管道之前的部分非常相似可持续发展管理用于他的回答所以我不会再讲了。唯一的区别是我使用-e " "- 一个四个空格的字符串作为缺少输入字段的替换,并-o 2.2仅输出 file2 的第二个字段
因此join -j1 -a1 -o 2.2 -e " " file1 file2生成一个四个字符宽的列(下面不可见,但 EK 之后没有任何内容) ** 和空行实际上是四个空格):

心电图







心电图生长因子
EKTS

埃克伊特



EKSN

然后我们paste(使用空格作为分隔符)到 file1,从中我们得到cut前 5 个字符| paste -d' ' - <(cut -c6- file1)
最终结果:

         060090 AKRABERG FYR                        DN  6138   -666     101
EKVG     060100 VAGA FLOGHAVN                       DN  6205   -728      88
         060110 TORSHAVN                            DN  6201   -675      55
         060120 KIRKJA                              DN  6231   -631      55
         060130 KLAKSVIK HELIPORT                   DN  6221   -656      75
         060160 HORNS REV A                         DN  5550    786      21
         060170 HORNS REV B                         DN  5558    761      10
         060190 SILSTRUP                            DN  5691    863       0
         060210 HANSTHOLM                           DN  5711    858       0
EKGF     060220 TYRA OEST                           DN  5571    480      43
EKTS     060240 THISTED LUFTHAVN                    DN  5706    870       8
         060290 GROENLANDSHAVNEN                    DN  5703   1005       0
EKYT     060300 FLYVESTATION AALBORG                DN  5708    985      13
         060310 TYLSTRUP                            DN  5718    995       0
         060320 STENHOEJ                            DN  5736   1033      56
         060330 HIRTSHALS                           DN  5758    995       0
EKSN     060340 SINDAL FLYVEPLADS                   DN  5750   1021      28

相关内容