Durchlaufen Sie jede Zeile in einer Zeichenfolge in PHP

130

Ich habe ein Formular, mit dem der Benutzer entweder eine Textdatei hochladen oder den Inhalt der Datei in einen Textbereich kopieren / einfügen kann. Ich kann leicht zwischen den beiden unterscheiden und das, was sie eingegeben haben, in eine Zeichenfolgenvariable einfügen, aber wohin gehe ich von dort aus?

Ich muss über jede Zeile der Zeichenfolge iterieren (vorzugsweise keine Gedanken über Zeilenumbrüche auf verschiedenen Computern machen), sicherstellen, dass genau ein Token vorhanden ist (keine Leerzeichen, Tabulatoren, Kommas usw.), die Daten bereinigen und dann eine SQL-Abfrage generieren basierend auf allen Zeilen.

Ich bin ein ziemlich guter Programmierer, daher kenne ich die allgemeine Idee, wie es geht, aber es ist so lange her, dass ich mit PHP gearbeitet habe, dass ich das Gefühl habe, nach den falschen Dingen zu suchen und so nutzlose Informationen zu finden. Das Hauptproblem, das ich habe, ist, dass ich den Inhalt der Zeichenfolge Zeile für Zeile lesen möchte. Wenn es eine Datei wäre, wäre es einfach.

Ich suche hauptsächlich nach nützlichen PHP-Funktionen, nicht nach einem Algorithmus dafür. Irgendwelche Vorschläge?

Topher Fangio
quelle
Möglicherweise möchten Sie zuerst die Zeilenumbrüche normalisieren. Die Methode s($myString)->normalizeLineEndings()ist mit github.com/delight-im/PHP-Str (Bibliothek unter MIT-Lizenz) verfügbar, die viele andere nützliche String-Helfer enthält. Vielleicht möchten Sie einen Blick auf den Quellcode werfen.
Caw

Antworten:

188

preg_split die Variable, die den Text enthält, und iterieren über das zurückgegebene Array:

foreach(preg_split("/((\r?\n)|(\r\n?))/", $subject) as $line){
    // do stuff with $line
} 
Kyril
quelle
Wird dies zusätzlich zu \ n \ r ^ M behandeln?
Topher Fangio
Ich bin nicht sicher, ob der ASCII-Wagenrücklauf in \ r konvertiert wird, sobald er in einer Variablen platziert ist. Wenn nicht, können Sie stattdessen immer einen split () / exlope () mit dem ASCII-Wert verwenden - ch (13)
Kyril
12
Ein besserer regulärer Ausdruck ist /((\r?\n)|(\r\n?))/.
Félix Saparelli
3
Um mit Unix LF (\ n), MacOS <9 CR (\ r), Windows CR + LF (\ r \ n) und seltenem LF + CR (\ n \ r) übereinzustimmen, sollte es sein:/((\r?\n)|(\n?\r))/
Warten auf Dev ...
2
Dies wird wahrscheinlich katastrophal für Multi-Byte-Daten bombardieren.
Pguardiario
156

Ich möchte eine deutlich schnellere (und speichereffiziente) Alternative vorschlagen : strtokanstatt preg_split.

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

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

Beim Testen der Leistung habe ich 100 Mal eine Testdatei mit 17.000 Zeilen durchlaufen: preg_split27,7 Sekunden, strtok1,4 Sekunden.

Beachten Sie, dass, obwohl das $separatorals definiert ist "\r\n", strtokbei beiden Zeichen getrennt wird - und ab PHP4.1.0 leere Zeilen / Token überspringen.

Siehe den strtok-Handbucheintrag: http://php.net/strtok

Erwin Wessels
quelle
21
+1 für Leistungsüberlegungen beim Umgang mit großen Leitungssätzen.
CodeAngry
4
Obwohl diese Funktions-API ein totales Durcheinander ist (Aufruf mit verschiedenen Parametern), ist dies die beste Lösung. Weder prey_splitnoch explodesollte verwendet werden, um strukturierte String-Fragmente zu erhalten. Es ist , als würde man mit einer Panzerfaust auf eine Fliege zielen .
Maciej Sz
1
Wenn Sie die Speichernutzung überprüfen, während die App ausgeführt wird, werden Sie die Magie sehen. Es zieht tatsächlich die Datei, die Sie lesen, in den Speicher, falls Sie jede der Zeilen durchlaufen, und behält Ihren Token-Speicherort bei. Sie sollten das leeren, um wirklich speichereffizient zu sein. php.net/strtok#103051
AbsoluteƵERØ
2
Kurzer Hinweis: Wenn Sie strtok()etwas anderes in dieser whileSchleife verwenden, werden die Dinge kaputt gehen. Ich benutzte es auch, um alles in einer Schnur bis zum ersten Leerzeichen zu packen ( stackoverflow.com/a/2477411/1767412 ) und brauchte eine Minute, um zu erkennen, warum die Dinge nicht wie geplant
liefen
1
sollte die akzeptierte Antwort sein, wahrscheinlich die schnellste Lösung aus allen Optionen.
John
94

