programing

bash에서 CSV를 JSON으로 변환

fastcode 2023. 3. 16. 21:58
반응형

bash에서 CSV를 JSON으로 변환

CSV 파일을 JSON으로 변환하려고 합니다.

다음으로 2개의 샘플 행을 나타냅니다.

-21.3214077;55.4851413;Ruizia cordata
-21.3213078;55.4849803;Cossinia pinnata

다음과 같은 것을 받고 싶습니다.

"occurrences": [
                 {
                "position": [-21.3214077, 55.4851413],
                "taxo": {
                    "espece": "Ruizia cordata"
                 },
                 ...
             }]

제 대본은 다음과 같습니다.

    echo '"occurences": [ '

cat se.csv | while read -r line
  do
      IFS=';' read -r -a array <<< $line;
      echo -n -e '{ "position": [' ${array[0]}
      echo -n -e ',' ${array[1]} ']'
      echo -e ', "taxo": {"espece":"' ${array[2]} '"'
done
echo "]";

정말 이상한 결과가 나옵니다.

   "occurences": [ 
 ""position": [ -21.3214077, 55.4851413 ], "taxo": {"espece":" Ruizia cordata
 ""position": [ -21.3213078, 55.4849803 ], "taxo": {"espece":" Cossinia pinnata

내 코드에 무슨 문제가 있나요?

다음은 효과적인 파이톤 원라이너/스크립트입니다.

cat my.csv | python -c 'import csv, json, sys; print(json.dumps([dict(r) for r in csv.DictReader(sys.stdin)]))'

이 작업에 적합한 도구는 입니다.

jq -Rsn '
  {"occurrences":
    [inputs
     | . / "\n"
     | (.[] | select(length > 0) | . / ";") as $input
     | {"position": [$input[0], $input[1]], "taxo": {"espece": $input[2]}}]}
' <se.csv

출력은 다음과 같이 입력):

{
  "occurences": [
    {
      "position": [
        "-21.3214077",
        "55.4851413"
      ],
      "taxo": {
        "espece": "Ruizia cordata"
      }
    },
    {
      "position": [
        "-21.3213078",
        "55.4849803"
      ],
      "taxo": {
        "espece": "Cossinia pinnata"
      }
    }
  ]
}

덧붙여서, 원래의 스크립트의 버그가 적은 버전은 다음과 같습니다.

#!/usr/bin/env bash

items=( )
while IFS=';' read -r lat long pos _; do
  printf -v item '{ "position": [%s, %s], "taxo": {"espece": "%s"}}' "$lat" "$long" "$pos"
  items+=( "$item" )
done <se.csv

IFS=','
printf '{"occurrences": [%s]}\n' "${items[*]}"

주의:

  • 사용해도 전혀 의미가 없습니다.cat루프에 접속하는 것(및 접속하지 않는 타당한 이유)으로 리다이렉트(redirection)를 사용하고 있습니다.<) 파일을 루프의 stdin으로 직접 엽니다.
  • read는, 행선지 변수 리스트를 건네줄 수 있기 때문에, 어레이에 읽어 들일 필요가 없습니다(또는, 우선 문자열을 읽어 들인 후, 여기서부터 읽어 들일 필요가 없습니다)._(추가 컬럼을 더미 변수에 삽입함으로써) 마지막에는 추가 컬럼이 폐기됩니다._추가되는 것이 아니라)pos.
  • "${array[*]}"요소를 연결함으로써 문자열을 생성합니다.array의 캐릭터와 함께IFS따라서 필요한 경우에만 출력에 쉼표가 표시되도록 할 수 있습니다.
  • printf보다 우선하여 사용되다echo(스펙 자체의 「APPLICATION USAGE」섹션에서 설명한 바와 같이).
  • 이것은 문자열 연결을 통해 JSON을 생성하기 때문에 기본적으로 버그가 발생합니다.쓰지 마세요.

승인된 답변은 다음과 같습니다.jq를 참조해 주세요.이게 먹히 되는데jqExcel 또는 이와 유사한 툴에서 생성된 CSV로부터의 입력을 처리하지 않습니다.

foo,"bar,baz",gaz

jq에는 3이 아닌 4개의 필드가 표시되므로 잘못된 출력이 발생합니다.

