Perl에서 전체 파일을 문자열로 어떻게 읽을 수 있습니까?


118

.html 파일을 하나의 큰 긴 문자열로 열려고합니다. 이것이 내가 가진 것입니다.

open(FILE, 'index.html') or die "Can't read file 'filename' [$!]\n";  
$document = <FILE>; 
close (FILE);  
print $document;

결과 :

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN

그러나 결과는 다음과 같습니다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

이렇게하면 전체 문서를 더 쉽게 검색 할 수 있습니다.


8
"Cant 설치"의 정의가 무엇인지, 일반적인 문제이며 일반적으로 만들 필요가없는 주장을 확인해야합니다. stackoverflow.com/questions/755168/perl-myths/…
Kent Fredric 2009-06-05

1
나는 실제로 스크립트 자체를 제외 하고이 스크립트가 실행되는 전체 서버에서 아무것도 수정할 수 없습니다.
goddamnyouryan 2009-06-05

그렇다면 서버의 어디에도 파일을 추가 할 수 없습니까?
Brad Gilbert

FatPack 모듈을 스크립트에 추가 하시겠습니까? 또한 정규 표현식을 사용하여 HTML을 구문 분석 할 생각이있는 것 같습니다.
MkV

답변:


81

더하다:

 local $/;

파일 핸들에서 읽기 전에. 전체 파일을 한 번에 읽을 수있는 방법을 참조하십시오 . , 또는

$ perldoc -q "전체 파일"

참조 파일 핸들과 관련된 변수perldoc perlvarperldoc -f local.

덧붙여서, 스크립트를 서버에 넣을 수 있다면 원하는 모든 모듈을 가질 수 있습니다. 내 모듈 / 라이브러리 디렉토리를 유지하는 방법을 참조하십시오 . .

또한, 경로 :: 클래스 :: 파일은 당신이 할 수 후루룩 소리 내며 먹기토 해낸다 .

Path :: Tinyslurp, slurp_rawslurp_utf8 그에 spew상응하는 것과 같은 훨씬 더 편리한 방법을 제공합니다 .


33
$ / 지역화 효과와 그 목적이 무엇인지 설명해야 할 것입니다.
Danny

12
현지화 $/에 대해 설명하지 않으려면 추가 정보에 대한 링크를 추가해야합니다.
Brad Gilbert

7
무엇을하고 있는지에 대한 단계별 설명 : {local $ /; <$ fh>} 제공 : perlmonks.org/?node_id=287647
dawez

사용해야하는 이유 아마도 단지 말을 local하지 my.
Geremia

@Geremia 범위 지정에 대한 논의는이 답변의 범위를 벗어납니다.
Sinan Ünür

99

나는 다음과 같이 할 것입니다.

my $file = "index.html";
my $document = do {
    local $/ = undef;
    open my $fh, "<", $file
        or die "could not open $file: $!";
    <$fh>;
};

open의 3 개 인수 버전 사용에 유의하십시오. 이전의 2 개 (또는 1 개) 인수 버전보다 훨씬 안전합니다. 또한 어휘 파일 핸들의 사용에 유의하십시오. 어휘 파일 핸들은 여러 가지 이유로 오래된 베어 워드 변형보다 더 좋습니다. 여기에서 그중 하나를 활용하고 있습니다. 범위를 벗어나면 닫힙니다.


9
이것은 아마도 가장 작은 필수 컨텍스트로 지역화 된 INPUT_RECORD_SEPARATOR ($ /) 변수를 유지하는 것뿐만 아니라 3 인수를 모두 사용하므로 cpan이 아닌 가장 좋은 방법입니다.
Danny

77

파일 :: 소리내어 먹는다 :

use File::Slurp;
my $text = read_file('index.html');

예, CPAN을 사용할 수도 있습니다 .


OP는 서버에서 아무것도 수정할 수 없다고 말했습니다. 여기에있는 "예, CPAN을 사용할 수도 있습니다."링크는 대부분의 경우 이러한 제한을 해결하는 방법을 보여줍니다.
Trenton

Can't locate File/Slurp.pm in @INC (@INC contains: /usr/lib/perl5/5.8/msys:(
Dmitry

