Làm thế nào để đọc một dòng tập tin lớn theo dòng?


469

Tôi muốn đọc từng dòng tệp, nhưng không tải hoàn toàn vào bộ nhớ.

Tệp của tôi quá lớn để mở trong bộ nhớ và nếu cố gắng làm như vậy tôi luôn thoát khỏi lỗi bộ nhớ.

Kích thước tệp là 1 GB.


xem câu trả lời của tôi tại liên kết
Sohail Ahmed

7
Bạn nên sử dụng fgets()mà không cần $lengththam số.
Carlos

26
Bạn có muốn đánh dấu là câu trả lời trên bất kỳ sau đây?
Kim Stacks

Câu trả lời:


684

Bạn có thể sử dụng fgets()chức năng để đọc từng dòng tệp:

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

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

3
Làm thế nào để tài khoản này cho too large to open in memorymột phần?
Starx

64
Bạn không đọc toàn bộ tập tin trong bộ nhớ. Bộ nhớ tối đa cần thiết để chạy này phụ thuộc vào dòng dài nhất trong đầu vào.
codaddict

13
@Brandin - Moot - Trong những tình huống đó, câu hỏi được hỏi, đó là đọc một tệp LINE BY LINE, không có kết quả được xác định rõ.
ToolmakerSteve

3
@ToolmakerSteve Sau đó, xác định những gì sẽ xảy ra. Nếu bạn muốn, bạn chỉ có thể in thông báo "Dòng quá dài; bỏ cuộc." và đó là một kết quả được xác định rõ quá.
Brandin

2
Một dòng có thể chứa một boolean sai? Nếu vậy thì phương pháp này sẽ dừng lại mà không đến cuối tập tin. Ví dụ # 1 trên URL này php.net/manual/en/feft.fgets.php cho thấy rằng đôi khi các fgets có thể trả về boolean false ngay cả khi chưa kết thúc tệp. Trong phần bình luận trên trang đó, mọi người báo cáo rằng fgets () không phải lúc nào cũng trả về các giá trị chính xác, vì vậy sẽ an toàn hơn khi sử dụng yếu tố như vòng lặp có điều kiện.
cjohansson

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

8
Như @ Cuse70 đã nói trong câu trả lời của mình, điều này sẽ dẫn đến một vòng lặp vô hạn nếu tệp không tồn tại hoặc không thể mở được. Kiểm tra if($file)trước vòng lặp while
FrancescoMM

10
Tôi biết điều này đã cũ, nhưng: không nên sử dụng while (! Feof ($ file)). Có một cái nhìn ở đây.
Kevin Van Ryckegem

BTW: "Nếu không có thêm dữ liệu để đọc trong con trỏ tệp, thì FALSE được trả về." php.net/manual/en/feft.fgets.php ... Chỉ trong trường hợp
người

2
feof()không tồn tại nữa?
Ryan DuVal

94

Bạn có thể sử dụng một lớp giao diện hướng đối tượng cho một tệp - 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
giải pháp sạch hơn nhiều. nhờ;) đã không sử dụng lớp học này, có những chức năng thú vị hơn ở đây để khám phá: php.net/manual/en/class.splfileobject.php
Lukas Liesis

6
Cảm ơn. Có, ví dụ: bạn có thể thêm dòng này trước trong khi $ file-> setFlags (SplFileObject :: DROP_NEW_LINE); để thả dòng mới ở cuối dòng.
elshnkhll

Theo như tôi có thể thấy không có eof()chức năng nào trong SplFileObject?
Chud37

3
Cảm ơn! Ngoài ra, sử dụng rtrim($file->fgets())để tách các dòng mới theo dõi cho từng chuỗi dòng được đọc nếu bạn không muốn chúng.
racl101


59

Nếu bạn đang mở một tệp lớn, có lẽ bạn muốn sử dụng Trình tạo cùng với fgets () để tránh tải toàn bộ tệp vào bộ nhớ:

