J'ai un fichier numbers.txt
qui se compose de plusieurs lignes de 100K, chacune composée de deux chiffres uniques séparés par :
signe:
number = 407597693 with open("numbers.txt", "r+") as library: file = library.read() if (str(number) + ":") in file: lines = file.splitlines() with open("numbers_temp.txt", "a+") as library_temp: for line in lines: if (str(number) + ":") in line: library_temp.write( "\n" + str(number) + ":" + str(time.time() + 3600) ) else: library_temp.write("\n" + line) library_temp.seek(0) new_file = library_temp.read() with open("numbers.txt", "w+") as library_2: library_2.write(new_file) os.remove("numbers_temp.txt")
Mon objectif est de localiser une ligne avec le numéro 407597693
sur le côté gauche, puis de changer le numéro sur le côté droit en y ajoutant 3600
. Après cela, je dois réécrire le fichier numbers.txt
avec toutes les modifications. Je dois effectuer la même opération (juste un nombre différent) sur le même fichier txt aussi vite que possible.
J'ai réussi à le faire fonctionner with open:
opérations with open:
file et for
pour chaque ligne, en recherchant le nombre nécessaire, en modifiant la ligne, puis en réécrivant le fichier entier. Cependant, j'ai remarqué que l'exécution constante d'une telle opération prend du temps pour mon programme, environ 0,2-0,5 s, ce qui s'additionne avec le temps et ralentit considérablement tout.
Voici le code que j'utilise:
407597693:1604722326.2426915 510905857:1604722326.2696202 76792361:1604722331.120079 112854912:1604722333.4496727 470822611:1604722335.283259
J'apprécierais vraiment toute contribution sur la façon d'accélérer ce processus, merci d'avance!
3 Réponses :
Je suppose que votre mémoire peut stocker tout le fichier. Cela devrait être plus rapide en utilisant regex:
import re number = 407597693 with open("numbers.txt", "r") as f: data = f.read() # data = re.sub(f'({number}):(.*)', lambda x:f"{x.group(1)}:{float(x.group(2))+3600}", data) data = re.sub("^" + str(number) + ".*\n", str(number) + ":" + str(int(time.time()) + 3600) + "\n", data, flags=re.MULTILINE) with open("numbers.txt", "w") as f: f.write(data)
Votre réponse était partiellement correcte. Bien que l'utilisation de regex ait réduit le temps nécessaire pour terminer la tâche de 4 fois, votre code spécifique ne fonctionnait pas tout à fait pour moi. La partie que j'ai dû changer est: data = re.sub("^" + str(number) + ".*\n", str(number) + ":" + str(int(time.time()) + 3600) + "\n", data, flags=re.MULTILINE)
. Je marquerai votre réponse comme une solution, mais peut-être voudrez-vous changer votre ligne de code pour la mienne puisque je l'ai testée, et cela semble beaucoup plus simple.
Oh ouais j'ai mis le f
dans la corde, ça devrait être au début.
Plutôt que de devoir exécuter plusieurs boucles, nous pouvons le faire en une seule boucle comme ci-dessous:
number = 407597693 numbers = '' with open('numbers.txt', "r+") as inputfile: file = inputfile.read() if(file.find(str(number))) != -1 : for line in file.splitlines(): if (line.find(str(number))) == 0: numbers += line.split(':')[0] + ':' + str(float(line.split(':')[1]) + float(3600)) + '\n' else: numbers += line + '\n' with open('numbers.txt', 'w') as updatedFile: updatedFile.writelines(numbers)
J'espère que cela aidera.
Vous pouvez ouvrir un fichier mappé en mémoire, utiliser une expression régulière pour trouver la ligne souhaitée et, avec un peu de chance, vous n'aurez à modifier qu'une seule page du fichier. J'utilise le module décimal afin que vous n'ayez pas de problèmes de conversion décimal en flottant binaire. Habituellement, le nouveau numéro et l'ancien numéro auront la même largeur et le contenu du fichier n'aura pas besoin d'être déplacé. Je montre un exemple Linux. Windows mmap.map
est un peu différent mais devrait être facile à utiliser.
import mmap import re from decimal import Decimal def increment_record(filename, findval, increment): with open(filename, "rb+") as fp: with mmap.mmap(fp.fileno(), 0) as fmap: search = re.search(rf"{findme}:([\d\.]+)".encode("ascii"), fmap, re.MULTILINE) if search: # found float to change. Use Decimal for base 10 precision newval = Decimal(search.group(1).decode("ascii")) + increment newval = f"{newval}".encode("ascii") delta = len(newval) - len(search.group(1)) if delta: # need to expand file and copy fsize = fmap.size() fmap.resize(fsize + delta) fmap.move(search.end(1) + delta, search.end(1), fsize - search.end(1)) # change just the number fmap[search.start(1):search.start(1) + len(newval)] = newval # test parameters filename = "test.txt" findme = "76792361" increment = 3600 testdata = """407597693:1604722326.2426915 510905857:1604722326.2696202 76792361:1604722331.120079 112854912:1604722333.4496727 470822611:1604722335.283259""" open(filename, "w").write(testdata) increment_record(filename, findme, increment) print("changes:") for old,new in zip(testdata.split("\n"), open(filename)): new = new.strip() if old != new: print((old,new)) print("done")
Je soupçonne que c'est un problème XY . Veuillez sauvegarder et décrire le cas d'utilisation réel. Je trouve intenable que l'objet soit de maintenir un fichier texte, faisant des mises à jour sur une seule ligne à la demande, dépendant de Python. Pouvez-vous apporter les modifications dans un lot? Devez-vous utiliser Python? Pourquoi un fichier texte est-il nécessaire? Cela semble bien mieux adapté à une application de base de données.
@Prune "Pouvez-vous apporter les modifications dans un lot?" - Veuillez clarifier cette question. "Devez-vous utiliser Python?" - Oui, puisque c'est la langue dans laquelle mon programme complexe est intégré. "Pourquoi un fichier texte est-il nécessaire?" - Étant donné que les utilisateurs doivent avoir un accès direct et facile aux informations + peuvent être consultées hors ligne + ne devraient pas nécessiter de logiciel supplémentaire pour lire et fonctionner.
Ce n'est pas censé être une réponse complète, mais j'ai eu un problème dans lequel j'ai dû extraire des opérations spécifiques dans des fichiers texte de 250 Mo avec des points limites particuliers indiquant une nouvelle structure d'enregistrement. J'ai obtenu une réponse beaucoup plus rapide en appelant d'abord le sous-processus
grep
pour localiser les lignes à l'avance, puis en utilisant Python pour ouvrir etseek
ces lignes, plutôt que de scanner l'ensemble du fichier en Python pour ces points de limite. Une grande partie du contenu du fichier n'avait aucun intérêt entre les frontières.