2
@Dmitry — 모듈을 설치하십시오. 이 답변에서 링크 한 metacpan 페이지에 설치 지침 링크가 있습니다.
Quentin

53

모든 게시물은 약간 비관 상적입니다. 관용구는 다음과 같습니다.

open my $fh, '<', $filename or die "error opening $filename: $!";
my $data = do { local $/; <$fh> };

대부분 $ /를로 설정할 필요가 없습니다 undef.


3
local $foo = undef단지 Perl Best Practice (PBP)가 제안한 방법입니다. 코드 스 니핏을 게시하는 경우이를 명확히하기 위해 최선을 다하는 것이 좋은 일이라고 생각합니다.
Danny

2
사람들에게 비 관상적인 코드를 작성하는 방법을 보여주는 것은 좋은 일입니까? 내가 작업하고있는 코드에서 "local $ / = undef"를 본 경우 첫 번째 조치는 irc에서 작성자를 공개적으로 모욕하는 것입니다. (그리고 저는 일반적으로 "스타일"문제에 대해 까다 롭지 않습니다.)
jrockway

1
좋아, 내가 물어볼 게 : "local $ / = undef"에 대해 정확히 모의 가치가있는 것은 무엇인가? 당신의 유일한 대답이 "비관 상적이다"라면, (a) 잘 모르겠고 (b) 그래서 뭐? 나는 그렇게 확신하지 못합니다. 왜냐하면 이것을 수행하는 방법으로 끔찍하게 일반적이기 때문입니다. 그래서 그것은 완벽하게 명확하고 합리적으로 짧기 때문에 어떨까요. 자신이 생각하는 스타일 문제에 대해 더 까다로울 수 있습니다.
Telemachus

1
핵심은 "local $ /"이 잘 알려진 관용구의 일부라는 것입니다. 임의의 코드를 작성하고 "local $ Foo :: Bar = undef;"라고 작성하면 괜찮습니다. 그러나이 매우 특별한 경우에는 "덜 분명한"(내가 동의하지 않는, "로컬"의 행동이이 점에서 잘 정의되어 있더라도) 다른 모든 사람과 동일한 언어를 사용하는 것이 좋습니다.
jrockway