/**
 * @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);
};

Sử dụng nó như thế này:

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

Bằng cách này, bạn có thể xử lý các dòng tệp riêng lẻ bên trong foreach ().

Lưu ý: Trình tạo yêu cầu> = PHP 5.5


3
Đây nên là một câu trả lời được chấp nhận thay thế. Nó nhanh hơn hàng trăm lần với máy phát điện.
Tachi

1
Và waaay hiệu quả bộ nhớ hơn.
Nino kopac

2
@ NinoŠkopac: Bạn có thể giải thích tại sao giải pháp này hiệu quả hơn về bộ nhớ không? Ví dụ, so với SplFileObjectcách tiếp cận.
k00ni

30

Sử dụng các kỹ thuật đệm để đọc tệp.

$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
điều này xứng đáng được yêu thích hơn, vì nó sẽ hoạt động với các tệp lớn, thậm chí các tệp không có lợi nhuận vận chuyển hoặc dòng quá dài ...
Jimmery

Tôi sẽ không ngạc nhiên nếu OP không thực sự quan tâm đến các dòng thực tế và chỉ muốn ví dụ như phục vụ tải xuống. Trong trường hợp đó, câu trả lời này là tốt (và hầu hết các lập trình viên PHP sẽ làm gì).
Álvaro González

30

Có một file()hàm trả về một mảng các dòng có trong tệp.

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

28
Tất cả các tệp một GB sẽ được đọc vào bộ nhớ và được chuyển đổi thành một mảng nhiều hơn một GB ... chúc may mắn.
FrancescoMM

4
Đây không phải là câu trả lời cho câu hỏi được hỏi, nhưng nó trả lời câu hỏi phổ biến hơn mà nhiều người có khi nhìn vào đây, vì vậy nó vẫn hữu ích, cảm ơn.
pilavdzice

2
file () rất thuận tiện để làm việc với các tệp nhỏ. Đặc biệt là khi bạn muốn một mảng () là kết quả cuối cùng.
chức năng

đây là một ý tưởng tồi với các tệp lớn hơn vì toàn bộ tệp đang được đọc vào một mảng
Flash Thunder

Điều này phá vỡ nghiêm trọng trên các tệp lớn, vì vậy nó chính xác là phương pháp không hoạt động.
ftrotter


17

Câu trả lời rõ ràng không có trong tất cả các câu trả lời.
PHP có một trình phân tích cú pháp phân tách trực tuyến gọn gàng có sẵn cho chính xác mục đích đó.

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

Cần lưu ý rằng mã này sẽ chỉ trả về các dòng cho đến khi dòng trống đầu tiên xảy ra. Bạn cần kiểm tra $ line! == false trong điều kiện whilewhile (($line = stream_get_line($fp, 1024 * 1024, "\n")) !== false)
cebe

8

Hãy cẩn thận với công cụ 'while (! Feof ... fgets ()', fgets có thể bị lỗi (returnfing false) và lặp mãi mãi mà không đến cuối tập tin. Codaddict gần nhất là chính xác nhưng khi 'trong khi fget' kết thúc vòng lặp, kiểm tra thông tin, nếu không đúng, thì bạn đã gặp lỗi.


8

Đây là cách tôi quản lý với tệp rất lớn (được thử nghiệm với tối đa 100G). Và nó nhanh hơn 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);

Làm thế nào để bạn đảm bảo rằng khối 1024 * 1024 không bị vỡ ở giữa dòng?
dùng151496

1
@ user151496 dễ !! đếm ... 1.2.3.4
Omar El Don

@OmarElDon ​​ý bạn là gì?
Codex73

7

Một trong những giải pháp phổ biến cho câu hỏi này sẽ có vấn đề với nhân vật dòng mới. Nó có thể được sửa chữa khá dễ dàng với một đơn giản str_replace.

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

6

SplFileObject rất hữu ích khi xử lý các tệp lớn.

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

Chức năng đọc với trả về mảng

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
Điều này sẽ tạo ra một mảng duy nhất có nhiều hơn một GB trong bộ nhớ (chúc may mắn với nó) được chia không chỉ trong các dòng mà là các khối ký tự 4096 tùy ý. Tại sao bạn muốn làm điều đó trên trái đất?
FrancescoMM
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.