Manipuler une chaîne de caractères qui est de 30 millions de caractères
Je suis le téléchargement d'un fichier CSV à partir d'un autre serveur comme un flux de données à partir d'un fournisseur.
J'utilise curl pour obtenir le contenu du fichier et de l'enregistrer dans une variable appelée $contents
.
Je peux accéder à la partie, mais j'ai essayé d'exploser par \r
et \n
pour obtenir un tableau de lignes, mais il échoue avec un "out of memory' erreur.
Je echo strlen($contents)
et c'est environ 30,5 millions de caractères.
J'ai besoin de manipuler les valeurs et les insérer dans une base de données. Que dois-je faire pour éviter l'allocation de la mémoire des erreurs?
Vous devez vous connecter pour publier un commentaire.
PHP est l'étouffement parce qu'il est à cours de mémoire. Au lieu d'avoir curl remplir une variable PHP avec le contenu du fichier, utilisez la
option pour enregistrer le fichier sur le disque au lieu.
Puis, une fois le fichier enregistré, au lieu d'utiliser le
file
oufile_get_contents
fonctions (qui serait de charger tout le fichier en mémoire, tuant PHP de nouveau), l'utilisationfopen
et fgets pour lire le fichier ligne par ligne.Comme d'autres réponses a dit :
CURLOPT_FILE
Mais, vous ne pouvez vraiment créer un fichier que vous voulez travailler avec les données dans la mémoire... l'Utiliser dès qu'il "arrive".
Une solution possible pourrait être de définir votre propre gestionnaire de flux, et d'utiliser celui-ci, au lieu d'un fichier réel, avec
CURLOPT_FILE
Tout d'abord, voir :
stream_wrapper_register
Les streamWrapper classe
Et maintenant, passons à un exemple.
Tout d'abord, nous allons créer notre gestionnaire de flux de classe :
Ce que je fais, c'est :
stream_write
Ensuite, nous avons enregistrer ce gestionnaire de flux, pour être utilisé avec le pseudo-protocole "test" :
Et, maintenant, nous faisons de notre demande curl, comme nous le feriez lors de la rédaction d'un "vrai" fichier, comme d'autres réponses proposées :
Remarque nous ne travaillons pas avec un vrai fichier, mais avec notre pseudo-protocole.
De cette façon, chaque fois qu'un bloc de données arrive,
MyStream::stream_write
méthode sera appelée, et sera en mesure de travailler sur une petite quantité de données (quand je l'ai testé, j'ai toujours eu de 8192 octets, quelle que soit la valeur que j'ai utilisé pourCURLOPT_BUFFERSIZE
)Quelques remarques :
Encore, j'espère que cette aide 😉
Amusez-vous !
CURLOPT_FILE
a été renomméCURLOPT_WRITEDATA
, et je pense que vous pouvez maintenant faire quelque chose de similaire à l'aide deCURLOPT_WRITEFUNCTION
, qui est un rappel comme votrestream_write($data)
, et enregistre la nécessité pour le gestionnaire de flux. Voir curl.haxx.se/libcurl/c/curl_easy_setopt.htmlDarren Cuire commentaire de Pascal MARTIN réponse est vraiment intéressant. Moderne, PHP+Curl versions, la
CURLOPT_WRITEFUNCTION
option peut être définie de sorte CURL appelle une fonction de rappel pour chaque reçu "morceau" de données. Plus précisément, le "callable" sera reçu à deux paramètres, le premier avec l'invocation curl objet, et la seconde avec le segment de données. La fonction doit retournerstrlen($data)
pour curl continuer à envoyer plus de données.Callables peut être des méthodes en PHP. À l'aide de tout cela, j'ai développé une solution que je trouve plus lisible que la précédente (bien que Pascal Martin réponse est vraiment super, les choses ont changé depuis). J'ai utilisé les attributs de la simplicité, mais je suis sûr que les lecteurs pourraient s'adapter et d'améliorer le code. Vous pouvez même annuler la demande CURL, quand un certain nombre de lignes (ou d'octets) ont été atteints. J'espère que cela serait utile pour les autres.
Vous pourriez envisager de les sauver dans un fichier temporaire, puis le lire ligne par ligne à l'aide de
fgets
oufgetcsv
.De cette façon, vous évitez les initiales grand tableau que vous obtenez à partir d'exploser telle une chaîne de grande taille.
memory_limit
dansphp.ini
.fopen()
etfgets()
.Bobine dans un fichier. N'essaie pas de retenir toutes les données en mémoire à la fois.
NB:
"Essentiellement, si vous ouvrez un fichier avec fopen, fclose, et puis de le dissocier d'elle,
il fonctionne très bien. Mais si entre fopen et fclose, vous donner le descripteur de fichier
pour cURL pour faire un peu de l'écriture dans le fichier, puis de le dissocier échoue. Pourquoi
ce qui se passe au-delà de moi. Je pense que ça peut être lié au Bug #48676"
http://bugs.php.net/bug.php?id=49517
Donc attention si vous êtes sur une ancienne version de PHP. Il y a une solution simple sur cette page à double-fermer le fichier de ressources: