Je traite un très gros fichier contenant du texte brut en python. Le contenu du fichier a le format suivant:
point_separator field_separator . with open(file_name, mode='rt', encoding='utf-8') as reader:
text = reader.read()
objects = text.strip().split('point_separator')
......
Auparavant, je lisais le fichier complet en utilisant l'appel d'API d'ouverture de fichier python:
new record field_separator another field field_separator another field field_separator another field field_separator another field field_separator another field field_separator another field field_separator call point_separator new record field_separator another field field_separator another field field_separator another field field_separator another field field_separator another field field_separator another field field_separator call point_separator new record field_separator another field field_separator another field field_separator another field field_separator another field field_separator another field field_separator another field field_separator call point_separator
Cependant, cela échoue avec MemoryError lorsque le fichier est très volumineux, c'est-à-dire 20 Go et que je traite dans une machine contenant 16 Go RAM.
Le problème est que je ne peux pas lire cette ligne de fichier par ligne car je dois collecter tous les champs basés sur field_separator jusqu'à ce que je vois un point_separator .
Y a-t-il un moyen pour que le système d'exploitation utilise la pagination et il serait traité de manière transparente?
3 Réponses :
traiter ligne par ligne
objects = []
fields = []
field = ''
with open(file_name, mode='rt', encoding='utf-8') as reader:
for line in reader:
line = line.strip()
if 'point_separator' == line:
objects.append(fields)
fields = []
elif 'field_separator' == line:
fields.append(field)
field = ''
else:
field += line + '\n'
Vous pouvez écrire votre propre fonction de générateur qui vous permet d'itérer sur le classer un enregistrement à la fois, sans jamais lire le fichier entier en mémoire simultanément. Par exemple:
['new record\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'call\n'] ['new record\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'call\n'] ['new record\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'another field\n', 'call\n']
Avec l'exemple de version dans la question, cela donne:
def myiter(filename, point_separator):
with open(filename, mode='rt', encoding='utf-8') as reader:
text = ''
while True:
line = reader.readline()
if not line:
break
if line.strip() == point_separator:
yield text
text = ''
else:
text += line
if text:
yield text
# put in the actual separator values here - tested the version shown
# in the question using literal "point_separator" and "field_separator"
point_separator = 'point_separator'
field_separator = 'field_separator'
filename = 'test.txt'
for record in myiter(filename, point_separator):
fields = record.split(field_separator + '\n')
print(fields)
Vous pouvez ensuite supprimer les nouvelles lignes selon vos besoins ( Je ne l'ai pas déjà fait pour vous, car je ne sais pas si les champs peuvent être multilignes.)
De plus, je n'ai rien fait de spécial avec le "nouvel enregistrement" et " appeler". Vous pouvez faire print (fields [1: -1]) pour les exclure.
Vous pouvez utiliser itertools.groupby pour itérer d'abord par tout ce qui se trouve entre les lignes "nouvel enregistrement", puis en interne par tout ce qui se trouve entre les lignes "field_separator". Dans le groupby externe, new_record sera vrai pour toutes les lignes contenant le texte "nouvel enregistrement" et new_record devient d'abord faux, vous savez que vous êtes dans l'enregistrement et faites l'intérieur groupby.
import itertools
def record_sep(line):
return line.strip() == "new record"
def field_sep(line):
return line.strip() == "field_separator"
records = []
with open('thefile') as fileobj:
for new_record, record_iter in itertools.groupby(fileobj, record_sep):
# skip new record group and proceed to field input
if not new_record:
record = []
for new_field, field_iter in itertools.groupby(record_iter, field_sep):
# skip field separator group and proceed to value group
if not new_field:
record.append(list(field_iter)) # assuming multiple values in field
records.append(record)
for record in records:
print(record)
Est-il possible d'analyser le fichier ligne par ligne (par exemple cmdlinetips.com/2011/08/... )? Dans quelle mesure est-il important de lire plus d'un seul morceau du fichier à la fois?
Le problème est que je ne peux pas lire ce fichier ligne par ligne car je dois collecter tous les champs basés sur
field_separatorjusqu'à ce que je vois unpoint_separator. Et certains champs peuvent avoir plusieurs lignes.Il peut toujours être possible de le gérer ligne par ligne, en gardant manuellement une trace de la dernière fois que vous avez vu un
point_separatorou unfield_separatorpour déterminer comment gérer la ligne actuelle que vous sur, mais je vois le problème.Lisez jusqu'au
point_separator, en collectant les champs jusqu'à ce que vous y arriviez. Traitez ensuite l'enregistrement. Répéter. N'essayez pas de rassembler tous les enregistrements en mémoire à la fois.