J'ai un fichier texte dit vraiment_big_file.txt qui contient:
line 1
line 2
line 3
line 4
...
line 99999
line 100000
J'aimerais écrire un script Python qui divise really_big_file.txt en fichiers plus petits de 300 lignes chacun. Par exemple, small_file_300.txt doit comporter les lignes 1 à 300, small_file_600, les lignes 301 à 600, etc. jusqu'à ce qu'il y ait suffisamment de petits fichiers conçus pour contenir toutes les lignes du gros fichier.
J'apprécierais toutes les suggestions sur la meilleure façon d'accomplir cela en utilisant Python
lines_per_file = 300
smallfile = None
with open('really_big_file.txt') as bigfile:
for lineno, line in enumerate(bigfile):
if lineno % lines_per_file == 0:
if smallfile:
smallfile.close()
small_filename = 'small_file_{}.txt'.format(lineno + lines_per_file)
smallfile = open(small_filename, "w")
smallfile.write(line)
if smallfile:
smallfile.close()
Utilisation de itertools
grouper recipe:
from itertools import izip_longest
def grouper(n, iterable, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
n = 300
with open('really_big_file.txt') as f:
for i, g in enumerate(grouper(n, f, fillvalue=''), 1):
with open('small_file_{0}'.format(i * n), 'w') as fout:
fout.writelines(g)
L'avantage de cette méthode par rapport au stockage de chaque ligne dans une liste est qu'elle fonctionne avec des itérables, ligne par ligne, de sorte qu'il n'est pas nécessaire de stocker chaque small_file
en mémoire à la fois.
Notez que le dernier fichier dans ce cas sera small_file_100200
mais ira seulement jusqu'au line 100000
. Cela se produit parce que fillvalue=''
, ce qui signifie que j’écrive rien dans le fichier lorsque je n’ai plus de lignes à écrire, car la taille du groupe ne se divise pas également. Vous pouvez résoudre ce problème en écrivant dans un fichier temporaire, puis en le renommant après au lieu de le nommer comme auparavant. Voici comment cela peut être fait.
import os, tempfile
with open('really_big_file.txt') as f:
for i, g in enumerate(grouper(n, f, fillvalue=None)):
with tempfile.NamedTemporaryFile('w', delete=False) as fout:
for j, line in enumerate(g, 1): # count number of lines in group
if line is None:
j -= 1 # don't count this line
break
fout.write(line)
os.rename(fout.name, 'small_file_{0}.txt'.format(i * n + j))
Cette fois, le fillvalue=None
et chaque ligne vérifiant None
, quand cela se produit, je sais que le processus est terminé et je soustrais donc 1
de j
pour ne pas compter le remplisseur, puis écrire le fichier.
import csv
import os
import re
MAX_CHUNKS = 300
def writeRow(idr, row):
with open("file_%d.csv" % idr, 'ab') as file:
writer = csv.writer(file, delimiter=',', quotechar='\"', quoting=csv.QUOTE_ALL)
writer.writerow(row)
def cleanup():
for f in os.listdir("."):
if re.search("file_.*", f):
os.remove(os.path.join(".", f))
def main():
cleanup()
with open("large_file.csv", 'rb') as results:
r = csv.reader(results, delimiter=',', quotechar='\"')
idr = 1
for i, x in enumerate(r):
temp = i + 1
if not (temp % (MAX_CHUNKS + 1)):
idr += 1
writeRow(idr, x)
if __== "__main__": main()
Je le fais de manière plus compréhensible en utilisant moins de raccourcis afin de vous permettre de mieux comprendre comment et pourquoi cela fonctionne. Les réponses précédentes fonctionnent, mais si vous n'êtes pas familiarisé avec certaines fonctions intégrées, vous ne comprendrez pas ce que fait la fonction.
Parce que vous n’avez posté aucun code, j’ai décidé de le faire de cette façon car vous pourriez ne pas être familier avec des éléments autres que la syntaxe de base de python, étant donné que vous avez formulé la question de manière à donner l’impression que vous n’avez pas essayé ni n’aviez la moindre idée de la question
Voici les étapes à suivre pour faire cela en python de base:
Commencez par lire votre fichier dans une liste pour la sauvegarde:
my_file = 'really_big_file.txt'
hold_lines = []
with open(my_file,'r') as text_file:
for row in text_file:
hold_lines.append(row)
Deuxièmement, vous devez configurer un moyen de créer les nouveaux fichiers par nom! Je suggérerais une boucle avec quelques compteurs:
outer_count = 1
line_count = 0
sorting = True
while sorting:
count = 0
increment = (outer_count-1) * 300
left = len(hold_lines) - increment
file_name = "small_file_" + str(outer_count * 300) + ".txt"
Troisièmement, à l'intérieur de cette boucle, vous avez besoin de boucles imbriquées qui sauvegarderont les lignes correctes dans un tableau:
hold_new_lines = []
if left < 300:
while count < left:
hold_new_lines.append(hold_lines[line_count])
count += 1
line_count += 1
sorting = False
else:
while count < 300:
hold_new_lines.append(hold_lines[line_count])
count += 1
line_count += 1
Dernière chose, encore une fois dans votre première boucle, vous devez écrire le nouveau fichier et ajouter votre dernier incrément de compteur pour que votre boucle répète et écrit un nouveau fichier.
outer_count += 1
with open(file_name,'w') as next_file:
for row in hold_new_lines:
next_file.write(row)
remarque: si le nombre de lignes n'est pas divisible par 300, le dernier fichier aura un nom qui ne correspond pas à la dernière ligne du fichier.
Il est important de comprendre pourquoi ces boucles fonctionnent. Vous l'avez paramétré pour que le nom du fichier que vous écrivez soit modifié dans la boucle suivante car son nom dépend d'une variable en cours de modification. C'est un outil de script très utile pour accéder aux fichiers, les ouvrir, les écrire, les organiser, etc.
Au cas où vous ne pourriez pas suivre ce qui était dans quelle boucle, voici l'intégralité de la fonction:
my_file = 'really_big_file.txt'
sorting = True
hold_lines = []
with open(my_file,'r') as text_file:
for row in text_file:
hold_lines.append(row)
outer_count = 1
line_count = 0
while sorting:
count = 0
increment = (outer_count-1) * 300
left = len(hold_lines) - increment
file_name = "small_file_" + str(outer_count * 300) + ".txt"
hold_new_lines = []
if left < 300:
while count < left:
hold_new_lines.append(hold_lines[line_count])
count += 1
line_count += 1
sorting = False
else:
while count < 300:
hold_new_lines.append(hold_lines[line_count])
count += 1
line_count += 1
outer_count += 1
with open(file_name,'w') as next_file:
for row in hold_new_lines:
next_file.write(row)
lines_per_file = 300 # Lines on each small file
lines = [] # Stores lines not yet written on a small file
lines_counter = 0 # Same as len(lines)
created_files = 0 # Counting how many small files have been created
with open('really_big_file.txt') as big_file:
for line in big_file: # Go throught the whole big file
lines.append(line)
lines_counter += 1
if lines_counter == lines_per_file:
idx = lines_per_file * (created_files + 1)
with open('small_file_%s.txt' % idx, 'w') as small_file:
# Write all lines on small file
small_file.write('\n'.join(stored_lines))
lines = [] # Reset variables
lines_counter = 0
created_files += 1 # One more small file has been created
# After for-loop has finished
if lines_counter: # There are still some lines not written on a file?
idx = lines_per_file * (created_files + 1)
with open('small_file_%s.txt' % idx, 'w') as small_file:
# Write them on a last small file
small_file.write('n'.join(stored_lines))
created_files += 1
print '%s small files (with %s lines each) were created.' % (created_files,
lines_per_file)
Je devais faire la même chose avec 650000 fichiers en ligne.
Utilisez l'index d'énumération et l'entier div it (//) avec la taille du bloc
Lorsque ce nombre change, fermez le fichier actuel et ouvrez-en un nouveau.
Ceci est une solution python3 utilisant des chaînes de format.
chunk = 50000 # number of lines from the big file to put in small file
this_small_file = open('./a_folder/0', 'a')
with open('massive_web_log_file') as file_to_read:
for i, line in enumerate(file_to_read.readlines()):
file_name = f'./a_folder/{i // chunk}'
print(i, file_name) # a bit of feedback that slows the process down a
if file_name == this_small_file.name:
this_small_file.write(line)
else:
this_small_file.write(line)
this_small_file.close()
this_small_file = open(f'{file_name}', 'a')