30 milyon karakter uzunluğunda bir dize işlemek

5 Cevap php

Ben bir satıcıdan bir veri akışı olarak başka bir sunucudan bir CSV dosyası indirirken duyuyorum.

Ben dosyanın içeriğini almak için curl kullanarak ve adında bir değişken olduğunu kaydederek $contents duyuyorum.

Ben bu bölümü sadece para cezası alabilirsiniz, ancak ben çizgilerin bir dizisini almak için \r ve \n ile patlayan çalıştı ama o bir 'bellek' hatası ile başarısız olur.

I echo strlen($contents) ve yaklaşık 30,5 milyon karakter bulunuyor.

Ben değerlerini işlemek ve bir veritabanına bunları eklemek gerekir. Ne bellek ayırma hataları önlemek için ne yapmak gerekiyor?

5 Cevap

Bu bellek tükeniyor çünkü PHP boğulma. Bunun yerine kıvırmak dosyasının içeriği ile bir PHP değişkeni doldurmak sahip, kullanın

CURLOPT_FILE

yerine diske dosyayı kaydetmek için bir seçenek.

//pseudo, untested code	to give you the idea

$fp = fopen('path/to/save/file', 'w');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_exec ($ch);
curl_close ($ch);
fclose($fp);

Ardından, dosya yerine file veya file_get_contents işlevleri (yine PHP öldürme, belleğe tüm dosya yüklemek hangi), kullanın {[(2)] kullanarak, kaydedildikten sonra } ve fgets bir anda dosya bir satır okumak.

Diğer cevaplar dediği gibi:

  • Eğer bellekte tüm bu olamaz
  • çözeltisi kullanmak olacaktır CURLOPT_FILE

Ama, gerçekten bir dosya ne oluşturmak için olmayabilir; Eğer kısa sürede "geldiğinde" olarak kullanarak ... bellek veri ile çalışmak istiyor olabilir.

Olası bir çözüm akışı sarıcı kendi definind ve CURLOPT_FILE ile, yerine gerçek bir dosya, bu birini kullanın olabilir

Her şeyden önce, bkz


And now, let's go with an example.

Birincisi, bizim akım sarıcı sınıfı yapalım:

class MyStream {
    protected $buffer;

    function stream_open($path, $mode, $options, &$opened_path) {
        // Has to be declared, it seems...
        return true;
    }

    public function stream_write($data) {
        // Extract the lines ; on y tests, data was 8192 bytes long ; never more
        $lines = explode("\n", $data);

        // The buffer contains the end of the last line from previous time
        // => Is goes at the beginning of the first line we are getting this time
        $lines[0] = $this->buffer . $lines[0];

        // And the last line os only partial
        // => save it for next time, and remove it from the list this time
        $nb_lines = count($lines);
        $this->buffer = $lines[$nb_lines-1];
        unset($lines[$nb_lines-1]);

        // Here, do your work with the lines you have in the buffer
        var_dump($lines);
        echo '<hr />';

        return strlen($data);
    }
}

Ben ne olduğunu:

  • Ulaştığında (ben var_dump kullanmak, ancak bunun yerine her zamanki şeyler yapardım) veri parçaları üzerinde çalışmak
  • Eğer "dolu satırları" alamadım Not: Bir satır sonunda bir yığın bir başlangıcı olduğunu ve aynı hattın başlangıcı önceki öbek sonunda oldu; bu yüzden, size çağrıları arasında bir chunck bazı parçaları tutmak zorunda stream_write


Next, we register this stream wrapper, to be used with the pseudo-protocol "test" :

// Register the wrapper
stream_wrapper_register("test", "MyStream")
    or die("Failed to register protocol");


And, now, we do our curl request, like we would do when writting to a "real" file, like other answers suggested :

// Open the "file"
$fp = fopen("test://MyTestVariableInMemory", "r+");

// Configuration of curl
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://www.rue89.com/");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_BUFFERSIZE, 256);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FILE, $fp);    // Data will be sent to our stream ;-)

curl_exec($ch);

curl_close($ch);

// Don't forget to close the "file" / stream
fclose($fp);

Gerçek bir dosya ile çalışmak, ama bizim sözde protokolü ile gerekmez.


This way, each time a chunk of data arrives, MyStream::stream_write method will get called, and will be able to work on a small amount of data (when I tested, I always got 8192 bytes, whatever value I used for CURLOPT_BUFFERSIZE)


A few notes :

  • Ben mi daha açıkçası, bu daha fazla test etmek gerekir
  • çizgiler 8192 bytetan ise benim stream_write uygulanması muhtemelen çalışmaz; kadar bunu yama için ;-)
  • Sadece birkaç işaretçiler, ve bir tam çalışma çözüm olarak anlatıyor: (tekrar) test ve muhtemelen biraz daha fazla kod var!

Still, I hope this helps ;-)
Have fun !

Bir dosyaya biriktirmek. Kerede bellekte tüm bu verileri tutmaya çalışmayın.

NB:

"Basically, if you open a file with fopen, fclose it and then unlink it, it works fine. But if between fopen and fclose, you give the file handle to cURL to do some writing into the file, then the unlink fails. Why this is happening is beyond me. I think it may be related to Bug #48676"

http://bugs.php.net/bug.php?id=49517

PHP'nin eski bir sürümü üzerinde iseniz bu yüzden dikkatli olun. Çift yakın dosya kaynak için bu sayfada basit bir düzeltme var:

fclose($fp);
if (is_resource($fp))
    fclose($fp);