web-dev-qa-db-fra.com

Fractionnement d'un fichier texte volumineux en fichiers texte plus petits par numéros de ligne à l'aide de Python

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

15
walterfaye
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()
22
Matt Anderson

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. 

20
jamylak
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()
3
Varun

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)
2
Ryan Saxe
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)
0
juliomalegria

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')
0
knowingpark