Acceder à une ligne dans fichier txt volumineux 2GB python

Poufpaf -  
mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention   -

Bonjour, 

J'ai un fichier texte d'environ 2GB, et j'aimerais pouvoir accéder à une ligne specifique en utilisant un index. Le probleme est que la méthode readlines() n'est pas utilisable car elle mobilise beaucoup trop de memoire, et mon programme crash. 

J'ai pensé à utiliser l'objet fichier en tant qu'itérateur, mais le probleme est qu'avec une boucle for python qui parcourt tout le fichier, cela mettrait trop de temps.

Je cherche une méthode très efficace, avec un module par exemple, qui affiche juste le contenu de la n-ième ligne, sans charger tout le fichier dans une variable python.

Merci pour vos futures réponses

A voir également:

4 réponses

yg_be Messages postés 23541 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 584
 

bonjour, n'hésite pas à montrer ton programme, il ne fait peut-être pas ce que tu imagines.

tu veux accéder à une seule ligne dans le fichier?

Décris la séquence des opérations que tu veux effectuer.

Toutes tes lignes ont la même longueur?

Tu peux faire une boucle de readline(), cela évitera le problème mémoire.

0
Poufpaf
 

Au fait mon but est de faire une dichotomique de hash. Pour un projet ou j'utilise une variante personnalisée des puzzles de merkle, j ai besoin de me rendre compte de la vitesse à laquelle il est possible de casse sha256.

J ai donc imaginé faire un mini programme qui casserai sha256 avec 5 caractères. À la place de faire une bruteforce classique en essayant toutes les combinaison à chaque fois, j'ai créé une liste de 1,66GB contenant tous les hash à partir de mdp à 5 caractères dans cette ordre là:

sha(aaaaa)

sha(aaaab)

sha(aaaac)

...

sha(99999)

En ayant le fichier dejà créé, il est désormais plus rapide de rechercher un hash(X), et d'en déduire X en fonction de l'index du hash dans le fichier (index de la ligne)

Mais pour augmenter encore la vitesse de calcul, j ai pensé faire une recherche dichotomique. J ai créé un fonction qui fonctionne déjà sur des listes python :

def speedfind(liste, el):
    lst = [0, len(liste) - 1]
    while lst[1]-lst[0] > 0:
        interval1 = [lst[0], (lst[1]-lst[0])//2+lst[0]]
        interval2 = [((lst[1]-lst[0])//2)+lst[0]+1, lst[1]]
        if liste[interval1[1]] >= int(el):
            lst = interval1[:]
        elif liste[interval1[1]] < int(el):
            lst = interval2[:]
    if liste[lst[0]] == int(el):
        return lst[0]
    else:
        return None

J ai donc maintenant besoin de l'adapter aux fichiers. Le probleme est justement que j'aurais bespin d'acceder rapidement au contenu d'une ligne du fichier de 1,66GB en utilisant l'index de la ligne. Il fait une methode qui soit relativement rapide afin que la recherche dichotomique apporte quelque chose de plus par rapport à une simple recherche de cas par cas.

(Pour vous répondre, oui, toutes les lignes on la même taille, vu que tous les hash en hexadecimal ont 64 caractères)

Merci d'avoir pris connaissance de ma question, j'espère qu'on m'aidera à trouver une solution.

????

0
Poufpaf
 

Il FAUT* une methode relativement rapide...

0
yg_be Messages postés 23541 Date d'inscription   Statut Contributeur Dernière intervention   1 584
 

Le fichier ne devrait-il pas être trié par hash, et pas par mot de passe, pour qu'une recherche dichotomique ait un sens?

As-tu envisagé d'utiliser la base de données SQLite, qui indexera les donnés, et fera la recherche dichotomique automatiquement?

Si tu veux vraiment utiliser un fichier: comme les lignes ont toutes la même longueur, tu peux facilement déterminer l'adresse de la donnée qui t'intéresse, et lire le contenu du fichier à partir de cette adresse.

0
Poufpaf > yg_be Messages postés 23541 Date d'inscription   Statut Contributeur Dernière intervention  
 

Oui effectivement, les données devraient être triées par hash, j ai réctifié ce que j'ai dit, majs mon message a été posté apres votre réponse, merci.

Dans le cas ou la liste est triés par hash(x), comment puis-je retrouver x sans associer à chaque hash(x) la valeur de x. Je pourais bien sûr asscier x pour chaque hash(x), mais imaginons que j'écrive dans chaque ligne: hash(x) et x  Est ce que le fait que x soit ecrit sir la meme ligne à la suite du hash(x) ne risque pas d'impacter in algorithme de tri ? Il me semble que normallement cela devrait fonctionner mais j'ai néanmoins un doute. De plus ça aura pour conséquence d'ajouter encore ~500MB au fichier, surtout que les hahs en hexadecimal "gaspillent un peut de la place sous forme de caractères, car ça va jusqu'à 16. On pourrait donc exprimer 3 caractères d'un hash avec seulement 2 caractères allant de 0 à 64 par exemple plur economiser de la mémoire.

Sinon à propos des bases SQL, je ne sais pas comment les utiliser. Est ce que c'est simple. Si oui je serais curieux de savoir comment les utiliser de manière à ce que ça fasse une recherche automatiquement.

Aussi, est ce que les donnés sont réèlement traitées plus rapidement avec une base SQL qu'avec un simple fichier texte(Je ne sais pas, c est pour ça que je demande).

Merci

0
yg_be Messages postés 23541 Date d'inscription   Statut Contributeur Dernière intervention   1 584 > Poufpaf
 

Une base de donnée comme SQLite est la solution la plus simple et la plus rapide pour ce genre d'activité.

0
Poufpaf
 

Ah oui, vous vous demandez sûrement comment la recherche dichotomique peut fonctionner alors que les hashs eux mêmes ne sont pas triés.

Je pensais effectivement utiliser 2 listes, 1 avec les hash(X) triés selon l'ordre des X, et une deuxième liste triée selon les hash(X) eux mêmes.

Je n'ai pas encore réfléchi à comment faire correspondre les 2 listes, si vous avez des idées n'hésitez pas à me les donner pour ça aussi.

En tout cas pour l'instant je cherche déjà une solution pour acceder rapidement à une ligne avec son index

0
yg_be Messages postés 23541 Date d'inscription   Statut Contributeur Dernière intervention   1 584
 

Un exemple:

def readl(fl,lg,linx):
    fl.seek(linx*lg)
    return fl.readline()
f=open("big.txt","r")
l=7
data=True
p=0
while data :
    data=readl(f,l,p)
    print(data)
    p += 1

La fonction readl() lit une ligne d'un fichier, lg étant la longueur d'une ligne, et linx l'index de la ligne.

0
Poufpaf > yg_be Messages postés 23541 Date d'inscription   Statut Contributeur Dernière intervention  
 

Merci beaucoup, si j'ai bien compris le but est de deplacer le curseur dans la liste à l'endoit que l'on souhaite, puis en retouener la ligne concernée.

Je n'ai pas encore essayé majs celà semble convenir. Par contre est ce que .seek compte le \n comme 1 caractère, 2 caractères, ou est ce qu'il est ignoré ?

0
yg_be Messages postés 23541 Date d'inscription   Statut Contributeur Dernière intervention   1 584 > Poufpaf
 

Tous les caractères comptent.  Un passage à la ligne est encodé comme un ou deux caractères, cela dépend du contenu du fichier.

0
mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention   7 878
 

Bonjour

À mon avis poser des questions pour mettre en place un code servant à casser un mot de passe est borderline par rapport à la charte du forum. Mais bon, comme un mot de passe fait généralement bien plus que 5 caractères et que l'ensemble des caractères n'est généralement pas limités aux chiffres et aux lettres, on peut se consoler en se disant que l'intérêt pratique est limité.

Ensuite la question est un peu étrange.

  • Quel est l'intérêt de stocker dans un fichier texte les résultats précalculés (ou même dans une base) ? Les accès disque, c'est lent et ici on parle de (26+10)^5 ~ 60,5 millions de paires (sha1, mot de passe) autant dire quelque chose qui tient largement en mémoire. Si c'est juste éviter de les recalculer, ne serait-il pas plus simple de directement sauver le dictionnaire qui associe les clés et les valeurs dans un dictionnaire par exemple avec pickle. En mémoire, la recherche sera bien plus rapide.
  • Rien ne garantit que sha1 soit injective, ce qui signifie qu'en général elle n'est pas inversible (dans ton cas, plusieurs mots de passe peuvent mener au même hash). Qu'est sensé faire le programme dans ce cas ? Il doit juste retourner un mot de passe arbitraire ?

Bref. En admettant qu'on veuille un mot de passe arbitraire et qu'avec un petit alphabet et de petits mots de passe et qu'on se sente l'âme de Trinity, voici à quoi ça peut ressembler :

import pickle
from hashlib import sha1
from itertools import product
from pprint import pprint

ALPHABET = "abcd"      # Ensemble des caractères impliqués dans le mot de passe
N = 3                  # Longueur maximum du le mot de passe
FILENAME = "dump.pkl"  # Fichier de backup

def str_to_sha1(s):
    return sha1(s.encode()).digest()

def passwords(alphabet, n):
    yield from (
        "".join(chars)
        for i in range(n + 1)
        for chars in product(*([alphabet] * i))
    )

def compute_map_sha1_password(alphabet, n):
    return { 
        str_to_sha1(password) : password
        for password in passwords(alphabet, n)
    }

try:
    with open(FILENAME, "rb") as f:
       print(f"loading {FILENAME}")
       map_sha1_password = pickle.load(f)
except:
    map_sha1_password = compute_map_sha1_password(ALPHABET, N)
    with open(FILENAME, "wb") as f:
        print(f"saving {FILENAME}")
        pickle.dump(map_sha1_password, f)

pprint(map_sha1_password)

Mais bon. On va pas casser la matrice avec ça.

Bonne chance

0
Poufpaf
 

Bonjour, merci pour votre réponse.

Sinon j'utilise sha256, et à propos des collisions, si aucune n'a encore été trouvée, je ne pense pas que ce sera parmis 36^5 mots de passes qu'il y en aura une.

0
mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention   7 878 > Poufpaf
 

Je ne pense pas que sha256 change grand chose à mon message à vrai dire (je pense que tu devrais arriver à remplacer sha1 par sha256 dans le code).

Concernant pour les collisions, heureusement qu'il n'y en a pas dans seulement 36^5 éléments, sinon le nom de fonction de hachage serait un peu galvaudé...

Est-ce que du coup avec le bout de code que je t'ai partagé, ton problème est résolu ?

0