큰 파일을 한 줄씩 읽는 방법?


469

파일을 한 줄씩 읽고 싶지만 메모리에 완전히로드하지 않습니다.

내 파일이 너무 커서 메모리에서 열 수 없으며 그렇게하면 항상 메모리 부족 오류가 발생합니다.

파일 크기는 1GB입니다.



7
매개 변수 fgets()없이 사용해야합니다 $length.
Carlos

26
다음 중 하나에 대한 답변으로 표시 하시겠습니까?
Kim Stacks

답변:


684

fgets()함수를 사용하여 파일을 한 줄씩 읽을 수 있습니다 .

$handle = fopen("inputfile.txt", "r");
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        // process the line read.
    }

    fclose($handle);
} else {
    // error opening the file.
} 

3
too large to open in memory부분을 ​​어떻게 설명합니까?
Starx

64
메모리에서 전체 파일을 읽지 않습니다. 이를 실행하는 데 필요한 최대 메모리는 입력에서 가장 긴 라인에 따라 다릅니다.
codaddict

13
@Brandin-Moot-이러한 상황에서 LINE BY LINE 파일을 읽는 질문은 올바르게 정의되지 않습니다.
ToolmakerSteve

3
@ToolmakerSteve 그런 다음 발생할 일을 정의하십시오. 원하는 경우 "줄이 너무 깁니다. 포기"메시지를 인쇄하면됩니다. 그리고 그것은 또한 잘 정의 된 결과입니다.
Brandin

2
줄에 부울 거짓을 포함 할 수 있습니까? 그렇다면이 방법은 파일 끝에 도달하지 않고 중단됩니다. 이 URL의 예제 # 1 php.net/manual/en/function.fgets.php 는 파일 끝에 아직 도달하지 않았더라도 fgets가 때때로 부울 false를 반환 할 수 있다고 제안합니다. 해당 페이지의 주석 섹션에서 사람들은 fgets ()가 항상 올바른 값을 반환하지는 않으므로 feof를 루프 조건부로 사용하는 것이 더 안전합니다.
cjohansson '12

130
if ($file = fopen("file.txt", "r")) {
    while(!feof($file)) {
        $line = fgets($file);
        # do same stuff with the $line
    }
    fclose($file);
}

8
@ Cuse70이 그의 대답에서 말했듯이 파일이 존재하지 않거나 열 수 없으면 무한 루프가 발생합니다. if($file)while 루프 전 테스트
FrancescoMM

10
나는 이것이 오래되었다는 것을 알고 있지만 while (! feof ($ file)) 사용은 권장되지 않습니다. 여기를보세요.
케빈 반 Ryckegem

BTW : "파일 포인터에서 읽을 데이터가 더 없으면 FALSE가 반환됩니다." php.net/manual/en/function.fgets.php ... 만일을 위해
사람

2
feof()더 이상 존재하지 않습니까?
Ryan DuVal

94

파일에 객체 지향 인터페이스 클래스를 사용할 수 있습니다 -SplFileObject http://php.net/manual/en/splfileobject.fgets.php (PHP 5> = 5.1.0)

<?php

$file = new SplFileObject("file.txt");

// Loop until we reach the end of the file.
while (!$file->eof()) {
    // Echo one line from the file.
    echo $file->fgets();
}

// Unset the file to call __destruct(), closing the file handle.
$file = null;

3
훨씬 더 깨끗한 솔루션. 덕분에) 탐험 더 흥미로운 기능이 여기에있다, 그러나이 클래스를 사용하지 않은 : php.net/manual/en/class.splfileobject.php
루카스 Liesis에게

6
감사. 예, 예를 들어 $ file-> setFlags (SplFileObject :: DROP_NEW_LINE); 줄 끝에 줄 바꿈을 제거하기 위해.
elshnkhll

내가 볼 수 eof()있는 한 SplFileObject 에는 함수 가 없습니다 .
Chud37

3
감사! 또한 rtrim($file->fgets())원하지 않는 경우 읽은 각 행 문자열에 대해 후행 줄 바꿈을 제거하십시오.
racl101


59

큰 파일을 열 경우 fgets ()와 함께 Generators를 사용하여 전체 파일을 메모리에로드하지 않도록 할 수 있습니다.

/**
 * @return Generator
 */
$fileData = function() {
    $file = fopen(__DIR__ . '/file.txt', 'r');

    if (!$file)
        die('file does not exist or cannot be opened');

    while (($line = fgets($file)) !== false) {
        yield $line;
    }

    fclose($file);
};

다음과 같이 사용하십시오.

foreach ($fileData() as $line) {
    // $line contains current line
}

이런 식으로 foreach () 내에서 개별 파일 라인을 처리 할 수 ​​있습니다.

참고 : 생성기는 PHP 5.5 이상이어야합니다.


3
이것은 대신 허용되는 답변이어야합니다. 발전기로 수백 배 더 빠릅니다.
Tachi

1
그리고 메모리 효율성이 뛰어납니다.
Nino Škopac 11

2
@ NinoŠkopac : 왜이 ​​솔루션이 더 메모리 효율적인지 설명 할 수 있습니까? 예를 들어, SplFileObject접근 방식 과 비교할 때.
k00ni

30

버퍼링 기술을 사용하여 파일을 읽습니다.

