세부 정보를 얻고 이를 가로 형식으로 변환하려면 어떻게 해야 합니까?
각 레코드는 Couse
. Couse는 비어 있거나 null이 아닙니다.
참고: 4개의 헤더에는 이름, 도시, 나이, 회사 등의 데이터가 포함됩니다.
두 번째 레코드가 표시되면 "Name": "" ->가 누락되어 대신 null이어야 하며 나머지는 다음과 같이 파이프로 구분되어 추가됩니다. null | Ors | 11 | MB
내 데모.txt 파일에는 다음 데이터가 있습니다.
"Name":"asxadadad ,aaf dsf"
"City":"Mum"
"Age":"23"
"Couse":"BBS"
"City":"Ors"
"Age":"11"
"Couse":"MB"
"Name":"adad sf"
"City":"Kol"
"Age":"21"
"Couse":"BB"
"Name":"pqr"
"Age":"21"
"Couse":"NN"
예상 출력:
asxadadad ,aaf dsf | Mum | 23 | BBS
null | Ors | 11 | MB
adad sf | Kol | 21 | BB
pqr | null | 21 | NN
다음 코드를 시도했지만 내 논리에 맞지 않습니다.
counter=0
var_0='Couse'
while read -r line
echo "$line"
counter=$(( counter + 1 ))
var_1=`echo "$line" | grep -oh "Couse"`
if [ $var_0 == $var_1 ]
then
head -$counter demo.txt > temp.txt
sed -i '1,$counter' demo.txt
counter = 0
else
echo "No thing to do"
fi
done < demo.txt
답변1
모든 Unix 시스템의 모든 쉘에서 awk를 사용하십시오.
$ cat tst.awk
BEGIN {
numTags = split("Name City Age Couse",nums2tags)
for (tagNr=1; tagNr<=numTags; tagNr++) {
tag = nums2tags[tagNr]
tags2nums[tag] = tagNr
wids[tagNr] = ( length(tag) > length("null") ? length(tag) : length("null") )
}
OFS=" | "
}
(NR==1) || (prevTag=="Couse") {
numRecs++
}
{
gsub(/^"|"$/,"")
tag = val = $0
sub(/".*/,"",tag)
sub(/[^"]+":"/,"",val)
tagNr = tags2nums[tag]
vals[numRecs,tagNr] = val
wid = length(val)
wids[tagNr] = ( wid > wids[tagNr] ? wid : wids[tagNr] )
prevTag = tag
}
END {
# Uncomment these 3 lines if youd like a header line printed:
# for (tagNr=1; tagNr<=numTags; tagNr++) {
# printf "%-*s%s", wids[tagNr], nums2tags[tagNr], (tagNr<numTags ? OFS : ORS)
# }
for (recNr=1; recNr<=numRecs; recNr++) {
for (tagNr=1; tagNr<=numTags; tagNr++) {
val = ( (recNr,tagNr) in vals ? vals[recNr,tagNr] : "null" )
printf "%-*s%s", wids[tagNr], val, (tagNr<numTags ? OFS : ORS)
}
}
}
$ awk -f tst.awk file
asxadadad ,aaf dsf | Mum | 23 | BBS
null | Ors | 11 | MB
adad sf | Kol | 21 | BB
pqr | null | 21 | NN
또는 하드코드된 레이블 목록(필드/열 이름)을 사용하지 않으려는 경우:
$ cat tst.awk
BEGIN { OFS=" | " }
(NR==1) || (prevTag=="Couse") {
numRecs++
}
{
gsub(/^"|"$/,"")
tag = val = $0
sub(/".*/,"",tag)
sub(/[^"]+":"/,"",val)
if ( !(tag in tags2nums) ) {
tagNr = ++numTags
tags2nums[tag] = tagNr
nums2tags[tagNr] = tag
wids[tagNr] = ( length(tag) > length("null") ? length(tag) : length("null") )
}
tagNr = tags2nums[tag]
vals[numRecs,tagNr] = val
wid = length(val)
wids[tagNr] = ( wid > wids[tagNr] ? wid : wids[tagNr] )
prevTag = tag
}
END {
for (tagNr=1; tagNr<=numTags; tagNr++) {
printf "%-*s%s", wids[tagNr], nums2tags[tagNr], (tagNr<numTags ? OFS : ORS)
}
for (recNr=1; recNr<=numRecs; recNr++) {
for (tagNr=1; tagNr<=numTags; tagNr++) {
val = ( (recNr,tagNr) in vals ? vals[recNr,tagNr] : "null" )
printf "%-*s%s", wids[tagNr], val, (tagNr<numTags ? OFS : ORS)
}
}
}
$ awk -f tst.awk file
Name | City | Age | Couse
asxadadad ,aaf dsf | Mum | 23 | BBS
null | Ors | 11 | MB
adad sf | Kol | 21 | BB
pqr | null | 21 | NN
두 번째 스크립트 출력의 열 순서는 이러한 레이블이 입력에 나타나는 순서입니다. 따라서 모든 레이블이 나타나는 순서대로 표시되지 않는 한 값을 식별하기 위해 헤더 행이 필요합니다. 원하는 입력 그들은 출력합니다.
답변2
펄에서. 그것이 무엇인지/어떻게 작동하는지에 대해 더 많은 설명을 추가하고 싶지만 코드의 주석이 모든 것을 다루고 있다고 생각합니다.
#!/usr/bin/perl
use strict;
my @people; # Array-of-Arrays (AoA) to hold each record
my %person; # hash to hold the current record as it's being read in.
# list of valid field names, in the order you want them printed
my @names = qw(Name City Age Couse);
my $end_key = 'Couse';
# build a regex from the valid names
my $names = join('|',@names);
my $names_re = qr/^(?:$names)$/;
# Initialise field widths, with a minimum of 4 (for 'null').
my %widths = map {$_ => (length > 4 ? length : 4) } @names;
while(<>) {
chomp;
s/^"|"$//g; # strip leading and trailing quotes
my ($key,$val) = split /"?:"?/; # split on :, with optional quotes.
if ($key =~ m/$names_re/) {
$widths{$key} = length($val) if ($widths{$key} < length($val) );
$person{$key} = $val;
if ($key eq $end_key) {
# push an array into the @people array, containing the values of
# the valid fields, in order. Use null as the default value
# if any field is empty/undefined.
push @people, [ map { $person{$_} || 'null' } @names ];
%person = ();
};
} else {
print STDERR "Error on input line $.: unrecognised data\n";
};
};
# build a printf format string, using the longest width of each field.
my $fmt = join(' | ', map { "%-$widths{$_}s" } @names) . "\n";
# optional header line, comment out if not wanted
printf $fmt, @names;
# optional ruler line, comment out if not wanted
print join('-|-', map { '-' x $widths{$_} } @names) . "\n";
foreach my $p (@people) {
printf $fmt, @{ $p };
}
예를 들어 다른 이름으로 저장 columns.pl
하고 chmod +x를 사용하여 실행 가능하게 만듭니다.
산출:
$ chmod +x columns.pl
$ ./columns.pl demo.txt
Name | City | Age | Couse
--------------------|------|------|------
asxadadad ,aaf dsf | Mum | 23 | BBS
null | Ors | 11 | MB
adad sf | Kol | 21 | BB
pqr | null | 21 | NN
답변3
태그가 레코드 블록에 있는 위치와 별도로 작동하고 인쇄할 때 입력의 태그 순서와 마지막 태그를 따르는 짧은 GNU awk 호환(정규 표현식으로 정의된 RS용) 솔루션입니다.Couse
레코드 끝 식별자입니다.
<infile awk -F'\n' -v tags='Name,City,Age,Couse' '
BEGIN{ tagsNum=split(tags, tgs, ","); RS="\n?\""tgs[tagsNum]"\":[^\n]*\n" }
function tbl(tag, field) {
if(index(field, "\""tag"\"")==1 && !key[tag]++ || field==RT){
gsub(/(^[^:]*:"|"\n?)/, "", field)
key[tag]=field
}
}
{ for(i=1; i<=NF; i++){ for(k in tgs) tbl(tgs[k], $i); tbl(RT, RT) }
for(i=1; i<tagsNum; i++)
printf "%s", (key[tgs[i]]!=""? key[tgs[i]]:"null") OFS; print key[RT]
delete key
}' OFS='@|' |column -ts'@'
배열의 각 태그 이름에 대해 이 함수를 호출하여 tgs
해당 태그가 나타나는 관련 필드를 일치시켜 해당 값을 다시 채운 다음 각 레코드에 대해 인쇄합니다.null
값이 없으면 배열을 삭제하고 다음 블록에서도 동일한 작업을 수행합니다.
우리가 사용하는 column -ts'@'
출력을 표로 만들기 위해 @
문자는 에서 옵니다 OFS='@|'
. 이 방법을 사용하면 column
해당 문자를 기반으로 출력 필드가 조정되고 나중에 출력에서 제거되므로 해당 @
문자가 입력 데이터에 없어야 한다고 가정합니다(가능한 경우). , 다른 문자로 바꿉니다). 당신이 가지고 있다면column
util-linux
포장 에서, 변경할 수 있습니다OFS='@|' |column -ts'@'
도착하다OFS='|' |column -t -s'|' -o' | '
.
asxadadad ,aaf dsf |Mum |23 |BBS
null |Ors |11 |MB
adad sf |Kol |21 |BB
pqr |null |21 |NN
답변4
데이터는 원본 JSON 문서 중 하나에서 수정된 것처럼 보입니다.
JSON 문서 구조를 복원해 보겠습니다.
- 문서의
[{
시작과 끝 부분에 추가됨}]
},{
정확한 문자열로 시작하는 각 줄의 끝에 추가됩니다"Couse"
(마지막 줄은 아님).- 달리 수정되지 않는 각 줄의 끝에 쉼표를 추가합니다(즉, 줄 끝에 여전히 큰따옴표가 있습니다).
sed -e '1 s/^/[{/' -e '$ s/$/}]/' \
-e '/^"Couse"/ { $! s/$/},{/; }' \
-e 's/"$/&,/' file
예쁘게 인쇄하면 문서가 다음과 같이 바뀔 것입니다.
[
{
"Name": "asxadadad ,aaf dsf",
"City": "Mum",
"Age": "23",
"Couse": "BBS"
},
{
"City": "Ors",
"Age": "11",
"Couse": "MB"
},
{
"Name": "adad sf",
"City": "Kol",
"Age": "21",
"Couse": "BB"
},
{
"Name": "pqr",
"Age": "21",
"Couse": "NN"
}
]
그런 다음 이를 CSV로 파이프할 수 있습니다 jq
(일부 열 헤더를 추가하고 null 값을 문자열로 대체 null
).
jq -r ' [ "Name", "City", "Age", "Couse" ],
(.[] | [ .Name, .City, .Age, .Couse ]) |
map(. // "null") | @csv'
이것은 생성됩니다
"Name","City","Age","Couse"
"asxadadad ,aaf dsf","Mum","23","BBS"
"null","Ors","11","MB"
"adad sf","Kol","21","BB"
"pqr","null","21","NN"
그런 다음 툴킷 csvlook
의 내용을 사용하여 csvkit
보기 좋은 테이블을 생성할 수 있습니다.
최종 파이프라인은 다음과 같습니다.
sed -e '1 s/^/[{/' -e '$ s/$/}]/' \
-e '/^"Couse"/ { $! s/$/},{/; }' \
-e 's/"$/&,/' file |
jq -r ' [ "Name", "City", "Age", "Couse" ],
(.[] | [ .Name, .City, .Age, .Couse ]) |
map(. // "null") | @csv' |
csvlook --blanks
문자열을 그대로 유지하기 위해 csvlook
해당 옵션을 사용합니다 (그렇지 않으면 문자열이 삭제됩니다).--blanks
null
결과는 다음과 같습니다
| Name | City | Age | Couse |
| ------------------- | ---- | --- | ----- |
| asxadadad ,aaf dsf | Mum | 23 | BBS |
| null | Ors | 11 | MB |
| adad sf | Kol | 21 | BB |
| pqr | null | 21 | NN |
또는 마크다운으로 렌더링합니다.
이름 | 도시 | 나이 | 쿠스 |
---|---|---|---|
아스다다드, aaf dsf | 어머니 | 이십 삼 | 법정 |
유효하지 않은 | 오르스 | 11 | MB |
아다드 SF | 서양 평지 | 이십 일 | BB |
퓨 | 유효하지 않은 | 이십 일 | 신경망 |