2012-06-06 48 views
5

Tôi có một tệp văn bản 1,3 GB mà tôi cần trích xuất một số thông tin từ trong PHP. Tôi đã nghiên cứu nó và đã đưa ra một vài cách khác nhau để làm những gì tôi cần làm, nhưng như mọi khi sau khi làm rõ một chút về phương pháp nào là tốt nhất hoặc nếu một cách khác tốt hơn mà tôi không biết?Cách tốt nhất để trích xuất văn bản từ tệp văn bản 1.3GB bằng PHP?

Thông tin tôi cần trong tệp văn bản chỉ là 40 ký tự đầu tiên của mỗi dòng và có khoảng 17 triệu dòng trong tệp. 40 ký tự từ mỗi dòng sẽ được chèn vào cơ sở dữ liệu.

Các phương pháp tôi có bên dưới;

// REMOVE TIME LIMIT 
set_time_limit(0); 
// REMOVE MEMORY LIMIT 
ini_set('memory_limit', '-1'); 
// OPEN FILE 
$handle = @fopen('C:\Users\Carl\Downloads\test.txt', 'r'); 
if($handle) { 
    while(($buffer = fgets($handle)) !== false) { 
     $insert[] = substr($buffer, 0, 40); 
    } 
    if(!feof($handle)) { 
     // END OF FILE 
    } 
    fclose($handle); 
} 

Trên đây là đọc từng dòng tại một thời điểm và nhận được dữ liệu, tôi có tất cả các cơ sở dữ liệu được sắp xếp chèn, làm 50 chèn vào một thời điểm mười lần trong một giao dịch.

Phương pháp tiếp theo giống như trên thực tế nhưng gọi file() để lưu trữ tất cả các dòng trong một mảng trước khi thực hiện foreach để nhận dữ liệu? Tôi không chắc chắn về phương pháp này mặc dù là mảng về cơ bản sẽ có hơn 17 triệu giá trị.

Phương pháp khác sẽ chỉ trích xuất một phần của tệp, ghi đè tệp bằng dữ liệu chưa sử dụng và sau khi phần đó đã được thực thi, hãy nhớ lại tập lệnh bằng cách sử dụng cuộc gọi header?

Cách tốt nhất để thực hiện điều này một cách nhanh chóng và hiệu quả nhất là gì? Hoặc là có một cách tốt hơn để tiếp cận điều này mà tôi đã nghĩ đến? Ngoài ra tôi có kế hoạch sử dụng kịch bản này với wamp, nhưng chạy nó trong một trình duyệt trong khi thử nghiệm đã gây ra vấn đề với thời gian chờ ngay cả với thiết lập thời gian kịch bản ra 0. Có cách nào tôi có thể thực thi kịch bản để chạy mà không cần truy cập vào trang thông qua trình duyệt?

+0

Đối với điểm cuối cùng, 'php path/to/script.php' sẽ thực thi tập lệnh. – sarnold

+0

@ sarnold tôi có làm điều đó từ dòng lệnh không? Cảm ơn – Griff

+1

Có, ngay từ dòng lệnh. Bạn cũng có thể biến nó thành một tệp kịch bản thực thi nếu bạn định thực thi nó thường xuyên bằng cách thêm '#!/Path/to/php' vào dòng đầu tiên của tập lệnh và sau đó chạy' chmod 755 path/to/script' hoặc 'chmod 500' hoặc bất kỳ điều khoản thích hợp nào bạn muốn. – sarnold

Trả lời

5

Bạn có nó tốt cho đến nay, không sử dụng "file()" chức năng vì nó rất có thể sẽ đạt giới hạn sử dụng RAM và chấm dứt kịch bản của bạn.

Tôi thậm chí sẽ không tích lũy công cụ vào mảng "chèn []", vì điều đó cũng sẽ lãng phí RAM. Nếu bạn có thể, hãy chèn vào cơ sở dữ liệu ngay lập tức.

BTW, có một công cụ tốt được gọi là "cắt" mà bạn có thể sử dụng để xử lý tệp.

cut -c1-40 file.txt 

Thậm chí bạn có thể chuyển hướng phần mềm giảm giá của đoạn mã tới một số tập lệnh PHP chèn vào cơ sở dữ liệu.

cut -c1-40 file.txt | php -f inserter.php 

