连接具有唯一标识符的两个文件


9

我要加入两个文件,分别有大约12900和4400个条目。这些文件包含全球所有陆基气象观测站的位置信息。最大的文件每两周更新一次,较小的文件大约一年一次。原始文件可以在这里找到(http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htmhttp://weather.rap.ucar.edu/surface/ station.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脚本来完成此任务?


文件按ID字段排序?
miracle173 2013年

Answers:


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

一个优雅的解决方案,我只有一些基本技能才能理解。谢谢!
Staffan Scherloff 2013年

该程序在开始工作之前将一个文件读入内存。如果文件变大,可能会大大降低性能。对于较大的文件,无法使用此方法。
miracle173

7

我们中的一些人想知道是否可以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在file1中包括与file2没有对应匹配项的任何行。因此,这就是促使这些行显示的原因:

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

-1 1-2 1说的哪些列参加从2个文件中的线条,主要是他们的第一个列。该-o ...是说从2档栏目,其中以显示和。

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

最后2个参数是喂养的2个文件,file1file2作为分类到join命令。

请好心,因为这是一项正在进行的工作,并且我们正在尝试演示如何使用join命令解决此类问题,因为这似乎是本应解决的问题。

显着的问题

  1. 第三栏

    主要问题是如何应对第三列,因为它是1个单词和2个单词的值的混合。这似乎是一个主要的绊脚石join,我想不出办法。任何指导将不胜感激。

  2. 间距

    所有原始间距都会丢失,join并且我也看不到任何一种保持间距的方法。所以join可能不是处理这些类型的毕竟问题的正确方法。

  3. 似乎可以工作吗?

    使用命令行多弯之后,一般的解决办法是有那么这似乎像它至少可以工作部分,所以这可能在解决方案的核心来使用,再利用其他工具,如awksed把它清理干净。但是,这引出了一个问题:“如果要通过awksed方式对其进行清理,那么您还可以直接使用它们吗?”。


@Terdon-看到这个答案,让我知道您的想法。
slm

+1我认为这是为解决此类任务而编程的Unix工具
miracle173 2013年

为什么不使用-e“”而不是-e“ N / A”。这行不通(我无法尝试)吗?
miracle173

@ miracle173-一定要使用空格作为arg。确实也可以。我选择了不适用,因此很明显它在做什么。
slm

2
@terdon-是的,这仍然是一个有趣的问题,喜欢一起工作,希望我们也可以一起解决一些将来的问题。我仍然认为此答案将对网站有用,因为我找不到很多极端的例子,join因此现在互联网上已经有了。8
slm

4

应该可以使用,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

这很好。非常感谢-非常感谢您在代码中说明文字。
Staffan Scherloff 2013年

@StaffanScherloff不客气。如果这回答了您的问题,请将其标记为已接受,以便将该问题标记为已回答。
terdon

@StaffanScherloff再三考虑,接受awk,好多了。– terdon 5分钟前
terdon

@terdon-您是否再使用join对此大惊小怪?好像要走的路,以前从未使用过它的-o功能,没有像我期望的那样工作。
slm

@slm没有,我在想的某种组合的-o-e,但不能让它的打印过的文件2的条目行。祝你好运,我想知道是否有可能。
terdon

3

巴什会做。

#!/usr/bin/env bash

# ### create a psuedo hash of icao locator id's
# read each line into an array
while read -a line; do
  # set icao_nnnnnn variable to the value
  declare "icao_${line[0]}"=${line[1]}
done <file2


# ### match up icao id's from file1
# read in file line at a time
while IFS=$'\n' read line; do
  # split the line into array
  read -a arr <<< "$line"
  # if the icao_nnnnnn variable exists, it will print out
  var="icao_${arr[0]}"
  printf "%-8s %s\n" "${!var}" "$line"
done <file1

请参阅此SO答案,以了解有关“哈希”的详细信息,Bash 4本机支持关联数组,但这应在3 + 4(也许是2?)中起作用。

您可能需要修剪掉file1中的行以获取格式。


2

这是使用join(+一些其他工具)并保留间距的简单方法。这两个文件似乎都按站号排序,因此不需要其他排序:

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

管道之前的部分与slm他的答案中使用的非常相似,因此我不再赘述。唯一的区别是,我正在使用-e " "-一个四空格字符串来替换缺少的输入字段,并且-o 2.2仅输出file2的第二个字段。
因此join -j1 -a1 -o 2.2 -e " " file1 file2产生了一个四字符宽的列(在下面看不到,但是在EK之后没有任何内容**和空行实际上是四个空格):

EKVG







生长因子
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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.