PHP'de bir dizedeki her satırı yineleyin


130

Kullanıcının bir metin dosyası yüklemesine veya dosyanın içeriğini bir metin alanına kopyalayıp / yapıştırmasına izin veren bir formum var. İkisini kolayca ayırt edebilir ve hangisini bir string değişkenine girmişlerse koyabilirim, ama oradan nereye gideceğim?

Dizenin her satırını yinelemeliyim (tercihen farklı makinelerdeki satırsonları hakkında endişelenmemeliyim), tam olarak bir jetona sahip olduğundan (boşluk, sekme, virgül vb.) Emin olmalı, verileri temizlemem ve ardından bir SQL sorgusu oluşturmam gerekiyor tüm satırlara dayalı.

Oldukça iyi bir programcıyım, bu yüzden nasıl yapılacağına dair genel fikri biliyorum, ancak PHP ile çalışmayalı o kadar uzun zaman oldu ki, yanlış şeyler aradığımı ve bu yüzden işe yaramaz bilgilerle geldiğimi hissediyorum. Yaşadığım temel sorun, dizenin içeriğini satır satır okumak istemem. Bir dosya olsaydı, kolay olurdu.

Çoğunlukla yararlı PHP işlevleri arıyorum, nasıl yapılacağına dair bir algoritma değil. Baska öneri?


Önce yeni satırları normalleştirmek isteyebilirsiniz. Yöntem s($myString)->normalizeLineEndings(), birçok başka yararlı dizgi yardımcıları olan github.com/delight-im/PHP-Str (MIT Lisansı altındaki kitaplık) ile kullanılabilir. Kaynak koduna bir göz atmak isteyebilirsiniz.
2016

Yanıtlar:


190

preg_split metni içeren değişken ve döndürülen dizi üzerinde yineleme:

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

Bu, \ n \ r'ye ek olarak ^ M'yi de işleyecek mi?
Topher Fangio

Bir değişkenin içine yerleştirildiğinde ascii taşıma dönüşünün \ r'ye dönüştürülüp dönüştürülmediğinden emin değilim. Değilse, bunun yerine ascii değeriyle her zaman bir split () / exlope () kullanabilirsiniz - ch (13)
Kyril

12
Daha iyi bir normal ifade /((\r?\n)|(\r\n?))/.
Félix Saparelli

3
Unix LF (\ n), MacOS <9 CR (\ r), Windows CR + LF (\ r \ n) ve nadir LF + CR (\ n \ r) ile /((\r?\n)|(\n?\r))/
eşleşmek için şunlar

2
Bu, çok baytlı veriler için felaketle sonuçlanacak gibi görünüyor.
pguardiario

158

Ben önermek istiyorum anlamlı : Daha hızlı (ve bellek verimli) alternatif strtokziyade preg_split.

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

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

Performansı test ederken, 17 bin satırlık bir test dosyası üzerinden 100 kez yineledim: preg_split27.7 saniye, strtok1.4 saniye sürdü.

Olsa bu Not $separatorolarak tanımlanır "\r\n", strtokve PHP4.1.0 itibarıyla boş satır / belirteçleri atlama - ya karakterine ayrı olacaktır.

Strtok manuel girişine bakın: http://php.net/strtok


21
Büyük hat kümeleriyle uğraşırken performans değerlendirmeleri için +1 .
CodeAngry

4
Bu işlev api tam bir karmaşa olsa da (farklı parametrelerle arama) bu en iyi çözümdür. Ne prey_splitde explodeyapılandırılmış dize kesimleri veren için kullanılmalıdır. Bazukayla uçmak gibi .
Maciej Sz

1
Uygulama çalışırken bellek kullanımını kontrol ederseniz, sihri göreceksiniz. Aslında sen çizgilerin her birinin içinden durumunda size döngü içinde belleğe okuyorsanız dosyayı çeker, ve bu sizin jeton yerini tutar. Gerçekten bellek açısından verimli olması için bunu temizlemek isteyeceksiniz. php.net/strtok#103051
AbsoluteƵERØ

2
hızlı not, strtok()bu whiledöngü içinde başka bir şey kullanmak işleri bozacaktır. Ayrıca, ilk boşluğa kadar ( stackoverflow.com/a/2477411/1767412 ) her şeyi bir dizede toplamak için kullanıyordum ve işlerin neden planlandığı gibi
gitmediğini anlamam

1
Kabul edilen cevap, muhtemelen tüm seçeneklerden en hızlı çözüm olmalıdır.
John

94

Farklı sistemlerde yeni satırları işlemeniz gerekiyorsa, önceden tanımlanmış PHP_EOL sabitini (http://php.net/manual/en/reserved.constants.php) kullanabilir ve normal ifade motorunun ek yükünden kaçınmak için patlatabilirsiniz. .

$lines = explode(PHP_EOL, $subject);

30
Dikkat: İşe yarayacak farklı sistemlerde ancak dizeleri ile iyi çalışmaz farklı sistemlerden . PHP Manual devletler PHP_EOL (string)olduğu için sembolü doğru 'Çizgi Sonu' bu platformda.
wadim

@wadim haklı! Bir Unix sunucusunda bir Windows metin dosyası işliyorsanız, başarısız olur.
javsmo

1
Satırlarınızın uzunluğuna bağlı olarak, bunun büyük dizeler için çok büyük miktarda bellek tüketebileceğini unutmayın.
Synchro

Son satır bir satır sonlandırıcı içeriyorsa, bu da bundan sonra başka bir boş dize döndürecektir.
18:57

20

Aşırı karmaşık ve çirkin ama benim görüşüme göre gidilecek yol bu:

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

1
+1 ve php://tempdaha büyük verileri geçici disk dosyasına depolamak için de kullanabilirsiniz .
CodeAngry

4
Bunun strtok () çözümünden farklı olarak boş satırları tespit etmenize izin verdiğine dikkat edilmelidir. Belgeler php.net/manual/en/… adresindedir
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
}

^ satırları böyle düzgün bir şekilde kırarsınız , platformlar arası uyumlu Regexp:)


6

Olası bellek sorunları strtok:

Önerilen çözümlerden biri kullandığından strtok, ne yazık ki potansiyel bir bellek sorununa işaret etmiyor (bellek verimli olduğunu iddia etse de). Kılavuzastrtok göre kullanıldığında :

Yalnızca strtok çağrısının dize bağımsız değişkenini kullandığına dikkat edin. Her sonraki strtok çağrısı , geçerli dizede nerede olduğunu izlediği için yalnızca belirtecin kullanılmasına ihtiyaç duyar .

Bunu, dosyayı belleğe yükleyerek yapar. Büyük dosyalar kullanıyorsanız, dosya içinde döngüyü tamamladıysanız onları temizlemeniz gerekir.

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

Yalnızca fiziksel dosyalarla ilgileniyorsanız (örn. Veri madenciliği):

Kılavuza göre , dosya yükleme kısmı için şu filekomutu kullanabilirsiniz :

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

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

4

Kyril'in cevabı en iyisi, farklı makinelerde yeni satırları kullanabilmeniz gerektiğini düşünerek.

"Çoğunlukla yararlı PHP işlevleri arıyorum, nasıl yapılacağına dair bir algoritma değil. Herhangi bir öneriniz var mı?"

Bunları çok kullanıyorum:

  • explode () , tek bir sınırlayıcı verildiğinde bir dizeyi bir diziye bölmek için kullanılabilir.
  • implode (), diziden dizgeye geri dönmek için patlamanın karşılığıdır.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.