Lặp lại từng dòng trong một chuỗi trong PHP


130

Tôi có một biểu mẫu cho phép người dùng tải lên tệp văn bản hoặc sao chép / dán nội dung của tệp vào văn bản. Tôi có thể dễ dàng phân biệt giữa hai và đặt bất kỳ cái nào chúng nhập vào một biến chuỗi, nhưng tôi sẽ đi đâu từ đó?

Tôi cần lặp lại qua từng dòng của chuỗi (tốt nhất là không lo lắng về các dòng mới trên các máy khác nhau), đảm bảo rằng nó có chính xác một mã thông báo (không có dấu cách, tab, dấu phẩy, v.v.), vệ sinh dữ liệu, sau đó tạo truy vấn SQL dựa trên tất cả các dòng

Tôi là một lập trình viên khá giỏi, vì vậy tôi biết ý tưởng chung về cách thực hiện nó, nhưng đã quá lâu kể từ khi tôi làm việc với PHP đến nỗi tôi cảm thấy mình đang tìm kiếm những thứ sai và do đó xuất hiện những thông tin vô dụng. Vấn đề chính tôi gặp phải là tôi muốn đọc nội dung của từng chuỗi. Nếu nó là một tập tin, nó sẽ dễ dàng.

Tôi chủ yếu tìm kiếm các hàm PHP hữu ích, không phải là thuật toán để làm điều đó. Bất kỳ đề xuất?


Bạn có thể muốn bình thường hóa các dòng mới đầu tiên. Phương pháp s($myString)->normalizeLineEndings()này có sẵn với github.com/delight-im/PHP-Str (thư viện theo Giấy phép MIT) có rất nhiều trình trợ giúp chuỗi hữu ích khác. Bạn có thể muốn xem mã nguồn.
caw

Câu trả lời:


188

preg_split biến chứa văn bản và lặp qua mảng trả về:

foreach(preg_split("/((\r?\n)|(\r\n?))/", $subject) as $line){
    // do stuff with $line
} 

Điều này sẽ xử lý ^ M ngoài \ n \ r?
Topher Fangio

Tôi không chắc chắn nếu lợi nhuận vận chuyển ascii được chuyển đổi thành \ r một khi nó được đặt bên trong một biến. Nếu không, bạn luôn có thể sử dụng dấu tách () / exlope () với giá trị ascii thay thế - ch (13)
Kyril

12
Một regrec tốt hơn là /((\r?\n)|(\r\n?))/.
Félix Saparelli

3
Để phù hợp với Unix LF (\ n), MacOS <9 CR (\ r), Windows CR + LF (\ r \ n) và hiếm gặp LF + CR (\ n \ r):/((\r?\n)|(\n?\r))/
Đang chờ Dev ...

2
Điều này có khả năng đánh bom thảm khốc cho dữ liệu nhiều byte.
pguardiario

156

Tôi muốn đề xuất một giải pháp thay thế nhanh hơn đáng kể (và hiệu quả bộ nhớ): strtokhơn là preg_split.

$separator = "\r\n";
$line = strtok($subject, $separator);

while ($line !== false) {
    # do something with $line
    $line = strtok( $separator );
}

Kiểm tra hiệu năng, tôi đã lặp lại 100 lần trong một tệp thử nghiệm với 17 nghìn dòng: preg_splitmất 27,7 giây, trong khi strtokmất 1,4 giây.

Lưu ý rằng mặc dù $separatorđược định nghĩa là "\r\n", strtoksẽ phân tách trên một trong hai ký tự - và kể từ PHP4.1.0, bỏ qua các dòng / mã thông báo trống.

Xem mục hướng dẫn sử dụng strtok: http://php.net/strtok


21
+1 để xem xét hiệu suất khi xử lý các bộ dòng lớn.
CodeAngry

4
Mặc dù chức năng này api là một mớ hỗn độn (cuộc gọi với các tham số khác nhau) đây là giải pháp tốt nhất. Không nên prey_splitcũng không explodeđược sử dụng để mang lại các đoạn chuỗi có cấu trúc. Nó giống như nhắm đến một con ruồi với bazooka .
Maciej Sz

1
Nếu bạn kiểm tra mức sử dụng bộ nhớ trong khi ứng dụng đang chạy, thì bạn sẽ thấy điều kỳ diệu. Nó thực sự kéo tập tin bạn đang đọc vào bộ nhớ trong trường hợp bạn lặp qua từng dòng nó giữ vị trí mã thông báo của bạn. Bạn sẽ muốn tuôn ra điều đó để thực sự hiệu quả bộ nhớ. php.net/strtok#103051
Tuyệt