하나의 옵션은 승인된 답변과 함께 쉼표 대신 탭으로 구분된 값을 사용하는 것입니다(입력 데이터에 탭이 포함되지 않은 경우).

또 하나의 옵션은 툴을 조합하여 각 부품에 최적인 툴을 사용하는 것입니다.입력 내용을 읽어내 JSON으로 변환하기 위한 CSV 파서입니다.jqJSON을 타겟 포맷으로 변환합니다.

python 기반 csvkit은 CSV를 지능적으로 해석하며 툴과 함께 제공됩니다.csvjsonCSV를 JSON으로 변환하는 것이 훨씬 효율적입니다.그런 다음 jq를 통해 파이프로 연결하여 csvkit에 의한 플랫 JSON 출력을 타깃 형식으로 변환할 수 있습니다.

OP에서 제공하는 데이터를 사용하면 원하는 출력에 대해 다음과 같이 간단하게 수행할 수 있습니다.

csvjson --no-header-row  |
  jq '.[] | {occurrences: [{ position: [.a, .b], taxo: {espece: .c}}]}'

은 csvjson을 합니다.; 경우를 "json"으로 할당합니다.a,b , , , , 입니다.c.

CSV 파일에 쓰는 경우에도 마찬가지입니다.csvkit 또는 새로운 JSON을 통해 할 수 .in2csv.

John Kerl의 Miller 툴에는 다음과 같은 기능이 내장되어 있습니다.

mlr --c2j --jlistwrap cat INPUT.csv > OUTPUT.json

다음은 이 주제에 관한 기사입니다.https://infiniteundo.com/post/99336704013/convert-csv-to-json-with-jq

JQ를하지만 JQ를 사용하는 방법은 다릅니다.split() ★★★★★★★★★★★★★★★★★」map().

jq --slurp --raw-input \
   'split("\n") | .[1:] | map(split(";")) |
      map({
         "position": [.[0], .[1]],
         "taxo": {
             "espece": .[2]
          }
      })' \
  input.csv > output.json

하지만 분리기 탈출은 처리되지 않습니다.

에 jq가 inputs내장 필터(jq 1.5 이후 사용 가능)는 -s 명령줄 옵션보다 사용하는 것이 좋습니다.

에서는 어떤 기기서 here here using를 사용한 이 나와 .inputs이 솔루션도 가변성이 없습니다.

{"occurrences":
  [inputs
   | select(length > 0)
   | . / ";"
   | {"position": [.[0], .[1]], 
      "taxo": {"espece": .[2]}} ]}

SSV, CSV 등

물론 위의 예에서는 각 줄에 세미콜론으로 구분된 필드가 있으며 CSV 파일과 관련된 복잡성은 없다고 가정합니다.

입력에 단일 문자로 엄격하게 구분된 필드가 있는 경우 jq는 해당 필드를 처리하는 데 문제가 없습니다.그렇지 않으면 jq가 직접 처리할 수 있는 TSV(탭 구분 값) 형식으로 안정적으로 변환할 수 있는 도구를 사용하는 것이 가장 좋습니다.

다음은 Ruby 원라이너 솔루션입니다.

ruby -r json -r csv -e 'puts CSV.parse(STDIN, headers:true).map(&:to_h).to_json' < INPUT.csv 

다음은 Miller(mlr)를 사용하는 다른 방법입니다.

mlr --implicit-csv-header --icsv --fs ';' --ojson label position,taxo,espece example.csv

그 결과 다음과 같은 결과가 나올 것이다.

[
{
  "position": -21.3214077,
  "taxo": 55.4851413,
  "espece": "Ruizia cordata"
},
{
  "position": -21.3213078,
  "taxo": 55.4849803,
  "espece": "Cossinia pinnata"
}
]

'만나서'를 사용하는 --ojsonl JSON ( 행)은 JSON 을

{"position": -21.3214077, "taxo": 55.4851413, "espece": "Ruizia cordata"}
{"position": -21.3213078, "taxo": 55.4849803, "espece": "Cossinia pinnata"}

