;
다음 형식(4열, 로 구분 ) 의 큰 텍스트 파일(5m 줄)이 있습니다 .
문자열 1; 문자열 3;
이것처음 3개의 문자열(SHA1)이라는 단일 ID를 형성합니다.애플리케이션 ID(그래서 이렇게 단순화할 수 있습니다 :) appId; userId
. 두 번째 열(string2 또는 두 번째 부분애플리케이션 ID) 자체는 쉼표로 구분된 여러 부분으로 구성될 수 있습니다 ,
. 파일이 정렬되었습니다.
다음과 같이 각 애플리케이션에 대한 사용자 목록을 갖고 싶습니다.
입력하다문서:
app1, user1
app1, user2
app1, user3
app2, user1
산출문서:
app1: user1, user2, user3
app2: user1
"진짜"의 일부입력하다문서:
44a934ca4052b34e70f9cb03f3399c6f065becd0;bf038823f9633d25034220b9f10b68dd8c16d867;309;8ead5b3e0af5b948a6b09916bd271f18fe2678aa
44a934ca4052b34e70f9cb03f3399c6f065becd0;bf038823f9633d25034220b9f10b68dd8c16d867;309;a21245497cd0520818f8b14d6e405040f2fa8bc0
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16;337556fc485cd094684a72ed01536030bdfae5bb
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16;382f3aaa9a0347d3af9b35642d09421f9221ef7d
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16;396529e08c6f8a98a327ee28c38baaf5e7846d14
"진짜"산출파일은 다음과 같아야 합니다.
44a934ca4052b34e70f9cb03f3399c6f065becd0;bf038823f9633d25034220b9f10b68dd8c16d867;309:8ead5b3e0af5b948a6b09916bd271f18fe2678aa, a21245497cd0520818f8b14d6e405040f2fa8bc0
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16:337556fc485cd094684a72ed01536030bdfae5bb, 382f3aaa9a0347d3af9b35642d09421f9221ef7d, 396529e08c6f8a98a327ee28c38baaf5e7846d14
어떻게 해야 하나요?
편집하다:또한 각 애플리케이션에는 수천 명의 사용자가 있을 수 있는데, 대기열은 얼마나 길어질 수 있습니까? 줄 길이에 제한이 있나요?
답변1
펄에서
perl -F';' -lane 'push @{$h{join ";",@F[0..2]}},$F[3];
END{
for(sort keys %h){
print "$_: ". join ",",@{$h{$_}};
}
}' your_file
연관 배열을 사용하여 비슷한 작업을 수행할 수 있어야 awk
하지만 저는 이에 대해 잘 알지 못하므로 awk
실제 코드에 기여할 수 없습니다.
설명하다
다음은 "마법"을 가능한 한 적게 사용하는 위 코드의 확장 버전입니다.
open($FH,"<","your_file");
while($line=<$FH>){ # For each line in the file (accomplished by -n)
chomp $line; # Remove the newline at the end (done by -l)
# The ; is set by -F and storing the split in @F done by -a
@F = split /;/,$line # Split the line into fields on ;
$app_id = join ";",@F[0..2]; # AppID is the first 3 fields
push @{$h{$app_id}},$F[3]; # The 4th field is added onto the hash
} # The whole file has been read at this point.
foreach $key (sort keys %h){ # Sort the hash by AppID
print "$key: " . join ",",@{h{$key}}."\n"; # Print the array values
# The newline ("\n") added at the end is also done by -l
}
이제 push
이 문장만 더 자세히 설명하면 됩니다.
push
일반적으로 배열 변수에 요소를 추가하는 데 사용됩니다. 예를 들어:push @a,$x
변수의 내용을
$x
배열에 추가합니다@a
.파일을 한 줄씩 읽는 루프가 해시 테이블(
%h
)을 채우고 있습니다. 해시의 키는 AppID이고, 각 키에 해당하는 값은 해당 AppID와 연결된 모든 사용자 ID를 포함하는 배열입니다. 이것은 익명 배열입니다(이름이 없음). Perl에서는 배열 참조로 구현됩니다(C 포인터와 다소 유사).%h
AppID에 해당하는 값은$app_id
으로 표시되므로 Perl$h{$app_id}
배열 sigial(@
)을 추가하면 해시 값을 배열로 처리하고(배열 참조 역참조) 현재 사용자 ID를 여기에 푸시합니다.덜 "Perlish"처럼 느껴질 수 있는 또 다른 대안은 네 번째 필드를 현재 값에 연결하는 것입니다.
while(...) { ... $h{$app_id} = $h{$app_id} . ",$F[3]" } foreach $key (sort keys %h) { print "$_: $h{$_}" }
Perl 에서는
.
문자열 연결 연산자입니다.
설명된 코드에서는 perl -e '...'
구문 강조 표시가 코드에 도달하여 더 쉽게 읽을 수 있도록 래퍼를 생략했습니다.
답변2
appId
파일이 정렬되었다고 말씀하셨는데, 간단한 루프를 사용하여 이전 문자열의 메모리 만 저장하는 것이 가능합니까 ? @Qeole의 접근 방식과 약간 비슷하지만 쉘의 구분 기호 기능과 문자열 비교를 사용하여 sed
정규식 오버헤드를 피합니다 .read
#!/bin/bash
appId=""
while IFS=\; read -r s1 s2 s3 userId; do
if [ "$s1;$s2;$s3" == "$appId" ]; then
printf ', %s' "$userId"
else
appId="$s1;$s2;$s3"
printf '\n%s:%s' "$appId" "$userId"
fi
done < yourfile
printf '\n'
참고: 이렇게 하면 출력 시작 부분에 추가 줄바꿈이 인쇄되지만 추가 복잡성을 최소화하면 이를 방지할 수 있습니다. 큰 타격을 입어야 한다상당히이런 종류의 작업에는 빠르지만 그렇지 않은 경우 거의 모든 유사한 스크립팅 언어로 다시 구현할 수 있습니다.
답변3
그리고 sed
:
sed 's/;/:\t/3;H;1h;x
s/^\(\([^:]*\):.*\)\n\2/\1/
/\n/P;//g;h;$!d' <input |
tr : \\n
인쇄:
44a934ca4052b34e70f9cb03f3399c6f065becd0;bf038823f9633d25034220b9f10b68dd8c16d867;309
8ead5b3e0af5b948a6b09916bd271f18fe2678aa
a21245497cd0520818f8b14d6e405040f2fa8bc0
5c3eb56d91a77d6ee5217009732ff421e378f298;200000000000000001000000200000,6fd299187a5c347fe7eaab516aca72295faac2ad,e25ba62bbd53a72beb39619f309a06386dd381d035de372c85d70176c339d6f4;16
337556fc485cd094684a72ed01536030bdfae5bb
382f3aaa9a0347d3af9b35642d09421f9221ef7d
396529e08c6f8a98a327ee28c38baaf5e7846d14
tr
그룹을 같은 줄에 유지하기 위해 삭제할 수 있습니다 . 이것ID:
이 경우 콜론으로 구분됩니다. \t
첫 번째 줄의 이스케이프 문자를 리터럴 문자로 바꿔야 할 수도 있습니다 <tab>
. 또는 Ab를 완전히 제거해도 됩니다 \t
. 그러면 출력이 더 읽기 쉬워집니다.(내 생각에는)정규식의 기능에는 중요하지 않습니다.
답변4
한 가지 sed
답변:
sed ': l;N;s/^\([^;]\+;[^;]\+;[^;:]\+\)[;:] *\(.*\)\n\1;\(.*\)/\1: \2, \3/;tl;P;D' input_file.txt
파일은 한 번만 읽어서 성능이 크게 나쁘지는 않겠지만 자세한 내용은 말씀드릴 수 없습니다.
세부:
sed ': l; # Label l
N; # Add next line of input to pattern space
s/^\([^;]\+;[^;]\+;[^;:]\+\)[;:] *\(.*\)\n\1;\(.*\)/\1: \2, \3/;
# If two lines in pattern space start with same AppID, then
# take user ID and append it to first line, then delete second line
tl; # If previous substitution succeeded, i.e. we scanned two lines with
# same AppID, then loop to label l. Else go on…
P; # Print first line from pattern space (here there should be two lines
# in pattern space, starting with a different AppID)
D; # Delete first line of pattern space; start script again with
# remaining text in pattern space, or next input line if pattern
# space is empty
' input_file.txt
(하지만 줄 길이에 대한 잠재적인 제한에 대해서는 모르겠습니다. 죄송합니다.)