inserter.php sau đó có thể đọc dòng từ php: // stdin và chèn vào DB.

"cắt" là một công cụ chuẩn có sẵn trên tất cả Linux, nếu bạn sử dụng Windows, bạn có thể lấy nó bằng trình bao MinGW, hoặc như một phần của msystools (nếu bạn sử dụng git) hoặc cài đặt ứng dụng win32 gốc bằng cách sử dụng gnuWin32.

+0

Sẽ không quá nhiều cho mysql để xử lý mặc dù? Làm một chèn 17million lần hoặc 50 tại một thời điểm? Sau khi chèn 50 mảng được đặt lại. – Griff

+0

@Griff, đó là truy cập mảng của PHP so với tốc độ chèn MySQL. Nó có thể nhanh hơn, nhưng nó cũng có thể chậm hơn. Cách duy nhất để kiểm tra đó sẽ là điểm chuẩn. Ngoài ra, bằng cách sử dụng câu lệnh INSERT chuẩn bị chỉ với các tham số thay đổi có thể hữu ích. –

+0

Cảm ơn bạn đã cung cấp thông tin này. Sẽ làm việc ngay lập tức và cho bạn biết làm thế nào tôi nhận được :) – Griff

2

Tại sao bạn làm điều này trong PHP khi RDBMS của bạn gần như chắc chắn có chức năng nhập hàng loạt được tích hợp sẵn? Ví dụ: MySQL có LOAD DATA INFILE:

LOAD DATA INFILE 'data.txt' 
INTO TABLE `some_table` 
    FIELDS TERMINATED BY '' 
    LINES TERMINATED BY '\n'; 
    (@line) 
SET `some_column` = LEFT(@line, 40); 

Một truy vấn.

MySQL cũng có tiện ích mysqlimport kết thúc tốt chức năng này từ dòng lệnh.

+0

Máy chủ chia sẻ của tôi không cho phép tôi sử dụng 'INFILE' đó là lựa chọn đầu tiên của tôi. – Griff

1

Không có mục nào ở trên. Vấn đề với việc sử dụng fgets() là nó không hoạt động như bạn mong đợi. Khi đạt đến các ký tự tối đa, cuộc gọi tiếp theo tới fgets() sẽ tiếp tục trên cùng một dòng. Bạn đã xác định chính xác sự cố khi sử dụng file(). Phương pháp thứ ba là một ý tưởng thú vị, và bạn có thể kéo nó ra với các giải pháp khác là tốt.

Điều đó nói rằng, ý tưởng đầu tiên của bạn về việc sử dụng fgets() là khá gần, tuy nhiên chúng tôi cần sửa đổi một chút hành vi của nó. Dưới đây là phiên bản tùy chỉnh sẽ hoạt động như bạn mong muốn:

function fgetl($fp, $len) { 
    $l = 0; 
    $buffer = ''; 
    while (false !== ($c = fgetc($fp)) && PHP_EOL !== $c) { 
     if ($l < $len) 
      $buffer .= $c; 
     ++$l; 
    } 
    if (0 === $l && false === $c) { 
     return false; 
    } 
    return $buffer; 
} 

Thực hiện thao tác chèn ngay lập tức hoặc bạn sẽ lãng phí bộ nhớ. Đảm bảo bạn đang sử dụng prepared statements để chèn nhiều hàng này; điều này sẽ làm giảm đáng kể thời gian thực hiện. Bạn không muốn gửi truy vấn đầy đủ trên mỗi lần chèn khi bạn chỉ có thể gửi dữ liệu.

+0

Đó vẫn là một vấn đề kể từ PHP 4.3.0? Ngoài ra, làm thế nào tốc độ sẽ được với 76 lần như nhiều cuộc gọi chức năng? – Wiseguy

+0

Tôi tin rằng đây là hành vi mong đợi của fgets(). Tốc độ không phải là vấn đề nếu bạn đang sử dụng các câu lệnh đã chuẩn bị (http://php.net/manual/en/pdo.prepared-statements.php) – siimsoni

+0

@KSiimson Tôi đang sử dụng câu lệnh chuẩn bị của PDO', @Wiseguy đây là những gì tôi nghĩ ommiting thuộc tính 'length' sẽ làm như tôi muốn? – Griff

Các vấn đề liên quan