Tham gia hai tệp với mã định danh duy nhất


9

Tôi có hai tệp với khoảng 12900 và 4400 mục tương ứng mà tôi muốn tham gia. Các tập tin chứa thông tin vị trí cho tất cả các trạm quan sát thời tiết trên đất liền trên toàn cầu. Tệp lớn nhất được cập nhật hai tuần một lần và nhỏ hơn mỗi năm một lần. Các tập tin gốc có thể được tìm thấy ở đây ( http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htmlmhttp://weather.rap.ucar.edu/surface/ trạm.txt ). Các tập tin tôi đã bị tôi thao túng với một số kịch bản hỗn hợp awk, sed và bash. Tôi sử dụng các tệp để trực quan hóa dữ liệu bằng gói GEMPAK, được cung cấp miễn phí từ Unidata. Tệp lớn nhất sẽ hoạt động với GEMPAK, nhưng không phải với khả năng đầy đủ của nó. Đối với điều này một tham gia là cần thiết.

Tệp 1 chứa thông tin vị trí cho các trạm quan sát thời tiết, trong đó 6 chữ số đầu tiên là mã định danh trạm duy nhất. Các tham số khác nhau (số trạm, tên trạm, mã quốc gia, kinh độ vĩ độ và độ cao của trạm) chỉ được xác định bởi vị trí của nó trong dòng, tức là không có tab.

         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

Tệp 2 chứa mã định danh duy nhất trong Tệp 1 và mã định danh thứ hai, 4 ký tự (định vị 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

Tôi muốn tham gia hai tệp, để tệp kết quả sẽ có mã định danh 4 ký tự ở 4 vị trí đầu tiên trong dòng, tức là mã định danh sẽ thay thế 4 khoảng trắng.

         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

Có thể hoàn thành nhiệm vụ này với một số tập lệnh bash và / hoặc awk không?


các tập tin được sắp xếp theo trường ID?
phép lạ173

Câu trả lời:


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

Một giải pháp tao nhã, mà tôi chỉ có một số kỹ năng cơ bản mới hiểu được. Cảm ơn bạn!
Staffan Scherloff

Chương trình đọc một tệp vào bộ nhớ trước khi bắt đầu hoạt động. Nếu các tập tin trở nên lớn hơn có thể làm giảm hiệu suất đáng kể. Đối với tệp lớn hơn, phương pháp này không thể được sử dụng.
phép lạ173

7

Một vài người trong chúng tôi muốn xem liệu chúng tôi có thể giải quyết vấn đề này joinchỉ bằng cách sử dụng . Đây là nỗ lực của tôi để làm điều đó. Vì nó hoạt động một phần @Terdon nợ tôi một bữa tối 8-).

Lệnh

$ 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)

Thí dụ

$ 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

Chi tiết

Ở trên đang sử dụng khá nhiều tùy chọn có sẵn để joinnói với ruột của tôi rằng chúng ta đang sử dụng sai, như trong một số cách của Frankenstein, nhưng tất cả chúng ta đều học ở đây, vì vậy, tôi đoán vậy.

Công tắc -a1cho biết tham gia bao gồm bất kỳ dòng nào không có kết quả khớp tương ứng từ tệp2 trong tệp1. Vì vậy, đây là những gì thúc đẩy các dòng này được hiển thị:

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

Các -1 1-2 1đang nói cột nào sẽ nối các dòng từ 2 tệp trên, chủ yếu là các cột đầu tiên của chúng. Có nghĩa -o ...là cột nào trong 2 tệp sẽ hiển thị và theo thứ tự nào.

Việc -e "N/A"nói sử dụng chuỗi "N / A" làm giá trị giữ chỗ để in cho các trường được coi là trống join.

2 đối số cuối cùng đang cung cấp cho 2 tệp file1và được file2sắp xếp vào lệnh nối.

Xin vui lòng, vì đây là một công việc đang tiến triển và chúng tôi đang cố gắng chứng minh cách một người sẽ giải quyết loại vấn đề này bằng cách sử dụng joinlệnh, vì đây dường như là loại vấn đề mà nó có nghĩa là gì.

