고유 식별자로 두 파일 결합


9

약 12900과 4400 개의 항목이있는 두 개의 파일이 있는데, 결합하고 싶습니다. 파일에는 전 세계 모든 육상 기상 관측소에 대한 위치 정보가 포함되어 있습니다. 가장 큰 파일은 격주로 업데이트되며 1 년에 한 번 정도 더 작아집니다. 원본 파일은 여기 ( http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htmhttp://weather.rap.ucar.edu/surface/ 에서 찾을 수 있습니다 . station.txt ). 내가 가지고있는 파일은 이미 awk, sed 및 bash 스크립트를 혼합하여 조작했습니다. 파일을 사용하여 Unidata에서 무료로 사용할 수있는 GEMPAK 패키지를 사용하여 데이터를 시각화합니다. 가장 큰 파일은 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

답변:


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

몇 가지 기본 기술 만 갖춘 우아한 솔루션입니다. 감사합니다!
Staffan Scherloff

프로그램은 작업을 시작하기 전에 하나의 파일을 메모리로 읽습니다. 파일이 커지면 성능이 크게 저하 될 수 있습니다. 더 큰 파일의 경우이 방법을 사용할 수 없습니다.
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는 file1의 file2와 일치하지 않는 행을 포함하도록 join에 지시합니다. 이것이 표시되도록 다음 줄을 구동하는 것입니다.

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

-1 1-2 1열, 주로 자신의 첫번째 열을 2 개 개의 파일에서 라인을 가입하는 말. 는 -o ...2 개 파일에서 열이 표시되는 순서대로 할 말하고있다.

-e "N/A"에 의해 빈 것으로 간주 필드에 인쇄 할 자리 표시 자 값으로 문자열 "N / A"를 사용 말한다 join.

마지막 두 인수는이 개 파일을 먹이, file1& file2로 정렬로 명령을 가입 할 수 있습니다.

이 작업은 진행중인 작업 join이므로 명령을 사용하여 이러한 유형의 문제를 해결하는 방법을 보여 주려고 노력 하고 있습니다. 이것이 의도 된 문제 유형 인 것 같습니다.

뛰어난 문제

  1. 세번째 열

    주요한 것은 1 단어와 2 단어 값이 혼합되어 있기 때문에 3 번째 열과 경쟁하는 방법입니다. 이것은 주요 걸림돌처럼 보이며 join나는 그 길을 알아낼 수 없습니다. 모든 안내를 부탁드립니다.

  2. 간격

    원래의 모든 간격이 없어 join지고 둘 중 하나를 유지할 방법이 없습니다. 따라서 join이러한 유형의 문제를 처리하는 올바른 방법이 아닐 수도 있습니다.

  3. 그래도 작동하는 것 같습니까?

    많은 명령 행으로 굽힘 후 일반적인 솔루션이 그것이 적어도 부분적 일 수있는 것처럼이이 솔루션의 핵심에 사용할 수 있도록, 보인다, 다음과 같은 다른 도구를 사용합니다 않도록 존재 awk하고 sed그것을 청소하기 . "하지만 당신이 그것을 awk& sed어떤 방법으로 청소하는 경우, 당신은 그들을 직접 사용할 수도 있습니다?"


@Terdon-이 답변을 참조하십시오.
slm

하나는 그 같은 작업 작업을 해결하기 위해 프로그램 된 유닉스 도구 생각
miracle173

왜 -e "N / A"대신 -e ""가 아닌가? 작동하지 않습니까 (시도 할 수 없습니다)?
miracle173

@ miracle173-꼭 공간을 인수로 사용하십시오. 작동합니다. 나는 N / A를 선택하여 그것이 무엇을하고 있는지 분명히했습니다.
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

@StaffanScherloff 천만에요. 이것이 귀하의 질문에 대한 답변 인 경우, 질문이 답변 된 것으로 표시 될 수 있도록 승인 된 것으로 표시하십시오.
terdon

두 번째 생각에 @StaffanScherloff, awk 하나를 받아들이면 훨씬 좋습니다. – terdon 5 분 전
terdon

@ terdon-더 이상 join을 사용 하여이 소란을 피우셨습니까? 갈 길처럼 보였고, 전에는 그 -o기능을 사용하지 않았으며 , 예상대로 작동하지 않았습니다.
slm

@ slm 아니오, 나는 어떤 조합을 생각 -o하고 -e있었지만 file2에 항목이없는 행을 인쇄 할 수 없었습니다. 행운을 빕니다, 가능하다면 알고 싶습니다.
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출력 파일 2 만 2 필드에
따라서 join -j1 -a1 -o 2.2 -e " " file1 file2생성하는 네 숯불 넓은 열 (이 아래에 보이지 않는하지만 아무것도 EK 후이 없다가 ** 및 빈 줄은 실제로 네 개의 공백입니다.)

EKVG







EKGF
EKTS

EKYT



EKSN

그런 다음 paste공백을 구분 기호로 사용하여 file1에서 cut처음 5자인 | paste -d' ' - <(cut -c6- file1)
End 결과 를 얻습니다 .

         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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.