냐하 because는jq솔루션에서는 CSV 이스케이프, 첫 줄 열 이름, 주석 출력 행 및 기타 일반적인 CSV "기능"은 처리하지 않습니다.CSV Cruncher 도구를 확장하여 CSV를 읽고 JSON으로 쓸 수 있도록 했습니다.'Bash'는 아니지만jq

주로 CSV-as-SQL 처리 앱이기 때문에 완전히 간단한 것은 아니지만 다음과 같은 이점이 있습니다.

./crunch -in myfile.csv -out output.csv --json -sql 'SELECT * FROM myfile'

또, 회선 마다 JSON 오브젝트 또는 적절한 JSON 어레이로서 출력할 수도 있습니다.메뉴얼을 참조해 주세요.

베타 품질이므로 피드백이나 풀 요청은 모두 환영합니다.

완벽함을 위해 Xidel과 XQuery 마법도 이 작업을 수행할 수 있습니다.

xidel -s input.csv --xquery '
  {
    "occurrences":for $x in tokenize($raw,"\n") let $a:=tokenize($x,";") return {
      "position":[
        $a[1],
        $a[2]
      ],
      "taxo":{
        "espece":$a[3]
      }
    }
  }
'
{
  "occurrences": [
    {
      "position": ["-21.3214077", "55.4851413"],
      "taxo": {
        "espece": "Ruizia cordata"
      }
    },
    {
      "position": ["-21.3213078", "55.4849803"],
      "taxo": {
        "espece": "Cossinia pinnata"
      }
    }
  ]
}

미쳐가고 싶다면 jq를 사용하여 파서를 작성할 수 있습니다. 그럼 제가 하고 있는 은, 그 할 수 이 구현입니다.@csv필터링을 실시합니다..jq.jq.

def do_if(pred; update):
    if pred then update else . end;
def _parse_delimited($_delim; $_quot; $_nl; $_skip):
    [($_delim, $_quot, $_nl, $_skip)|explode[]] as [$delim, $quot, $nl, $skip] |
    [0,1,2,3,4,5] as [$s_start,$s_next_value,$s_read_value,$s_read_quoted,$s_escape,$s_final] |
    def _append($arr; $value):
        $arr + [$value];
    def _do_start($c):
        if $c == $nl then
            [$s_start, null, null, _append(.[3]; [""])]
        elif $c == $delim then
            [$s_next_value, null, [""], .[3]]
        elif $c == $quot then
            [$s_read_quoted, [], [], .[3]]
        else
            [$s_read_value, [$c], [], .[3]]
        end;
    def _do_next_value($c):
        if $c == $nl then
            [$s_start, null, null, _append(.[3]; _append(.[2]; ""))]
        elif $c == $delim then
            [$s_next_value, null, _append(.[2]; ""), .[3]]
        elif $c == $quot then
            [$s_read_quoted, [], .[2], .[3]]
        else
            [$s_read_value, [$c], .[2], .[3]]
        end;
    def _do_read_value($c):
        if $c == $nl then
            [$s_start, null, null, _append(.[3]; _append(.[2]; .[1]|implode))]
        elif $c == $delim then
            [$s_next_value, null, _append(.[2]; .[1]|implode), .[3]]
        else
            [$s_read_value, _append(.[1]; $c), .[2], .[3]]
        end;
    def _do_read_quoted($c):
        if $c == $quot then
            [$s_escape, .[1], .[2], .[3]]
        else
            [$s_read_quoted, _append(.[1]; $c), .[2], .[3]]
        end;
    def _do_escape($c):
        if $c == $nl then
            [$s_start, null, null, _append(.[3]; _append(.[2]; .[1]|implode))]
        elif $c == $delim then
            [$s_next_value, null, _append(.[2]; .[1]|implode), .[3]]
        else
            [$s_read_quoted, _append(.[1]; $c), .[2], .[3]]
        end;
    def _do_final($c):
        .;
    def _do_finalize:
        if .[0] == $s_start then
            [$s_final, null, null, .[3]]
        elif .[0] == $s_next_value then
            [$s_final, null, null, _append(.[3]; [""])]
        elif .[0] == $s_read_value then
            [$s_final, null, null, _append(.[3]; _append(.[2]; .[1]|implode))]
        elif .[0] == $s_read_quoted then
            [$s_final, null, null, _append(.[3]; _append(.[2]; .[1]|implode))]
        elif .[0] == $s_escape then
            [$s_final, null, null, _append(.[3]; _append(.[2]; .[1]|implode))]
        else # .[0] == $s_final
            .
        end;
    reduce explode[] as $c (
        [$s_start,null,null,[]];
        do_if($c != $skip;
            if .[0] == $s_start then
                _do_start($c)
            elif .[0] == $s_next_value then
                _do_next_value($c)
            elif .[0] == $s_read_value then
                _do_read_value($c)
            elif .[0] == $s_read_quoted then
                _do_read_quoted($c)
            elif .[0] == $s_escape then
                _do_escape($c)
            else # .[0] == $s_final
                _do_final($c)
            end
        )
    )
    | _do_finalize[3][];
