문제

저는 다양한 소스로부터 입력을 받아 한 줄씩 작업할 수 있는 스크립트를 관리하고 있습니다.사용된 실제 소스에 따라 줄 바꿈은 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 항목을 추상화하고 문자열만 반환합니다.

메모

  1. 추가 사항에 주목하는 것이 중요합니다. /g , 그것이 없으면 여러 줄의 문자열이 주어지면 첫 번째 공격적인 성격.

  2. 또한, 제거 $, 이는 제거하려는 목적으로 중복됩니다. 모두 줄 바꿈은 무엇을 의미하든지 앞의 줄 바꿈이 아닙니다. $ 이 OS에서.

  3. 여러 줄 문자열에서, $ 의 끝과 일치합니다. 그리고 그것은 문제가 될 것입니다).

  4. 포인트 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. 사이에는 아무것도 없습니다 {1} 그리고 {2}
  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'수정자를 사용하므로 모두 교체 할 때까지 이중 공간을 계속 찾습니다. (효과적으로 단일 공간을 효과적으로 대체)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top