텍스트 파일에서, 같은 내용의 줄이 여러 번 중복되어 있는 경우에, 그 중복된 줄을 모두 지우고 하나씩만 남기는 방법입니다.
즉 파일의 데이터를 정돈하는 스크립트입니다.
delDupLine.pl test.txt
이렇게 명령행 옵션으로 텍스트 파일을 지정해 주면, test.txt 에서 중복된 줄을 모두 지우고 유일한 줄만 화면에 출력합니다.
화면 출력을 다시 텍스트 파일로 저장하려면, 즉 "새 이름으로 저장(Save As)" 하려면
delDupLine.pl test.txt > out.txt
이렇게
재지향(Redirection)을 사용하면 됩니다.
텍스트 파일의, 중복 라인(데이터) 삭제 프로그램
파일명: delDupLine.pl
#!/usr/bin/perl
use strict; use warnings;
foreach (<>) {
chomp;
print "$_\n" unless $_{$_}++;
}
테스트용으로 사용할 텍스트 파일: test.txt
foo
foo
foo
bar
0
0
0
1
1
1
0
0
0
맹구는 복숭아를 먹었습니다.
맹구는 복숭아를 먹었습니다.
맹구는 복숭아를 먹었습니다.
1twyw
2yeyeye
3tuteute
4646464
557575
6yeyt
7ryryre
8ytrwyrw
968486
065yrhdhd
2yeyeye
6yeyt
2yeyeye
AAAAAAAAAAAAAA
BBBBBBBBBBBB
CCCCCCCCCC
EEEEEEEEEE
EEEEEEEEEE
FFFFFFFF
GGGGGG
HHHHHHH
IIII
JJJJJJ
KKKKKKK
LLLLLLL
MMMMMMM
NNNNNNN
AAAAAAAAAAAAAA
GGGGGG
HHHHHHH
IIII
JJJJJJ
자장면
자장면
자장면
탕수육
탕수육
자장면
탕수육
짬뽕
맹구는 복숭아를 먹었습니다.
GGGGGG
GGGGGG
GGGGGG
GGGGGG
실행 결과:
(윈도우에
액티브펄(ActivePerl)을 설치한 후, 실행한 결과임)
D:\Z>delDupLine.pl test.txt
foo
bar
0
1
맹구는 복숭아를 먹었습니다.
1twyw
2yeyeye
3tuteute
4646464
557575
6yeyt
7ryryre
8ytrwyrw
968486
065yrhdhd
AAAAAAAAAAAAAA
BBBBBBBBBBBB
CCCCCCCCCC
EEEEEEEEEE
FFFFFFFF
GGGGGG
HHHHHHH
IIII
JJJJJJ
KKKKKKK
LLLLLLL
MMMMMMM
NNNNNNN
자장면
탕수육
짬뽕
D:\Z>
중복된 줄이 말끔히 지워지고, 유일한 줄만 남았습니다.
코드 설명
foreach (<>) {
chomp;
print "$_\n" unless $_{$_}++;
}
소스 자체는 어처구니 없을 정도로 짧고 간단한데, 알고리즘이랄까 내용은 좀 복잡합니다.
foreach (<>) {...
명령행 옵션으로 입력해 준, 텍스트 파일을 1행씩 읽어, 기본 변수인 $_에 차례로 담습니다.
chomp;
라는 것은, 라인 끝의 개행문자를 제거합니다. 개행문자(줄바꿈 문자)를 제거하지 않으면, 줄 끝의 개행문자의 유무에 따라, 같은 문자열도 다른 문자열로 간주되는 문제가 생깁니다. chomp 뒤에 아무것도 적어 주지 않으면, 기본 변수인 $_ 의 끝에서 개행문자를 지웁니다.
print "$_\n" unless $_{$_}++;
unless 라는 것은 if문의 반대입니다. 조건에 맞지 않으면 뭘 하라는 뜻인데, 여기서는 $_{$_}++ 라는 조건이 거짓(undef; 초기화되지 않은 값)이라면 print문을 실행하라는 뜻입니다.
플러스 플러스(++)로 1 이상이 되면, 아까 처리했던 줄이니 출력하지 말라는 것입니다.
더 자세한 설명은 다음과 같습니다:
기본 디폴트 해쉬인 %_ 의 변수적 표현인 $_{} 이곳에
$_{현재 행}
이렇게 현재 행을 통째로 키(key)로 삼아 집어 넣고, 해쉬의 그 요소의 값(value)을 +1 하여 증가시킵니다. (뭘 계산하기 위해서 증가시키는 것이 아니고, 참/거짓의 논리값을 얻기 위한 트릭입니다.)
만약 처음 마주친 줄이라면, 그 해쉬 요소의 값은 초기화되지 않은 undef 즉 null 이기에 논리값이 거짓(false)입니다. 거짓이면 그 줄이 print 됩니다. (unless 는 if의 정반대이기에)
만약 아까 보았던 줄이라면, 즉 "중복된 줄"이라면, 그 해쉬의 값이 ++ 에 의해 undef 에 1이 더해져서 1이나 1이상의 값이 되어 있을 것입니다.
해쉬의 값이 1이나 1 이상일 경우, 펄에서 0 은 거짓, 0이 아닌 숫자는 참이기에, 그 중복된 줄은 "참(true)"이 되어 print 되지 않습니다. (unless 는 if의 정반대이기에)
따라서, 결국 유일한 줄, 즉 유니크(Unique)한 줄만 프린트되고, 중복된(Duplicated) 줄은 자연스레 무시되는 것입니다.
자장면 : 현재 $_{자장면} 의 값은 undef
자장면 : 이제 $_{자장면} 값은 1. 앞의 자장면에서 +1 했기에
자장면 : $_{자장면} 의 값은 이제 2로 증가
탕수육 : $_{탕수육} 은 처음 나왔으니 undef
위의 경우, 맨 처음 나온 "자장면"이라는 줄만 출력되고, 다음 줄의 자장면들은 모두 무시됩니다. 왜냐하면 2번째 "$_{자장면}" 부터는 값이 undef 이 아니기 때문입니다.
중복된 행만, 별도로 추출하는 스크립트:
출처:
https://www.perlmonks.org/?node_id=592934
파일명: test2.pl
사용법: test2.pl A.txt
#!/usr/bin/perl
use strict; use warnings;
my %duplicates;
while (<>) {
chomp;
$duplicates{$_}++;
}
foreach my $key (keys %duplicates) {
if ($duplicates{$key} > 1) {
delete $duplicates{$key};
print "$key\n";
}
}
2개의 파일을 비교해, 파일B에 있는 행이, 파일A에서 발견되면 삭제한 후, 파일A 출력하기:
출처:
https://stackoverflow.com/questions/14931656/using-perl-i-want-to-compare-two-files-and-how-do-i-keep-unique-lines-from-first
파일명: test3.pl
사용법: test3.pl A.txt B.txt
참고: test3.pl 자체에도, 파일 A의 중복된 행을 제거하는 기능이 있음.
#!/usr/bin/perl
use strict; use warnings;
my %skip;
{
open(my $fh, '<', $ARGV[1])
or die("Can't open \"$ARGV[1]\": $!\n");
while (<$fh>) {
chomp;
++$skip{$_};
}
}
{
open(my $fh, '<', $ARGV[0])
or die("Can't open \"$ARGV[0]\": $!\n");
while (<$fh>) {
chomp;
print "$_\n" if !$skip{$_}++;
}
}
바로 위에 있는 2가지 코드 실행 결과 화면
D:\Z>type A.txt
111
222
111
111
333
444
555
666
D:\Z>
D:\Z>test2.pl A.txt
111
D:\Z>
D:\Z>
D:\Z>test2.pl A.txt > B.txt
D:\Z>test3.pl A.txt B.txt
222
333
444
555
666
D:\Z>
파일에서, 중복된 빈 줄들만 삭제하는 방법:
▶▶ 펄/Perl] 여러 개의 빈줄 제거, 하나의 빈줄로 합치기, Collapse Multiple Blank Lines Into One
tag: perl
Perl | 펄