문제
저는 다양한 소스로부터 입력을 받아 한 줄씩 작업할 수 있는 스크립트를 관리하고 있습니다.사용된 실제 소스에 따라 줄 바꿈은 Unix 스타일, Windows 스타일 또는 일부 집계 입력의 경우 혼합(!) 스타일일 수 있습니다.
파일에서 읽을 때 다음과 같이 진행됩니다.
@lines = <IN>;
process(\@lines);
...
sub process {
@lines = shift;
foreach my $line (@{$lines}) {
chomp $line;
#Handle line by line
}
}
따라서 제가 해야 할 일은 chomp를 Unix 스타일이나 Windows 스타일의 줄 바꿈을 제거하는 것으로 바꾸는 것입니다.나는 Perl의 일반적인 단점 중 하나인 이 문제를 해결하는 너무 많은 방법을 생각해 냈습니다. :)
일반적인 줄 바꿈을 없애는 가장 깔끔한 방법에 대한 귀하의 의견은 무엇입니까?무엇이 가장 효율적일까요?
편집하다:약간의 설명 - 'process' 메소드는 어딘가에서 행 목록을 가져옵니다. 반드시 파일에서 읽지 않아도 됨.각 라인에는
- 후행 줄 바꿈 없음
- 유닉스 스타일 줄 바꿈
- Windows 스타일 줄 바꿈
- Just Carriage-Return(원본 데이터에 Windows 스타일 줄 바꿈이 있고 $/ = ' '으로 읽는 경우)
- 선의 스타일이 서로 다른 집합 집합
해결책
조금 파고 들었습니다 Perlre 문서는 조금, 지금까지 최고의 제안을 제시 할 것입니다. Perl 5.10은 일반화 된 라인 브레이크로 r 캐릭터 클래스를 추가했습니다.
$line =~ s/\R//g;
그것은 다음과 같습니다.
(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])
나는이 질문을 잠시 동안 열어두고, 제안되기를 기다리는 더 멋진 방법이 있는지 알아 보겠습니다.
다른 팁
입력을 거치고 캐릭터를 제거하거나 교체하고 싶을 때마다 나는 이와 같은 작은 서브 루틴을 통해 실행합니다.
sub clean {
my $text = shift;
$text =~ s/\n//g;
$text =~ s/\r//g;
return $text;
}
화려하지는 않지만이 방법은 몇 년 동안 나에게 완벽하게 작동했습니다.
독서 펄포트 나는 다음과 같은 것을 제안 할 것입니다
$line =~ s/\015?\012?$//;
및 에 있는 내용은 Perl에 따라 다를 수 있으므로 현재 사용 중인 플랫폼과 처리 중인 줄바꿈 스타일에 관계없이 안전합니다.
$line =~ s/[\r\n]+//g;
2017년 참고 사항:파일::Slurp는 설계 실수와 유지 관리되지 않은 오류로 인해 권장되지 않습니다.사용 파일::슬러퍼 또는 경로::작은 대신에.
당신의 대답을 확장
use File::Slurp ();
my $value = File::Slurp::slurp($filename);
$value =~ s/\R*//g;
File::Slurp는 File IO 항목을 추상화하고 문자열만 반환합니다.
메모
추가 사항에 주목하는 것이 중요합니다.
/g
, 그것이 없으면 여러 줄의 문자열이 주어지면 첫 번째 공격적인 성격.또한, 제거
$
, 이는 제거하려는 목적으로 중복됩니다. 모두 줄 바꿈은 무엇을 의미하든지 앞의 줄 바꿈이 아닙니다.$
이 OS에서.여러 줄 문자열에서,
$
의 끝과 일치합니다. 끈 그리고 그것은 문제가 될 것입니다).포인트 3은 포인트 2도 사용하고 싶다는 가정하에 만들어졌음을 의미합니다.
/m
그렇지 않으면 '$'는 라인이 1개 이상인 문자열이나 단일 라인 처리를 수행하는 경우 실제로 이해하는 OS에 대해 기본적으로 의미가 없습니다.$
그리고 가까스로 찾아내는데 성공한다\R*
그 진행$
예
while( my $line = <$foo> ){
$line =~ $regex;
}
위의 표기법을 고려하면 OS의 기본 구분 기호가 설정된 기본 시나리오에서 파일의 '
' 또는 '
' 구분 기호가 무엇이든 이해하지 못하는 OS입니다. $/
전체 파일을 하나의 연속된 문자열로 읽게 됩니다(문자열에 $OS 구분 기호가 있는 경우 제외).
따라서 이 경우 이러한 정규식은 모두 쓸모가 없습니다.
/\R*$//
:마지막 시퀀스만 삭제됩니다.\R
파일에/\R*//
:첫 번째 시퀀스만 삭제됩니다.\R
파일에/\012?\015?//
:언제 첫 번째 것만 지울 것인가?012\015
,\012
, 또는\015
순서,\015\012
결과는 다음 중 하나입니다.\012
또는\015
방출되고 있습니다./\R*$//
:파일에 '\015$OSDELIMITER'의 바이트 시퀀스가 없는 경우 아니요 OS 자체를 제외한 줄 바꿈은 제거됩니다.
내가 말하는 내용을 아무도 이해하지 못하는 것 같으니 여기에 예제 코드가 있습니다. 테스트를 거쳤습니다. 에게 아니다 줄바꿈을 제거하세요.실행해 보면 라인피드가 남아 있는 것을 볼 수 있습니다.
#!/usr/bin/perl
use strict;
use warnings;
my $fn = 'TestFile.txt';
my $LF = "\012";
my $CR = "\015";
my $UnixNL = $LF;
my $DOSNL = $CR . $LF;
my $MacNL = $CR;
sub generate {
my $filename = shift;
my $lineDelimiter = shift;
open my $fh, '>', $filename;
for ( 0 .. 10 )
{
print $fh "{0}";
print $fh join "", map { chr( int( rand(26) + 60 ) ) } 0 .. 20;
print $fh "{1}";
print $fh $lineDelimiter->();
print $fh "{2}";
}
close $fh;
}
sub parse {
my $filename = shift;
my $osDelimiter = shift;
my $message = shift;
print "Parsing $message File $filename : \n";
local $/ = $osDelimiter;
open my $fh, '<', $filename;
while ( my $line = <$fh> )
{
$line =~ s/\R*$//;
print ">|" . $line . "|<";
}
print "Done.\n\n";
}
my @all = ( $DOSNL,$MacNL,$UnixNL);
generate 'Windows.txt' , sub { $DOSNL };
generate 'Mac.txt' , sub { $MacNL };
generate 'Unix.txt', sub { $UnixNL };
generate 'Mixed.txt', sub {
return @all[ int(rand(2)) ];
};
for my $os ( ["$MacNL", "On Mac"], ["$DOSNL", "On Windows"], ["$UnixNL", "On Unix"]){
for ( qw( Windows Mac Unix Mixed ) ){
parse $_ . ".txt", @{ $os };
}
}
에 대한 분명히 처리되지 않은 출력은 여기를 참조하세요. http://pastebin.com/f2c063d74
물론 작동하는 특정 조합이 있지만 이는 여러분이 직접 테스트한 조합일 가능성이 높습니다.
이 출력에서 모든 결과는 다음 형식이어야 합니다. >|$string|<>|$string|<
~와 함께 라인 피드 없음 유효한 출력으로 간주됩니다.
그리고 $string
일반적인 형태이다 {0}$data{1}$delimiter{2}
모든 출력 소스에는 다음 중 하나가 있어야 합니다.
- 사이에는 아무것도 없습니다
{1}
그리고{2}
- 오직
|<>|
~ 사이{1}
그리고{2}
예에서는 그냥 갈 수 있습니다.
chomp(@lines);
또는:
$_=join("", @lines);
s/[\r\n]+//g;
또는:
@lines = split /[\r\n]+/, join("", @lines);
파일에서 직접 사용 :
perl -e '$_=join("",<>); s/[\r\n]+//g; print' <a.txt |less
perl -e 'chomp(@a=<>);print @a' <a.txt |less
위의 Ted Cambron의 답변을 확장하려면 여기에서 다루지 않은 내용을 확장하려면 다음과 같은 내용을 제거하면 모든 줄을 제거하면 입력 한 텍스트 덩어리에서 무차별 적으로 나뉘어지면 나중에 해당 텍스트를 출력 할 때 공백없이 단락이 서로 실행됩니다. 이것이 제가 사용하는 것입니다.
sub cleanLines{
my $text = shift;
$text =~ s/\r/ /; #replace \r with space
$text =~ s/\n/ /; #replace \n with space
$text =~ s/ / /g; #replace double-spaces with single space
return $text;
}
마지막 치환은 G 'Greedy'수정자를 사용하므로 모두 교체 할 때까지 이중 공간을 계속 찾습니다. (효과적으로 단일 공간을 효과적으로 대체)