11
죄송합니다. 동의하지 않습니다. 매직 변수의 실제 동작을 변경하고 싶을 때 명시하는 것이 훨씬 더 일반적입니다. 의도의 선언입니다. 심지어 문서 사용하는 '지역 $ / = 미확정'(참조는 perldoc.perl.org/perlsub.html#Temporary-Values-via-local () )
레오나르도 헤레라

19

에서 perlfaq5 : 어떻게 한 번에 모든 전체 파일을 읽을 수 있습니까? :


File :: Slurp 모듈을 사용하여 한 단계로 수행 할 수 있습니다.

use File::Slurp;

$all_of_it = read_file($filename); # entire file in scalar
@all_lines = read_file($filename); # one line per element

파일의 모든 행을 처리하는 일반적인 Perl 접근 방식은 한 번에 한 행씩 처리하는 것입니다.

open (INPUT, $file)     || die "can't open $file: $!";
while (<INPUT>) {
    chomp;
    # do something with $_
    }
close(INPUT)            || die "can't close $file: $!";

이것은 전체 파일을 줄의 배열로 메모리로 읽어 들인 다음 한 번에 한 요소 씩 처리하는 것보다 훨씬 더 효율적입니다. 거의 항상 그렇지는 않지만 잘못된 접근 방식입니다. 누군가가 이렇게하는 것을 볼 때마다 :

@lines = <INPUT>;

한 번에 모든 것을로드해야하는 이유에 대해 오랫동안 열심히 생각해야합니다. 확장 가능한 솔루션이 아닙니다. 표준 Tie :: File 모듈 또는 DB_File 모듈의 $ DB_RECNO 바인딩을 사용하는 것이 더 재미있을 수도 있습니다.이 바인딩을 사용하면 배열을 파일에 연결하여 배열이 실제로 파일의 해당 줄에 액세스하도록 배열을 연결할 수 있습니다. .

전체 파일 핸들 내용을 스칼라로 읽을 수 있습니다.

{
local(*INPUT, $/);
open (INPUT, $file)     || die "can't open $file: $!";
$var = <INPUT>;
}

일시적으로 레코드 구분 기호를 해제하고 블록 종료시 파일을 자동으로 닫습니다. 파일이 이미 열려 있으면 다음을 사용하십시오.

$var = do { local $/; <INPUT> };

일반 파일의 경우 읽기 기능을 사용할 수도 있습니다.

read( INPUT, $var, -s INPUT );

세 번째 인수는 INPUT 파일 핸들에있는 데이터의 바이트 크기를 테스트하고 해당 바이트를 $ var 버퍼로 읽습니다.


8

간단한 방법은 다음과 같습니다.

while (<FILE>) { $document .= $_ }

또 다른 방법은 입력 레코드 구분 기호 "$ /"를 변경하는 것입니다. 전역 레코드 구분 기호를 변경하지 않도록 베어 블록에서 로컬로 수행 할 수 있습니다.

{
    open(F, "filename");
    local $/ = undef;
    $d = <F>;
}

1
귀하가 제공 한 두 가지 예에는 상당한 수의 문제가 있습니다. 가장 큰 문제는 내가 읽어 보시기 바랍니다 것이다, 그들은 고대 Perl로 작성되어 있다는 것이다 현대 펄
브래드 길버트

@Brad, 댓글은 몇 년 전에 작성되었지만 요점은 여전히 ​​존재합니다. 더{local $/; open(my $f, '<', 'filename'); $d = <$f>;}
조엘 버거

@Joel은 약간 더 좋습니다. 의 출력을 확인하지 않았 open거나 암시 적으로 close. my $d = do{ local $/; open(my $f, '<', 'filename') or die $!; my $tmp = <$f>; close $f or die $!; $tmp}. (입력 인코딩을 지정하지 않는 문제는 여전히 남아 있습니다.)
Brad Gilbert

use autodie, 내가 보여 주려고했던 주요 개선점은 어휘 파일 핸들과 3 개의 인수가 열려 있다는 것입니다. do이걸하는 이유가 있나요? 블록 전에 선언 된 변수에 파일을 덤프하지 않는 이유는 무엇입니까?
Joel Berger

7

로 설정 $/하거나 undef(jrockway의 답변 참조) 모든 파일의 행을 연결하십시오.

$content = join('', <$fh>);

이를 지원하는 모든 Perl 버전에서 파일 핸들에 스칼라를 사용하는 것이 좋습니다.


4

또 다른 가능한 방법 :

open my $fh, '<', "filename";
read $fh, my $string, -s $fh;
close $fh;

3

<FILE>스칼라 컨텍스트에서 평가하기 때문에 다이아몬드 연산자에서 첫 번째 줄만 가져옵니다 .

$document = <FILE>; 

목록 / 배열 컨텍스트에서 다이아몬드 연산자는 파일의 모든 행을 반환합니다.

@lines = <FILE>;
print @lines;

1
명칭에 그냥 참고 사항 : 우주선 연산자가 <=>와는 <>다이아몬드 연산자입니다.
toolic

오, 감사합니다. "다이아몬드 오퍼레이터"를 들어 본 적이 없었고 둘 다 같은 이름을 공유한다고 생각했습니다. 위에서 수정하겠습니다.
Nathan

2

나는 가장 간단한 방법으로 할 것이므로 더 똑똑한 방법이 있더라도 누구나 무슨 일이 일어나는지 이해할 수 있습니다.

my $text = "";
while (my $line = <FILE>) {
    $text .= $line;
}

이러한 모든 문자열 연결은 상당히 비쌉니다. 나는 이것을 피할 것입니다. 데이터를 쪼개서 다시 합치는 이유는 무엇입니까?
andru

2
open f, "test.txt"
$file = join '', <f>

<f>-파일에서 줄 배열을 반환하고 ( $/기본값이있는 경우 "\n") join ''이 배열을 붙입니다.


2

이것은 그것을 하지 않는 방법에 대한 제안입니다 . 상당히 큰 Perl 응용 프로그램에서 버그를 찾는 데 어려움을 겪었습니다. 대부분의 모듈에는 자체 구성 파일이 있습니다. 구성 파일을 전체적으로 읽기 위해 인터넷 어딘가에서이 한 줄의 Perl을 찾았습니다.

# Bad! Don't do that!
my $content = do{local(@ARGV,$/)=$filename;<>};

앞에서 설명한대로 줄 구분 기호를 다시 할당합니다. 그러나 또한 STDIN을 재 할당합니다.

이것은 내가 찾는 데 몇 시간이 걸리는 부작용이 적어도 하나 있었다 : 암시 적 파일 핸들을 제대로 닫지 않는다 (아무것도 호출하지 않기 때문에 close).

예를 들면 다음과 같습니다.

use strict;
use warnings;

my $filename = 'some-file.txt';

my $content = do{local(@ARGV,$/)=$filename;<>};
my $content2 = do{local(@ARGV,$/)=$filename;<>};
my $content3 = do{local(@ARGV,$/)=$filename;<>};

print "After reading a file 3 times redirecting to STDIN: $.\n";

open (FILE, "<", $filename) or die $!;

print "After opening a file using dedicated file handle: $.\n";

while (<FILE>) {
    print "read line: $.\n";
}

print "before close: $.\n";
close FILE;
print "after close: $.\n";

결과 :

After reading a file 3 times redirecting to STDIN: 3
After opening a file using dedicated file handle: 3
read line: 1
read line: 2
(...)
read line: 46
before close: 46
after close: 0

이상한 점은 $.모든 파일에 대해 라인 카운터 가 하나씩 증가 한다는 것입니다 . 재설정되지 않으며 행 수를 포함하지 않습니다. 그리고 적어도 한 줄을 읽을 때까지 다른 파일을 열 때 0으로 재설정되지 않습니다. 제 경우에는 다음과 같이했습니다.

while($. < $skipLines) {<FILE>};

이 문제로 인해 회선 카운터가 제대로 재설정되지 않았기 때문에 조건이 거짓이었습니다. 이것이 버그인지 아니면 단순히 잘못된 코드인지 모르겠습니다 . close;oder를 호출 close STDIN;해도 도움이되지 않습니다.

이 읽을 수없는 코드를 열기, 문자열 연결 및 닫기를 사용하여 대체했습니다. 그러나 Brad Gilbert가 게시 한 솔루션은 대신 명시 적 파일 핸들을 사용하기 때문에 작동합니다.

처음에있는 세 줄은 다음으로 바꿀 수 있습니다.

my $content = do{local $/; open(my $f1, '<', $filename) or die $!; my $tmp1 = <$f1>; close $f1 or die $!; $tmp1};
my $content2 = do{local $/; open(my $f2, '<', $filename) or die $!; my $tmp2 = <$f2>; close $f2 or die $!; $tmp2};
my $content3 = do{local $/; open(my $f3, '<', $filename) or die $!; my $tmp3 = <$f3>; close $f3 or die $!; $tmp3};

파일 핸들을 제대로 닫습니다.


2

사용하다

 $/ = undef;

전에 $document = <FILE>;. $/는 IS 입력 레코드 분리 기본적 개행이다. 으로 재정의하면 undef필드 구분 기호가 없음을 의미합니다. 이것을 "slurp"모드라고합니다.

undef $/local $/(그러나 아님 my $/) 과 같은 다른 솔루션은 $ /를 재 선언하므로 동일한 효과를 생성합니다.


0

간단히 서브 루틴을 생성 할 수 있습니다.

#Get File Contents
sub gfc
{
    open FC, @_[0];
    join '', <FC>;
}

0

좋은 습관인지는 모르겠지만 이것을 사용했습니다.

($a=<F>);

-1

이것들은 모두 좋은 대답입니다. 하지만 게으르고 파일이 그다지 크지 않고 보안이 문제가되지 않는다면 (오염 된 파일 이름이 없다는 것을 알고 있음) 다음과 같이 말할 수 있습니다.

$x=`cat /tmp/foo`;    # note backticks, qw"cat ..." also works

-2

Linux에서 cat을 사용할 수 있습니다.

@file1=\`cat /etc/file.txt\`;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.