특정 필드를 조작하는 방법은 무엇입니까?

특정 필드를 조작하는 방법은 무엇입니까?

CSV 파일이 있습니다. 줄 번호는 최대 25개입니다.

제목시간(UTC)(1번째 필드), 위도(2번째 필드), 경도(3번째 필드), 깊이(4번째 필드), mag(5번째 필드), 장소(14번째 필드) 등입니다.

샘플 데이터

2019-12-10T21:58:28.816Z 35.488 26.4157 57.32 5.4 35km NNE of Palaikastron, Greece


2019-12-11T11:54:27.670Z 18.6158 -67.2838 85 2.85 23km NW of San Antonio, Puerto Rico

먼저 내장 변수 fieldwith를 포함하여 제목을 삽입하고 싶습니다. 둘째, 나는 전환하고 싶다시간(UTC)에게UTC+03:00제목의 날짜와 시간을 구분하고 날짜 형식을 변경할 수 있습니다. 세 번째로 ' 사이의 일치 항목을 추출하고 싶습니다.~의'라는 말과반점14번째 필드의 국가 이름 앞에.

제목으로 원하는 출력:

Date Time Latitude Longitude Depth Mag Place

원하는 출력:

11.12.2019 00:58:28 35.488 26.4157 57.32 5.4 Palaikastron


11.12.2019 14:54:27 18.6158 -67.2838 85 2.85 San Antonio
time,latitude,longitude,depth,mag,magType,nst,gap,dmin,rms,net,id,updated,place,type,horizontalError,depthError,magError,magNst,status,locationSource,magSource
2019-12-06T13:04:46.931Z,-15.2838,-175.1193,10,6,mww,,50,3.512,0.81,us,us60006n19,2019-12-07T13:11:48.228Z,"164km WNW of Hihifo, Tonga",earthquake,8.4,1.9,0.08,15,reviewed,us,us
2019-12-04T20:10:03.614Z,-19.0515,169.5628,266,6,mww,,21,2.812,0.82,us,us60006m2j,2019-12-05T23:44:01.300Z,"63km NNE of Isangel, Vanuatu",earthquake,7.6,1.9,0.037,71,reviewed,us,us
2019-12-03T08:46:36.374Z,-18.5597,-70.6504,32.44,6,mww,,112,0.31,1.4,us,us70006fh7,2019-12-05T08:07:29.617Z,"37km WSW of Arica, Chile",earthquake,6.2,7.8,0.069,20,reviewed,us,us
2019-12-02T05:01:54.693Z,51.3218,-178.2425,27.33,6,mww,,104,0.862,0.97,us,us70006f6d,2019-12-07T02:09:55.119Z,"60km E of Amatignak Island, Alaska",earthquake,6.7,4.2,0.066,22,reviewed,us,us
2019-11-27T07:23:42.552Z,35.7272,23.2673,71.76,6,mww,,23,1.394,1.16,us,us70006dlt,2019-12-03T23:18:27.456Z,"41km NW of Platanos, Greece",earthquake,5.8,5.4,0.046,46,reviewed,us,us
2019-11-26T02:54:12.594Z,41.5112,19.5151,20,6.4,mww,,17,0.937,0.58,us,us70006d0m,2019-12-09T15:46:11.689Z,"16km WSW of Mamurras, Albania",earthquake,3.5,1.8,0.037,72,reviewed,us,us
2019-11-24T00:54:01.052Z,51.3809,-175.5108,20,6.3,mww,,22,0.658,0.95,us,us70006cb6,2019-12-10T01:04:03.731Z,"96km SE of Adak, Alaska",earthquake,3.9,1.8,0.05,38,reviewed,us,us
2019-11-23T12:11:16.261Z,1.6286,132.7854,10,6.1,mww,,38,4.549,1.1,us,us70006c6w,2019-11-25T21:00:33.040Z,"Papua region, Indonesia",earthquake,7.8,1.8,0.061,26,reviewed,us,us
2019-11-20T23:50:43.955Z,19.4533,101.3558,10,6.2,mww,,15,2.366,0.62,us,us70006ara,2019-12-04T05:52:37.313Z,"32km ESE of Chaloem Phra Kiat, Thailand",earthquake,6.4,1.7,0.049,40,reviewed,us,us

한 가지만 성공하면 다른 일은 하지 않습니다.나에게는 도전이다. 안내해주세요, 제발. 한편으로 awk는 사용하기가 이해하기 어렵습니다. 반면에 awk시간 함수는 매우 유용합니다. 나는 지금 너무 혼란스럽다. 내가 무엇을 시도해도 잘 되지 않습니다.

답변1

awk를 배우는 것은 훌륭한 목표이지만 실제 CSV 파일(특히 이스케이프 또는 따옴표로 묶인 구분 기호가 포함될 수 있는 필드)을 구문 분석하기 위한 기본 제공 메커니즘이 없으며 시간 함수는 GNU 전용이며 이식 가능하지 않습니다.

이러한 이유로 Perl 사용을 고려할 수 있습니다.텍스트::CSV모듈), Python - 또는 이런 종류의 작업에서 제가 현재 가장 좋아하는 것은밀러. 진정한 CSV 구문 분석을 제공할 뿐만 아니라 적절한 strptime기능도 제공하는 반면, GNU awk를 사용하더라도 인수 mktime를 수동으로 구문 분석하고 조합해야 합니다 datespec.