Wenn Sie Zeilenumbrüche in verschiedenen Systemen verarbeiten müssen, können Sie einfach die vordefinierte PHP-Konstante PHP_EOL (http://php.net/manual/en/reserved.constants.php) verwenden und einfach explode verwenden, um den Overhead der Engine für reguläre Ausdrücke zu vermeiden .

$lines = explode(PHP_EOL, $subject);
FerCa
quelle
30
Achtung: Es funktioniert auf verschiedenen Systemen, aber nicht gut mit Zeichenfolgen aus verschiedenen Systemen . Die PHP Manual besagt , dass PHP_EOL (string)ist die richtige ‚End Of Line‘ Symbol für diese Plattform.
Wadim
@ Wadim ist richtig! Wenn Sie eine Windows-Textdatei auf einem Unix-Server verarbeiten, schlägt dies fehl.
Javsmo
1
Beachten Sie, dass dies abhängig von der Länge Ihrer Zeilen sehr viel Speicher für große Zeichenfolgen verbrauchen kann.
Synchro
Beachten Sie, dass, wenn die letzte Zeile einen Zeilenabschluss enthält, danach auch eine weitere leere Zeichenfolge zurückgegeben wird.
Rechtsfalte
20

Es ist zu kompliziert und hässlich, aber meiner Meinung nach ist dies der richtige Weg:

$fp = fopen("php://memory", 'r+');
fputs($fp, $data);
rewind($fp);
while($line = fgets($fp)){
  // deal with $line
}
fclose($fp);
pguardiario
quelle
1
+1 und Sie können auch php://tempgrößere Daten in einer temporären Festplattendatei speichern.
CodeAngry
4
Es ist zu beachten, dass Sie damit im Gegensatz zur strtok () -Lösung leere Zeilen erkennen können. Die Dokumentation ist bei php.net/manual/en/…
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
}

^ So brechen Sie Linien richtig , plattformübergreifend kompatibel mit Regexp:)

CodeAngry
quelle
6

Mögliche Speicherprobleme mit strtok:

Da eine der vorgeschlagenen Lösungen verwendet wird, strtokweist sie leider nicht auf ein potenzielles Speicherproblem hin (obwohl sie behauptet, speichereffizient zu sein). Bei Verwendung strtokgemäß dem Handbuch gilt Folgendes :

Beachten Sie, dass nur der erste Aufruf von strtok das Zeichenfolgenargument verwendet. Jeder nachfolgende Aufruf von strtok benötigt nur das zu verwendende Token, da es verfolgt, wo es sich in der aktuellen Zeichenfolge befindet.

Dazu wird die Datei in den Speicher geladen. Wenn Sie große Dateien verwenden, müssen Sie diese leeren, wenn Sie die Datei durchlaufen haben.

<?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('', '');
}

Wenn Sie sich nur mit physischen Dateien befassen (z. B. Datenerfassung):

Laut Handbuch können Sie für den Teil zum Hochladen von Dateien den folgenden fileBefehl verwenden:

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

 foreach ( $lines as $line ) {
   //do something here.
 }
Absoluter Nullpunkt
quelle
4

Die Antwort von Kyril ist am besten, wenn man bedenkt, dass man in der Lage sein muss, Zeilenumbrüche auf verschiedenen Maschinen zu verarbeiten.

"Ich suche hauptsächlich nach nützlichen PHP-Funktionen, nicht nach einem Algorithmus dafür. Irgendwelche Vorschläge?"

Ich benutze diese oft:

  • explode () kann verwendet werden, um eine Zeichenfolge mit einem einzelnen Trennzeichen in ein Array aufzuteilen.
  • implode () ist das Gegenstück von explode, um vom Array zurück zum String zu gelangen.
Joe Kiley
quelle