def parse_delimited($delim; $quot; $nl; $skip):
    _parse_delimited($delim; $quot; $nl; $skip);
def parse_delimited($delim; $quot; $nl):
    parse_delimited($delim; $quot; $nl; "\r");
def parse_delimited($delim; $quot):
    parse_delimited($delim; $quot; "\n");
def parse_delimited($delim):
    parse_delimited($delim; "\"");
def parse_csv:
    parse_delimited(",");

데이터의 경우 구분 기호를 세미콜론으로 변경할 수 있습니다.

$ cat se.csv
-21.3214077;55.4851413;Ruizia cordata
-21.3213078;55.4849803;Cossinia pinnata
$ jq -R 'parse_delimited(";")' se.csv
[
  "-21.3214077",
  "55.4851413",
  "Ruizia cordata"
]
[
  "-21.3213078",
  "55.4849803",
  "Cossinia pinnata"
]

이 방법은 대부분의 입력에서 한 번에 한 줄을 구문 분석할 때 올바르게 작동하지만 데이터에 리터럴 줄바꿈이 있는 경우 파일 전체를 문자열로 읽어야 합니다.

$ cat input.csv
Year,Make,Model,Description,Price
1997,Ford,E350,"ac, abs, moon",3000.00
1999,Chevy,"Venture ""Extended Edition""","",4900.00
1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00
1996,Jeep,Grand Cherokee,"MUST SELL!
air, moon roof, loaded",4799.00
$ jq -Rs 'parse_csv' input.csv
[
  "Year",
  "Make",
  "Model",
  "Description",
  "Price"
]
[
  "1997",
  "Ford",
  "E350",
  "ac, abs, moon",
  "3000.00"
]
[
  "1999",
  "Chevy",
  "Venture \"Extended Edition\"",
  "",
  "4900.00"
]
[
  "1999",
  "Chevy",
  "Venture \"Extended Edition, Very Large\"",
  "",
  "5000.00"
]
[
  "1996",
  "Jeep",
  "Grand Cherokee",
  "MUST SELL!\nair, moon roof, loaded",
  "4799.00"
]

CSV 파일이 큰 경우 인쇄하기 전에 전체 사전 구조를 메모리에 저장하지 않는 Jstaabs 응답 버전입니다.

import csv, json, sys

for r in csv.DictReader(sys.stdin):
    print(dict(r))

이 출력은 JSON 행 형식이 아닌 JSON 행 형식으로 출력됩니다.

다음은 적절한 JSON 어레이를 출력하는 버전입니다.단, 시간이 길어집니다.

import csv, json, sys

sys.stdout.write('[')

first = True
for r in csv.DictReader(sys.stdin):
    if not first:
        sys.stdout.write(',')
    first = False
    json.dump(dict(r), sys.stdout)

sys.stdout.write(']')

php one-liner 솔루션을 원하는 사용자:

php -r 'echo json_encode(array_map(''str_getcsv'', file(''file.csv'')));'

다른 딜리미터를 지정해야 하는 경우:

php -r 'echo json_encode(array_map(fn($line) => str_getcsv($line, '';''), file(''file.csv'')));'

json_decode를 사용할 때도 몇 가지 플래그를 사용할 수 있습니다.

https://www.php.net/manual/pt_BR/function.json-encode.php

언급URL : https://stackoverflow.com/questions/44780761/converting-csv-to-json-in-bash

반응형