Vấn đề nổi bật

  1. Cột thứ 3

    Vấn đề chính là làm thế nào để tranh luận với cột thứ 3 vì nó là sự pha trộn của 1 giá trị từ và 2 từ. Điều này có vẻ như là một trở ngại lớn joinvà tôi không thể tìm ra cách nào xung quanh nó. Bất kỳ hướng dẫn sẽ được đánh giá cao.

  2. khoảng cách

    Tất cả các khoảng cách ban đầu bị mất joinvà tôi cũng không thấy cách nào để giữ nó xung quanh. Vì vậy, joincó thể không phải là cách đúng đắn để giải quyết các loại vấn đề này.

  3. Có vẻ để làm việc mặc dù?

    Sau nhiều lần uốn cong với dòng lệnh, giải pháp chung là có, vì vậy có vẻ như nó có thể hoạt động ít nhất một phần, do đó, điều này có thể được sử dụng ở cốt lõi của giải pháp, sau đó sử dụng các công cụ khác như awksedđể làm sạch nó . Điều này đặt ra câu hỏi mặc dù: "Nếu bạn đang dọn dẹp nó bằng awk& bằng sedmọi cách, thì bạn có thể sử dụng chúng trực tiếp không?".


@Terdon - xem câu trả lời này, cho tôi biết bạn nghĩ gì.
slm

+1 Tôi nghĩ rằng đó là công cụ unix đã được lập trình để giải quyết một nhiệm vụ như vậy
miracle173

tại sao không -e "" thay vì -e "N / A". Điều này không hoạt động (tôi không thể thử nó)?
phép lạ173

@ miracle173 - bằng mọi cách, vâng, việc sử dụng một không gian như một đối số. cũng làm việc Tôi đã chọn N / A để biết rõ ràng nó đang làm gì.
slm

2
@terdon - vâng, đó cũng là một vấn đề thú vị, rất thích làm việc cùng nhau, hy vọng chúng ta có thể cùng nhau giải quyết một số vấn đề trong tương lai. Tôi vẫn nghĩ rằng câu trả lời này sẽ phục vụ một mục đích hữu ích trên trang web vì tôi không thể tìm thấy nhiều ví dụ cực đoan joinvì vậy bây giờ internet có cái này. 8-)
slm

4

Điều này thể được sử dụng joinnhưng tôi không thể tìm ra cách làm cho nó in không gian và các trường trống một cách chính xác. Dù sao, kịch bản Perl nhỏ này sẽ thực hiện thủ thuật:

#!/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; 
}

Lưu cái này foo.plvà chạy như sau:

$ 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

Điều này làm việc tuyệt vời. Rất cám ơn - Tôi thực sự đánh giá cao văn bản giải thích của bạn trong mã.
Staffan Scherloff

@StaffanScherloff bạn được chào đón. Nếu điều này trả lời câu hỏi của bạn, vui lòng đánh dấu nó là được chấp nhận để câu hỏi có thể được đánh dấu là đã trả lời.
terdon

@StaffanScherloff với ý nghĩ thứ hai, chấp nhận awk, nó tốt hơn nhiều. - terdon 5 phút trước
terdon

@terdon - Bạn có phiền phức với cái này nữa không bằng cách tham gia? Có vẻ như con đường để đi, không bao giờ sử dụng -otính năng của nó trước đây, không hoạt động như tôi mong đợi.
slm

@slm không, tôi đã nghĩ đến một số kết hợp -o-enhưng không thể lấy nó để in các dòng không có mục trong tệp2. Chúc may mắn, tôi sẽ quan tâm để biết nếu nó có thể.
terdon

3

Bash sẽ làm.

#!/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

Xem câu trả lời SO này để biết chi tiết về những gì đang xảy ra với "băm" Bash 4 hỗ trợ nguyên bản của mảng kết hợp, nhưng điều này sẽ hoạt động trong 3 + 4 (có thể là 2?)

Bạn có thể cần phải cắt bớt dòng từ tệp1 để có được định dạng của bạn.


2

Đây là một cách đơn giản để làm điều đó với join(+ một vài công cụ nữa) và giữ khoảng cách. Cả hai tệp dường như được sắp xếp theo số trạm nên không cần sắp xếp bổ sung:

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

Phần trước ống rất giống với slm được sử dụng trong câu trả lời của anh ấy nên tôi sẽ không xem lại nó nữa. Sự khác biệt duy nhất là tôi đang sử dụng -e " "- một chuỗi bốn không gian thay thế cho các trường đầu vào bị thiếu và -o 2.2chỉ xuất ra trường thứ 2 của tệp2
Vì vậy, join -j1 -a1 -o 2.2 -e " " file1 file2tạo ra một cột rộng bốn ký tự (không hiển thị bên dưới nhưng không có gì sau EK ** và các dòng trống thực sự là bốn khoảng trắng):

EKVG







EKGF
EKTS

EKYT



EKSN

sau đó chúng tôi paste(sử dụng khoảng trắng làm dấu phân cách) cho tệp1 từ đó chúng tôi có cut5 ký tự đầu tiên | paste -d' ' - <(cut -c6- file1)
Kết quả cuối cùng:

         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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.