$filename = "test.txt";
$source_file = fopen( $filename, "r" ) or die("Couldn't open $filename");
while (!feof($source_file)) {
    $buffer = fread($source_file, 4096);  // use a buffer of 4KB
    $buffer = str_replace($old,$new,$buffer);
    ///
}

2
캐리지 리턴이 없거나 너무 긴 줄을 가진 파일조차도 거대한 파일에서 작동하므로 더 많은 사랑을받을 가치가 있습니다.
Jimmery

OP가 실제로 실제 회선을 신경 쓰지 않고 다운로드를 제공하려는 경우 놀라지 않을 것입니다. 이 경우이 답변은 훌륭합니다 (대부분의 PHP 코더는 어쨌든 할 것입니다).
Álvaro González

30

file()파일에 포함 된 라인의 배열을 반환 기능.

foreach(file('myfile.txt') as $line) {
   echo $line. "\n";
}

28
1GB 파일은 모두 메모리로 읽히고 둘 이상의 GB 배열로 변환됩니다. 행운을 빕니다.
FrancescoMM

4
이것은 묻는 질문에 대한 답변이 아니지만 여기를 볼 때 많은 사람들이 자주 묻는 질문에 대한 답변이므로 여전히 유용했습니다. 감사합니다.
pilavdzice

2
file ()은 작은 파일 작업에 매우 편리합니다. 특히 최종 결과로 array ()를 원할 때.
functionvoid

전체 파일을 한 번에 배열로 읽을 때 더 큰 파일을 사용하는 것은 좋지 않습니다.
Flash Thunder

이것은 큰 파일에서 나쁘게 작동하므로 정확하게 작동하지 않는 방법입니다.
ftrotter


17

명백한 대답은 모든 대답에 없었습니다.
PHP에는 그 목적을 위해 깔끔한 스트리밍 구분 기호 파서가 있습니다.

$fp = fopen("/path/to/the/file", "r+");
while ($line = stream_get_line($fp, 1024 * 1024, "\n")) {
  echo $line;
}
fclose($fp);

이 코드는 첫 번째 빈 줄이 발생할 때까지 줄만 반환합니다. 당신은 $ 라인 테스트에 필요한 == 그동안 조건에서 거짓!while (($line = stream_get_line($fp, 1024 * 1024, "\n")) !== false)
CEBE

8

'while (! feof ... fgets ()'항목에주의해야합니다. 루프가 종료되면 feof를 확인하십시오. 그렇지 않으면 오류가 있습니다.


8

이렇게하면 매우 큰 파일로 관리하는 방법 (100G까지 테스트). 그리고 fgets ()보다 빠릅니다.

$block =1024*1024;//1MB or counld be any higher than HDD block_size*2
if ($fh = fopen("file.txt", "r")) { 
    $left='';
    while (!feof($fh)) {// read the file
       $temp = fread($fh, $block);  
       $fgetslines = explode("\n",$temp);
       $fgetslines[0]=$left.$fgetslines[0];
       if(!feof($fh) )$left = array_pop($lines);           
       foreach ($fgetslines as $k => $line) {
           //do smth with $line
        }
     }
}
fclose($fh);

줄 중간에서 1024 * 1024 블록이 깨지지 않도록하려면 어떻게해야합니까?
user151496

1
@ user151496 쉽게 !! count ... 1.2.3.4
Omar El Don

@OmarElDon ​​무슨 뜻인가요?
Codex73

7

이 질문에 대한 인기있는 솔루션 중 하나는 줄 바꾸기 문자에 문제가 있습니다. 간단하게 간단하게 고정 할 수 있습니다 str_replace.

$handle = fopen("some_file.txt", "r");
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        $line = str_replace("\n", "", $line);
    }
    fclose($handle);
}

6

SplFileObject는 큰 파일을 다룰 때 유용합니다.

function parse_file($filename)
{
    try {
        $file = new SplFileObject($filename);
    } catch (LogicException $exception) {
        die('SplFileObject : '.$exception->getMessage());
    }
    while ($file->valid()) {
        $line = $file->fgets();
        //do something with $line
    }

    //don't forget to free the file handle.
    $file = null;
}

1
<?php
echo '<meta charset="utf-8">';

$k= 1;
$f= 1;
$fp = fopen("texttranslate.txt", "r");
while(!feof($fp)) {
    $contents = '';
    for($i=1;$i<=1500;$i++){
        echo $k.' -- '. fgets($fp) .'<br>';$k++;
        $contents .= fgets($fp);
    }
    echo '<hr>';
    file_put_contents('Split/new_file_'.$f.'.txt', $contents);$f++;
}
?>

-8

배열 반환으로 읽을 함수

function read_file($filename = ''){
    $buffer = array();
    $source_file = fopen( $filename, "r" ) or die("Couldn't open $filename");
    while (!feof($source_file)) {
        $buffer[] = fread($source_file, 4096);  // use a buffer of 4KB
    }
    return $buffer;
}

4
이렇게하면 행 단위가 아닌 임의의 4096 문자 청크로 분할 된 메모리 (행운이 있음)에 1GB 이상의 단일 배열이 생성됩니다. 왜 지구상에서 그렇게 하시겠습니까?
FrancescoMM
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.