예를 들어 Miller에서는 다음을 수행할 수 있습니다.

mlr --csv \
  put -S '
    s = strptime($time,"%Y-%m-%dT%H:%M:%SZ") + 3*3600; 
    $date = strftime(s,"%d.%m.%Y"); 
    $time = strftime(s,"%H:%M:%S"); 
    $place =~ "(.* of |)([^,]*),(.*)$" { $place = "\2" }
  ' then cut -o -f date,time,latitude,longitude,depth,mag,place input.csv

공백으로 구분된 출력 열을 원하는 경우 ("예쁘게 인쇄된" 표 형식 출력 - 헤더 포함) 또는 (간단한 공백으로 구분된 출력) --csv로 변경하세요.--icsv --opprint--icsv --onidx

전.

$ mlr --icsv --opprint   put -S '
    s = strptime($time,"%Y-%m-%dT%H:%M:%SZ") + 3*3600; 
    $date = strftime(s,"%d.%m.%Y"); 
    $time = strftime(s,"%H:%M:%S"); 
    $place =~ "(.* of |)([^,]*),(.*)$" { $place = "\2" }
  ' then cut -o -f date,time,latitude,longitude,depth,mag,place input.csv
date       time     latitude longitude depth mag place
06.12.2019 16:04:46 -15.2838 -175.1193 10    6   Hihifo
04.12.2019 23:10:03 -19.0515 169.5628  266   6   Isangel
03.12.2019 11:46:36 -18.5597 -70.6504  32.44 6   Arica
02.12.2019 08:01:54 51.3218  -178.2425 27.33 6   Amatignak Island
27.11.2019 10:23:42 35.7272  23.2673   71.76 6   Platanos
26.11.2019 05:54:12 41.5112  19.5151   20    6.4 Mamurras
24.11.2019 03:54:01 51.3809  -175.5108 20    6.3 Adak
23.11.2019 15:11:16 1.6286   132.7854  10    6.1 Papua region
21.11.2019 02:50:43 19.4533  101.3558  10    6.2 Chaloem Phra Kiat

Miller는 Ubuntu universe저장소에서 사용할 수 있습니다.

답변2

먼저, 포함된 쉼표를 더 잘 처리하기 위해 CSV 입력을 사전 처리해야 합니다. 그런 다음 AWK를 기능적 덩어리로 나눕니다.

$ cat preprocess.sed
#!/bin/sed -f
:start   # loop back to here
/"/{  # for any line that has a double quote
  h   # copy to the hold buffer
  s/[^"]*"\([^"]*\).*/\1/  # what is between the first pair of dquotes
  s/,/@@/g    # replace comma with '@@'
  G   # append the hold buffer to the pattern buffer
      # so we get what was in dqoutes followed by a newline followed by the
      # original line
  s/\(.*\)\n\([^"]*\)"\([^"]*\)"\(.*\)/\2\1\4/
      # replace the unquoted part with what was there
  t start   # go back to 'start'
}

".*,.*"이는 를 으로 대체하여 .*@@.*AWK를 더 쉽게 만듭니다.

날짜만 새 시간대로 변경하려면 첫 번째 줄을 바꾸세요.

$ cat change_date.sh
#!/bin/sh
userTZ="${1:-UTC+3}"
sed 's/,/ /' |
    while read datestr rest; do
        if [ "${datestr}" = time ]; then
            newdate="${datestr}"
        else
            newdate=$(TZ=${userTZ} date -d "${datestr}" "+%d %m %Y %H:%M:%S")
        fi
        echo "${newdate}:${rest}"

    done

AWK 스크립트는 다음과 같습니다:

$ cat reformat.awk
#!/bin/awk  -f
BEGIN {IFS=","}  # comma separated fields
NR==1 {print; next;}  # print the header and do nothing more with it
{   # get just the "town" from the place field
    sub(/.* of /,"",$14)  # strip up to the " of "
    sub(/@@ .*/,"",$14)   # strip after the embedded comma (now '@@')
}
{
    printf("%s %8.3f %8.3f %8.3fs %8.3f %s\n", $1, $2, $3, $4, $5, $14)
}

둘 다 실행 가능하고 실행되는지 확인하십시오.preprocess.sed sample.csv | change_date.sh | reformat.awk

또는 한 줄에:

sed ':start;/"/{;h;s/[^"]*"\([^"]*\).*/\1/;s/,/@@/g;G;s/\(.*\)\n\([^"]*\)"\([^"]*\)"\(.*\)/\2\1\4/;t start;};s/,/ /' test.csv | while read datestr rest; do if [ "$datestr" = "time" ]; then newdate="${datestr}"; else newdate=$(TZ=UTC+3 date -d "$datestr" "+%d %m %Y %H:%M:%S"); fi; echo "${newdate},${rest}"; done | awk -F, 'NR==1 {print;next} {sub(/.* of /,"",$14);sub(/@@ .*/,"",$14)} {printf("%s %8.3f %8.3f %8.3fs %8.3f %s\n", $1, $2, $3, $4, $5, $14)}'

관련 정보