2
lưu ý nhanh chóng, sử dụng strtok()vào một cái gì đó khác trong whilevòng lặp đó sẽ phá vỡ mọi thứ. Tôi cũng đang sử dụng nó để lấy mọi thứ trong một chuỗi lên đến không gian đầu tiên ( stackoverflow.com/a/2477411/1767412 ) và tôi mất một phút để nhận ra lý do tại sao mọi thứ không theo kế hoạch
billynoah

1
nên là câu trả lời được chấp nhận, có lẽ là giải pháp nhanh nhất từ ​​tất cả các lựa chọn.
Giăng

94

Nếu bạn cần xử lý các dòng mới trong các hệ thống khác nhau, bạn chỉ cần sử dụng hằng số PHP_EOL được xác định trước (http://php.net/manual/en/reserved.constants.php) và chỉ cần sử dụng trình nổ để tránh chi phí hoạt động của công cụ biểu thức chính quy .

$lines = explode(PHP_EOL, $subject);

30
Chú ý: Nó sẽ hoạt động trên các hệ thống khác nhau nhưng nó sẽ không hoạt động tốt với các chuỗi từ các hệ thống khác nhau . Các PHP Manual bang mà PHP_EOL (string)sự đúng 'End Of Line' biểu tượng cho này nền tảng.
wadim

@wadim nói đúng! Nếu bạn đang xử lý tệp văn bản Windows trên máy chủ Unix, nó sẽ thất bại.
javsmo

1
Xin lưu ý rằng tùy thuộc vào độ dài của các dòng của bạn, điều này có thể ăn một lượng bộ nhớ rất lớn cho các chuỗi lớn.
Synchro

Lưu ý rằng nếu dòng cuối cùng chứa một bộ kết thúc dòng, thì dòng này cũng sẽ trả về một chuỗi trống khác sau đó.
đúng vào

20

Nó quá phức tạp và xấu xí nhưng theo tôi đây là cách để đi:

$fp = fopen("php://memory", 'r+');
fputs($fp, $data);
rewind($fp);
while($line = fgets($fp)){
  // deal with $line
}
fclose($fp);

1
+1 và bạn cũng có thể sử dụng php://tempđể lưu trữ dữ liệu lớn hơn vào tệp đĩa tạm thời.
CodeAngry

4
Cần lưu ý rằng điều này cho phép bạn phát hiện các dòng trống, không giống như giải pháp strtok (). Tài liệu có tại php.net/manual/en/ trộm
Josip Rodin

7
foreach(preg_split('~[\r\n]+~', $text) as $line){
    if(empty($line) or ctype_space($line)) continue; // skip only spaces
    // if(!strlen($line = trim($line))) continue; // or trim by force and skip empty
    // $line is trimmed and nice here so use it
}

^ đây là cách bạn ngắt dòng đúng cách , tương thích đa nền tảng với Regexp:)


6

Vấn đề bộ nhớ tiềm năng với strtok:

Vì một trong những giải pháp được đề xuất sử dụng strtok, thật không may, nó không chỉ ra vấn đề bộ nhớ tiềm ẩn (mặc dù nó được cho là hiệu quả bộ nhớ). Khi sử dụng strtoktheo hướng dẫn , các:

Lưu ý rằng chỉ có lệnh gọi đầu tiên đến strtok sử dụng đối số chuỗi. Mỗi cuộc gọi tiếp theo đến strtok chỉ cần mã thông báo để sử dụng, vì nó theo dõi vị trí của chuỗi trong chuỗi hiện tại.

Nó thực hiện điều này bằng cách tải tập tin vào bộ nhớ. Nếu bạn đang sử dụng các tệp lớn, bạn cần xóa chúng nếu bạn hoàn thành việc lặp qua tệp.

<?php
function process($str) {
    $line = strtok($str, PHP_EOL);

    /*do something with the first line here...*/

    while ($line !== FALSE) {
        // get the next line
        $line = strtok(PHP_EOL);

        /*do something with the rest of the lines here...*/

    }
    //the bit that frees up memory
    strtok('', '');
}

Nếu bạn chỉ quan tâm đến các tệp vật lý (ví dụ: datamining):

Theo hướng dẫn , đối với phần tải lên tệp, bạn có thể sử dụng filelệnh:

 //Create the array
 $lines = file( $some_file );

 foreach ( $lines as $line ) {
   //do something here.
 }

4

Câu trả lời của Kyril là tốt nhất khi bạn cần để có thể xử lý các dòng mới trên các máy khác nhau.

"Tôi chủ yếu tìm kiếm các hàm PHP hữu ích, không phải là thuật toán để làm điều đó. Bạn có đề xuất nào không?"

Tôi sử dụng rất nhiều:

  • nổ tung() có thể được sử dụng để phân tách một chuỗi thành một mảng, được cung cấp một dấu phân cách duy nhất.
  • implode () là bản sao của explode, để đi từ mảng trở lại chuỗi.
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.