Accès et recherche dans un gros fichier texte (10 000 lignes)

Partagez vos scripts, et vos applications AutoIt.
Règles du forum
.
marcgforce
Niveau 3
Niveau 3
Messages : 47
Enregistré le : lun. 07 mars 2016 07:20
Status : Hors ligne

Accès et recherche dans un gros fichier texte (10 000 lignes)

#1

Message par marcgforce »

Bonjour a tous

je me suis penché sur la façon d’accéder et de rechercher une donnée contenue dans un fichier texte de plusieurs milliers de lignes, sans que ces manipulations ne prennent trop de temps.

le fichier que j'ai choisi pour ma recherche est le fichier regroupant l'ensemble des codes IATA (aéroportuaires) : ce petit code de trois lettre que l'on trouve sur son bagage à la sortie de l'avion.

La première idée (qui n'est pas mauvaise) consistait a mettre ce fichier dans un tableau en mémoire, de façon à ce que la recherche soit instantanée.
#include <array.au3>
#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <EditConstants.au3>
global $gui,$Input,$Label_aeroport,$arr, $file=@ScriptDir & "\codesnew.csv"
$gui = GUICreate("Codes Aéroports V 1.1", 730, 128, 232, 194)
$Input = GUICtrlCreateInput("", 16, 24, 30, 21)
GUICtrlCreateLabel("<--- Code Aéroportuaire : Ex: STN", 60, 24, 200, 21)
GUICtrlCreateLabel("Correspond à :", 16, 64, 76, 17)
$Label_aeroport = GUICtrlCreateLabel("", 104, 64, 608, 25)
$Group1 = GUICtrlCreateGroup("", 8, 8, 713, 105)
GUICtrlCreateLabel("Concept && dévellopement : Marc GRAZIANI", 500, 96, 220, 17)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Button_ok = GUICtrlCreateButton("OK", 648, 16, 65, 25)
$button_array = GUICtrlCreateButton("Liste",648,46,65,25)
$accEnter = GUICtrlCreateDummy()

Global $a_AccelKeys[1][2] = [["{ENTER}", $accEnter]]
GUISetAccelerators($a_AccelKeys)



If not FileExists($file) then
   msgbox(64,"Erreur", "Fichier Codes.csv Manquant" & @CRLF & "Je quitte")
   Exit
EndIf

Dim $sCSV = FileRead($file)
ProgressOn("Préparation","Chargement, veuillez patienter")
Local $arr = toArrayCsv($sCSV)
GUISetState(@SW_SHOW)

While 1
   $nMsg = GUIGetMsg()
   Switch $nMsg
      Case $GUI_EVENT_CLOSE
         Exit
      Case $Button_ok
         _trouve_code()
      case $button_array
         _ArrayDisplay($arr,"Codes Aéroports","|3",16)
      Case $accEnter
            If _GuiCtrlGetFocus($gui) = $Input Then
                _trouve_code()
            Else
                GUISetAccelerators("")
                ControlSend($gui, "", _GuiCtrlGetFocus($gui), "{ENTER}")
                GUISetAccelerators($a_AccelKeys)
            EndIf

   EndSwitch
WEnd

Func _trouve_code()
   $code=GUICtrlRead($Input)
   $result=correspond($arr,$code)
   GUICtrlsetdata($Label_aeroport,$result)
EndFunc

Func toArrayCsv($s)
    Local $line = StringSplit($s,@CRLF,2+1) ; combien de ligne de mon fichier <> tableau
    Local $maxCol = 1 ; nombre de colonne de depart
    Local $a[UBound($line)][$maxCol] ; je crée un tableau de n ligne et de 1 colonne que je modifierai à ma guise
    ProgressSet(0)
   For $i = 0 To UBound($line)-1 ; parcours toutes les lignes

      Local $tmp = StringSplit($line[$i],";",3) ; combien de colonnes pour la ligne n°i
        If(UBound($tmp) > $maxCol ) then $maxCol = UBound($tmp) ; pour connaitre le nombre de colonnes
        ReDim $a[UBound($line)][$maxCol] ; je redimensionne mon tableau aux bonnes dimensions
        For $j = 0 To UBound($tmp)-1 ; je parcours cellule par cellule "de gauche a droite"
            ProgressSet($i*100/UBound($line),"Lecture de la ligne : " & $i)
         $a[$i][$j] = $tmp[$j] ; je remplis chaque cellule
        Next
    Next
   ProgressOff()
    Return $a
EndFunc

func correspond($array, $code)
   $result = _ArrayFindAll($array, $code, 0, 0, 0, 1)
   If IsArray($result) Then
      For $i = 0 To UBound($array) - 1
         $name = $array[$result[$i]][1]& ", " & $array[$result[$i]][2] & ", " & $array[$result[$i]][3] & " (Nom, Ville, Pays)"
         return $name
      Next
   Else
      Return "Non trouvé"
   EndIf
EndFunc

Func _GuiCtrlGetFocus($hGui = "")
    Local $InputID = ControlGetHandle($hGui, "", ControlGetFocus($hGui))
    Return _ControlGetGuiID($InputID)
EndFunc ;==>_GuiCtrlGetFocus

Func _ControlGetGuiID($hCtrl)
    Local $Result = DllCall("user32.dll", "int", "GetDlgCtrlID", "hwnd", $hCtrl)
    If @error = 0 Then Return $Result[0]
    Return SetError(1, 0, '')
EndFunc ;==>_ControlGetGuiID
Dans ce programme le fichier codesnew.csv est lu et placé dans une array, lorsqu'on fait une recherche, la correspondance est trouvée dans l'array et affichée.

Avantage : l'accès est immédiat
Inconvénient : au lancement, le programme est long a charger.

deuxième idée : faire parcourir le fichier a chaque requête
#include <array.au3>
#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <File.au3>
global $gui,$Input,$Label_aeroport,$arr,$file=@ScriptDir & "\codes.csv"
$gui = GUICreate("Codes Aéroports V 1.1", 730, 128, 232, 194)
$Input = GUICtrlCreateInput("", 16, 24, 30, 21)
GUICtrlCreateLabel("<--- Code Aéroportuaire : Ex: STN", 60, 24, 200, 21)
GUICtrlCreateLabel("Correspond à :", 16, 64, 76, 17)
$Label_aeroport = GUICtrlCreateLabel("", 104, 64, 608, 25)
$Group1 = GUICtrlCreateGroup("", 8, 8, 713, 105)
GUICtrlCreateLabel("Concept && dévellopement : Marc GRAZIANI", 500, 96, 220, 17)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Button_ok = GUICtrlCreateButton("OK", 648, 16, 65, 25)

If not FileExists($file) then
   msgbox(64,"Erreur", "Fichier Codes.csv Manquant" & @CRLF & "Je quitte")
   Exit
EndIf

HotKeySet("{enter}","_recherche_code")

GUISetState(@SW_SHOW)

While 1
   $nMsg = GUIGetMsg()
   Switch $nMsg
      Case $GUI_EVENT_CLOSE
         Exit
      Case $Button_ok
         _recherche_code()
   EndSwitch
WEnd

Func _recherche_code()
   $flag=0
   guictrlsetdata($Label_aeroport,"")
    $file_maxline = _FileCountLines($file)
   $recherche_code = GUICtrlRead($Input)
   $pc_liste = FileOpen($file, 0)
    If $pc_liste = -1 Then
        MsgBox(0, "Erreur", "Impossible d'ouvrir le fichier !")
        Exit
    EndIf
   ProgressOn("Recherche","Je recherche... Patientez... ")
   for $i = 1 to $file_maxline
      ProgressSet($i * 100 / $file_maxline)
      $line = FileReadLine($pc_liste,$i) ; on parcours chaque ligne du fichier TXT
        If StringInStr(stringleft($line,3), $recherche_code) Then
         $flag=1
         $split = StringSplit($line,";")
         GUICtrlsetdata($Label_aeroport,$split[2])
         ProgressOff()
         ExitLoop
      EndIf
      if $i=$file_maxline and $flag =0 Then
         GUICtrlsetdata($Label_aeroport,"Je suis désolé, je ne trouve pas")
         ProgressOff()
         ExitLoop
      EndIf
   Next
    FileClose($file)
EndFunc
Ici, le fichier codes.csv est lu a chaque requête, plus besoin de créer une array.

avantage : le lancement du programme est immédiat
Inconvénient : en cas d'accès répété les temps de recherche sont longs.

troisième idée : transformer le fichier CSV en fichier ini contenant des balises et des labels. (solution retenue)
Les Codes IATA étant générés sur 3 lettres ou chiffres, il est possible de les ordonner alphabetiquemement et numeriquement. chaque balise regroupant les codes de sa lettre ou son chiffre.
#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <EditConstants.au3>
global $gui,$Input,$Label_aeroport,$arr,$file=@ScriptDir & "\codes-IATA.ini"

$gui = GUICreate("Codes Aéroports V 1.3", 830, 128, 232, 194)
$Input = GUICtrlCreateInput("", 16, 24, 30, 21)

GUICtrlCreateLabel("<--- Code Aéroportuaire : Ex: STN", 60, 24, 200, 21)
GUICtrlCreateLabel("Correspond à (Nom, Ville, PAYS) : ", 16, 75, 170, 17)
$Label_aeroport = GUICtrlCreateLabel("", 190, 75, 608, 25)
$Group1 = GUICtrlCreateGroup("", 8, 8, 813, 105)
GUICtrlCreateLabel("Concept && dévellopement : Marc GRAZIANI", 600, 96, 220, 17)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Button_ok = GUICtrlCreateButton("OK", 748, 16, 65, 25)
$button_QUITTER = GUICtrlCreateButton("Quitter",748,46,65,25)
$accEnter = GUICtrlCreateDummy()

Global $a_AccelKeys[1][2] = [["{ENTER}", $accEnter]]
GUISetAccelerators($a_AccelKeys)

If not FileExists($file) then
   msgbox(64,"Erreur", "Fichier " & $file & " Manquant" & @CRLF & "Je quitte")
   Exit
EndIf

GUISetState(@SW_SHOW)

While 1
   $nMsg = GUIGetMsg()
   Switch $nMsg
      Case $GUI_EVENT_CLOSE, $button_QUITTER
         Exit
      Case $Button_ok
         _recherche_code()
      Case $accEnter
            If _GuiCtrlGetFocus($gui) = $Input Then
                _recherche_code()
            Else
                GUISetAccelerators("")
                ControlSend($gui, "", _GuiCtrlGetFocus($gui), "{ENTER}")
                GUISetAccelerators($a_AccelKeys)
            EndIf
   EndSwitch
WEnd

Func _recherche_code()
   guictrlsetdata($Label_aeroport,"")
   $recherche_code = GUICtrlRead($Input)
   $lettre_recherche = StringLeft($recherche_code,1)
   $result = IniRead($file, stringupper($lettre_recherche), stringleft($recherche_code,3),"")
   If $result = "" then
      GUICtrlsetdata($Label_aeroport,"Je suis désolé, je ne trouve pas")
   Else
      GUICtrlsetdata($Label_aeroport,$result)
   EndIf
EndFunc

Func _GuiCtrlGetFocus($hGui = "")
    Local $InputID = ControlGetHandle($hGui, "", ControlGetFocus($hGui))
    Return _ControlGetGuiID($InputID)
EndFunc ;==>_GuiCtrlGetFocus

Func _ControlGetGuiID($hCtrl)
    Local $Result = DllCall("user32.dll", "int", "GetDlgCtrlID", "hwnd", $hCtrl)
    If @error = 0 Then Return $Result[0]
    Return SetError(1, 0, '')
EndFunc ;==>_ControlGetGuiID
Lors de l'interrogation, la première lettre de l'imput est selectionnée comme balise, et les autres comme label. Pas besoin de creer une array, le fichier codes-IATA.ini est lu a chaque interrogation, mais cette fois cis, l'organisation fait que seule la partie concernée est lue et non l'entièreté comme précédemment.

Avantage : ouverture et temps d'accès immédiats.
Inconvénients : demande la transformation du fichier CSV en INI

Je laisse cette petite recherche pour ceux que aimeraient faire de même et qui ne savent pas comme s'y prendre.... tout en sachant que certains sont bien meilleur que moi (qu'ils n’hésitent pas à modifier et améliorer le code)
Fichiers joints
codes.zip
codes.csv + codes-IATA.ini + codesnew.csv
(354.91 Kio) Téléchargé 386 fois
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2272
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#2

Message par jchd »

Le fichier fourni codesnew.csv comporte deux erreurs :
ligne 2502 code FTY il manque la ville (Faya-Largeau)
ligne 2699 code HEW il manque le pays (GREECE)

Le stockage idéal pour ce type de données est une base SQLite. Pas de chargement ni de transformation, accès et recherche ultra rapides, index géré automatiquement, facilité d'ajout de champs additionnels (par exemple code pays ISO2 ou 3), ...
IATA.zip
(174.44 Kio) Téléchargé 410 fois
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
Avatar du membre
orax
Modérateur
Modérateur
Messages : 1479
Enregistré le : lun. 23 mars 2009 04:50
Localisation : ::1
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#3

Message par orax »

Avec cet hébergeur de fichiers il faut attendre 20 minutes entre chaque téléchargement, ce qui n'est pas pratique. Donc j'ai édité le premier message pour y ajouter un fichier ZIP qui contient les trois fichiers. Dans le codesnew.csv du .zip, j'ai fait les corrections signalées par jchd.
De petits détails peuvent faire toute la différence. — Quand la boule de neige commence à rouler… poussez-la. (Columbo)
mdanielm
Membre émérite
Membre émérite
Messages : 254
Enregistré le : mer. 11 déc. 2013 19:48
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#4

Message par mdanielm »

Et ça ?
$sCodes = FileRead(".\codes\codesnew.csv")
$code ="ZZV" ; A0 suit

$p = StringInStr($scodes, $code)

if $p>0 Then
   $q = StringInStr($sCodes, @crlf, 0, 1, $p)
   msgbox(0,"", StringMid($scodes, $p, $q-$p))
EndIf
Avatar du membre
mikell
Spammer !
Spammer !
Messages : 6292
Enregistré le : dim. 29 mai 2011 17:32
Localisation : Deep Cévennes
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#5

Message par mikell »

Et ça ? :mrgreen:

@jc : tu ne posterais pas le code qui permet d'utiliser ta base ?
$sCodes = FileRead(".\codes\codesnew.csv")
    $code ="ZZV" ; A0 suit
#cs
    $t = TimerInit()
    $p = StringInStr($scodes, $code)
    if $p>0 Then
        $q = StringInStr($sCodes, @crlf, 0, 1, $p)
        $s = StringMid($scodes, $p, $q-$p)
        $r = TimerDiff($t)
        ConsoleWrite($s &@crlf& $r &@crlf&@crlf)
    EndIf
#ce
Sleep(100)

     $t = TimerInit()
     $q = StringRegExp($sCodes, '(?m)^' & $code & '\N+', 1)
     If not @error Then
        $s = $q[0]
        $r = TimerDiff($t)
        ConsoleWrite($s &@crlf& $r &@crlf&@crlf)
    EndIf
" L'échec est le fondement de la réussite. " (Lao-Tseu )
" Plus ça rate, plus on a de chances que ça marche " (les Shadoks )
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2272
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#6

Message par jchd »

J'veux bien, mais il n'est pas écrit ! J'utilise à 99% SQLite Expert pour gérer à peu près tout, sans avoir à écrire une seule ligne de code.

Là n'est pas le problème, le code serait simple. L'idée est démontrer l'intérêt de ce type de stockage et que, malgré une apparente complexité initiale, une requête SQL très simple permet par la suite de rechercher un code ou nom ou ville incomplet en association avec un pays (ici, intérêt de stocker le code ISO2 ou ISO3) ou un autre indice. Suivant les besoins de l'application (ou de plusieurs applis) on peut avoir un jour à identifier régulièrement un aéroport selon des critères un peu "diagonaux".

J'ajouterais qu'ici notre fichier est tout petit et ne fait que 10k entrées, mais que la gestion d'une table de 100M d'entrées se "verrait" à peine plus, modulo création des index utiles aux recherches coûteuses.
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
Avatar du membre
orax
Modérateur
Modérateur
Messages : 1479
Enregistré le : lun. 23 mars 2009 04:50
Localisation : ::1
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#7

Message par orax »

Et ça ? Image
convertcsv.xml.zip
codesnew.csv converti en XML
(135.19 Kio) Téléchargé 351 fois
Local $oXmlDoc = ObjCreate("Msxml2.DOMDocument.6.0")

$oXmlDoc.load("convertcsv.xml")
If $oXmlDoc.parseError.errorCode <> 0 Then
   $oErr = $oXmlDoc.parseError
   ConsoleWriteError($oErr.reason & @CRLF)
   Exit 1
EndIf

$oXmlDoc.setProperty("SelectionLanguage", "XPath")
$t = TimerInit()
$oNode = $oXmlDoc.selectSingleNode("/root/Code[text() = 'ZZV']")
$d = TimerDiff($t)
ConsoleWrite($d & @CRLF)
ConsoleWrite($oNode.text & @CRLF)
ConsoleWrite($oNode.getAttribute("Name") & @CRLF)
ConsoleWrite($oNode.getAttribute("City") & @CRLF)
ConsoleWrite($oNode.getAttribute("Country") & @CRLF)
C'était juste pour voir si mon code allait être plus rapide que celui de mikell ! ^^
De petits détails peuvent faire toute la différence. — Quand la boule de neige commence à rouler… poussez-la. (Columbo)
Avatar du membre
mikell
Spammer !
Spammer !
Messages : 6292
Enregistré le : dim. 29 mai 2011 17:32
Localisation : Deep Cévennes
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#8

Message par mikell »

@jc
Absolument d'accord avec tout ce que tu as dit, évidemment
Mais un petit code AutoIt (même très basique) pour illustrer le propos, c'est quand même sympa
#include <SQLite.au3>
#include <Array.au3>

_SQLite_Startup ()
_SQLite_Open(@ScriptDir & "\IATA.sq3")

Local $tablenames, $array, $hQuery, $aNames

#cs
; histoire de savoir ce que contient la base
 $ret = _SQLite_GetTable2d(-1, "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;", $tablenames, 0, 0)
  _ArrayDisplay($tablenames, "nom des tables")
For $i = 1 to UBound($tablenames)-1
  _SQLite_Query(-1, "SELECT * FROM '" & $tablenames[$i][0] & "';", $hQuery)
  _SQLite_FetchNames($hQuery, $aNames)
  _ArrayDisplay($aNames, "table " & $tablenames[$i][0] & " : nom des colonnes")
;_SQLite_GetTable2d (-1, "SELECT * FROM '" & $tablenames[$i][0] & "' ;", $array, 0, 0)
;_ArrayDisplay($array, "table " & $tablenames[$i][0])
Next
#ce

_SQLite_GetTable2d (-1, "SELECT Code || '|' || Name || ' ' || City || '|' || Country FROM Codes WHERE Code = 'ZZV';", $array, 0, 0)
 Msgbox(0,"", $array[1][0])

_SQLite_Close ()
_SQLite_Shutdown ()
" L'échec est le fondement de la réussite. " (Lao-Tseu )
" Plus ça rate, plus on a de chances que ça marche " (les Shadoks )
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2272
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#9

Message par jchd »

Manque de temps, M'sieur !
Ce soir je posterai une version mise à jour avec les codes pays ISO2, ISO3 et M49. Le fichier d'origine me semble d'ailleurs très suspect de ce point de vue car il y a des pays ambigüs (UNITED STATES), dont le nom est mal orthograpié (KYRGYSTAN), officieux (VIRGIN ISLANDS (BRITISH)) ou tronqué (HONG KONG) et d'autres qui n'existent carrément plus depuis longtemps (ZAIRE, YUGOSLAVIA). C'est pour cette raison que la recherche ou le classement par nom de pays explicite n'est pas très recommandé, les codes ISO (surtout ISO2 à cause du Kossovo qui n'a été répertorié ni en ISO3 ni en M49) ou M49 sont préférables.
Je prendrai le temps de faire un bout de code si le besoin s'en fait sentir. On peut toujours charger la base dans SQLite Expert pour tout savoir sur elle, essayer des requêtes, créer des vues ou autre, ce qui permet ensuite de coder des requêtes "en dur" sous AutoIt dont on sait qu'elles fonctionnent.

Tout ceci n'était de ma part qu'une illustration des bénéfices de ce type de stockage sur un exemple concret. Le plus (peut-être inutile ici) est qu'on peut aussi par exemple stocker le nom natif des aéroports/villes/pays dans leur script natif (LIBYA -> دولة ليبيا) et utiliser cette même base avec le même code SQL sur toute plate-forme matérielle/logicielle imaginable, du PIC au mainframe, ce qui n'est même pas vrai pour un fichier texte Unicode : UTF avec ou sans BOM, UTF16-LE ou -BE, LF ou CR ou CRLF, CSV (Comma) ou CSV (Column) ou TSV (Tab), etc.
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
amphase
Niveau 3
Niveau 3
Messages : 40
Enregistré le : mar. 30 août 2016 00:28
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#10

Message par amphase »

Bonjour,
Pour ne pas être à l'aise avec SQL, je me serai penché sur Scripting Dictionary, rapide et relativement simple d'emploi.
Une autre piste... :)
Avatar du membre
Yle
Niveau 5
Niveau 5
Messages : 167
Enregistré le : mar. 21 avr. 2009 17:22
Localisation : La NORMANDIE -76
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#11

Message par Yle »

Bonjour,

J'ai essayé le script de Mikell et j'ai cette erreur :

"D:\AU3\Sqlite_Test1.au3"(35,135) : error: _SQLite_GetTable2d() called with Const or expression on ByRef-param(s).
$tt=_SQLite_GetTable2d (-1, "SELECT Code || '|' || Name || ' ' || City || '|' || Country FROM Codes WHERE Code = 'ZZV';", $array, 0, 0)

?
Avatar du membre
mikell
Spammer !
Spammer !
Messages : 6292
Enregistré le : dim. 29 mai 2011 17:32
Localisation : Deep Cévennes
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#12

Message par mikell »

@Yle
Essaie comme ça
Local $array, $iRows, $iColumns
_SQLite_GetTable2d (-1, "SELECT Code || '|' || Name || ' ' || City || '|' || Country FROM Codes WHERE Code = 'ZZV';", $array, $iRows, $iColumns)
@amphase
Et un ptit code pour illustrer le commentaire ? :P

@jc
Je redoute ce que tu prépares. Est-ce que tu as conscience que tes exemples, s'ils sont parfaitement didactiques, arrivent à l'être tellement qu'ils peuvent faire fuir en courant le scripteur occasionnel qui n'a - a priori - pas de préjugé défavorable particulier vis à vis de, par exemple, SQLite ? :mrgreen:
" L'échec est le fondement de la réussite. " (Lao-Tseu )
" Plus ça rate, plus on a de chances que ça marche " (les Shadoks )
Avatar du membre
Yle
Niveau 5
Niveau 5
Messages : 167
Enregistré le : mar. 21 avr. 2009 17:22
Localisation : La NORMANDIE -76
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#13

Message par Yle »

Bonsoir,

OK pour _SQLite_GetTable2d et ses paramètres mais la msgbox plante car il ne doit pas trouver de correspondance dans la base...
Avatar du membre
orax
Modérateur
Modérateur
Messages : 1479
Enregistré le : lun. 23 mars 2009 04:50
Localisation : ::1
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#14

Message par orax »

#include <SQLite.au3>
#include <Array.au3>
#include <MsgBoxConstants.au3>

_SQLite_Startup()
If @error Then
   MsgBox($MB_SYSTEMMODAL, Default, "SQLite n'a pas pu être chargé." & @CRLF & _
         "sqlite3.dll ou sqlite3_x64.dll n'a pas été trouvé dans les dossiers @SystemDir, @WindowsDir, @ScriptDir, @WorkingDir, @LocalAppDataDir\AutoIt v3\SQLite")
   Exit -1
EndIf

If Not FileExists(@ScriptDir & "\IATA.sq3") Then Exit 1

_SQLite_Open(@ScriptDir & "\IATA.sq3")
If @error Then
   MsgBox($MB_SYSTEMMODAL, Default, "Impossible de charger la base de données.")
   Exit -1
EndIf

Local $array, $iRows, $iColumns
Local $iRval = _SQLite_GetTable2d(-1, "SELECT Code || '|' || Name || ' ' || City || '|' || Country FROM Codes WHERE Code = 'ZZV';", $array, $iRows, $iColumns)
If $iRval = $SQLITE_OK Then
   MsgBox(0, "", $array[1][0])
   _SQLite_Display2DResult($array)
Else
   MsgBox($MB_SYSTEMMODAL, Default, "Erreur SQLite : " & $iRval & @CRLF & _SQLite_ErrMsg())
EndIf

_SQLite_Close()
_SQLite_Shutdown()
De petits détails peuvent faire toute la différence. — Quand la boule de neige commence à rouler… poussez-la. (Columbo)
amphase
Niveau 3
Niveau 3
Messages : 40
Enregistré le : mar. 30 août 2016 00:28
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#15

Message par amphase »

Double post supprimé.
Quand tu passes pour un con, passe vite...
Pierre Dac
Zooom...
Modifié en dernier par amphase le ven. 16 sept. 2016 23:33, modifié 2 fois.
amphase
Niveau 3
Niveau 3
Messages : 40
Enregistré le : mar. 30 août 2016 00:28
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#16

Message par amphase »

Et un ptit code pour illustrer le commentaire ? :P
Je me dois de répondre à cette truculente provocation de mikell. :wink: Il serait trop aisé de ma part de rétorquer que j'ai fourni un lien, et que le code était au bout du clic. J'ai bien compris qu'un exemple entaché de la sueur de mon front était attendu. Alors, bien qu'ayant d'autres chats à fouetter - dont le pauvre URL, resté coincé pendant plus de cinq jours sur le toit d’autoit à miauler du code en si bémol triphasé :shock: – je vais, de bonne grâce, démontrer que j’ai bien compris que ce forum était un lieu d’échanges et que je n’étais pas là juste pour faire bosser les bienveillants, néanmoins débordés bénévoles compétents. On ne pompe pas les gens qui citent les Shadoks, on les honore. 8)

@Yle
La proposition qui suit correspond à mon (variable) ressenti du moment. Elle n'a aucune prétention, en dehors de celle d'être entièrement écrite avec AutoIt (aucune dll qui a fricoté avec des langages de mauvaise vie n’est requise ; du 100% pur AutoIt).

Dès les premières manipulations du fichier codesnew.csv, je me suis aperçu que la Base de Données n’était pas entièrement fiable (malgré les corrections apportées par les intervenants depuis l’ouverture du sujet) et que quelques contrôles supplémentaires s’imposaient. Aussi, le ptit code qui suit traite d’abord de l’intégrité des données. J’aborderai Scripting.Dictionary dans la seconde partie avec des données propres et la conscience tranquille.

Pour le projet qui nous intéresse ici, les données ont 3 propriétés à surveiller de près : le format, le type et le contenu. Bien que les deux premières propriétés interfèrent entre elles, il est possible de développer des routines de recherche d’erreurs en interne. Pour le contenu, il en est autrement : si Paris a été saisi en Chine, il faut croiser les données avec d’autres pour cibler l’erreur. Là, ça peut déclencher un fou rire si les formats sont très différents ou que l’autre BdD est en plus mauvais état que la première. Pour ce qui suit, il me faut construire un tableau 2D de 5 colonnes de la forme $aArray[n][5] ; les 4 existantes dans codesnew.csv, auxquelles j'ajouterai une colonne d’index (ici, ça ne consiste qu'à numéroter les lignes) pour un adressage... indexé (servira en seconde partie). Le script est grassement commenté, sauf les expressions régulières que je détaille à part pour ne pas étouffer le code. Auparavant, voici les pensées du jour pouvant aussi fonctionner certaines nuits :
  • Les premières lignes du script doivent être celles qui s’assurent de la qualité des données, parce qu’il vaut mieux un programme qui plante, on sait qu’il y a quelque chose à corriger, qu’un programme qui laisse passer des données erronées vers une commande de sécurité, comme l’ABS ou certains boutons rouges. :twisted: Ce serait drôle (comme atterrir à Paris, en Chine) ou pas, mais c’est sur que ce serait bref. :mrgreen:
    Si la rigueur doit être plus ou moins proportionnelle à la taille des données, il est essentiel de privilégier le traitement des erreurs.
La suite en écoutant : « En v'la du code en v'la ! »
#include <Array.au3>
#include <File.au3>
#include <FileConstants.au3>
#include <MsgBoxConstants.au3>
#include <TrayConstants.au3>

Opt("MustDeclareVars", 1)

Local $hTimer = TimerInit() ; initialise le chrono
Local $aCodesArray ; tableau des données complètes
Local $sCodesCsvPath = ".\codes\codesnew.csv"

If Not FileExists($sCodesCsvPath) Then $sCodesCsvPath = FileOpenDialog("Merci de localiser 'codesnew.csv'", @WorkingDir, "(*.csv)")

Local $iErrFlag = _FileReadToArray($sCodesCsvPath, $aCodesArray, $FRTA_NOCOUNT) ; lit dans un tableau 1D les lignes entières
If $iErrFlag <> 1 Then Exit MsgBox($MB_TOPMOST, "Erreur lecture !", "Le fichier 'codesnew.csv' ne peut être lu." & @CRLF & @CRLF & "Erreur : " & @error)

_ArrayDelete($aCodesArray, "0") ; supprime la 1ère ligne (labels Code;Name;City;Country) pour simplifier le traitement qui suit

Local $aOutArray[0][5] ; déclare un nouveau tableau (vide) de 5 colonnes (4 données + 1 index)
For $iLine = 0 To UBound($aCodesArray) - 1
   If Not StringRegExp($aCodesArray[$iLine], "(*UCP)[0-9A-Z]{3}\s(;?.+){3}", 0) Or _
      Not StringRegExp($aCodesArray[$iLine], "([;][^;\r\n]+){3}", 0) Then _
      MsgBox($MB_TOPMOST, "Corrige donc ça !", "Y-a une nouille dans le goulash, ligne : " & _
      $iLine + 1 & @CRLF & @CRLF & $aCodesArray[$iLine]) ; vérifie le format voir commentaires

   ; ==== si l'espace clignotante après 'codes' est d'une quelconque utilité, supprimez LA ligne suivante sans pitié - mais dans ce cas seulement.  ====
   $aCodesArray[$iLine] = StringRegExpReplace($aCodesArray[$iLine], "(*UCP)(?<=[0-9A-Z]{3})\s*", "") ; ben justement, supprime l'espace en question

   $aCodesArray[$iLine] = StringSplit($aCodesArray[$iLine], ";", $STR_NOCOUNT) ; crée un nouveau tableau de 4 lignes dans chaque ligne du tableau principal (une aspirine ?) - par éclatement
   _ArrayAdd($aCodesArray[$iLine], $iLine + 1, Default, Default, Default, $ARRAYFILL_FORCE_INT) ; ajoute l'index en ligne 5
   _ArrayTranspose($aCodesArray[$iLine]) ; tourne ça de 90° et ça fait 5 colonnes
   _ArrayAdd($aOutArray, $aCodesArray[$iLine]) ; les données sont ajoutées en fin du nouveau tableau àà chaque tour de boucle

   If $iLine / 100 = Int($iLine / 100) Then TrayTip("C'est long, mais c'est qu'une fois.", _
         "J'en suis à la ligne " & $iLine & "/" & UBound($aCodesArray) - 1 & @CRLF & _
         Int(TimerDiff($hTimer) / 100) & " secondes", 30, $TIP_NOSOUND) ; informe toutes les 100 lignes
Next

Local $sCodeCleanPath = StringRegExpReplace($sCodesCsvPath, "codesnew.csv", "CodesClean.data")
$iErrFlag = _FileWriteFromArray($sCodeCleanPath, $aOutArray, "|") ; sauve le noveau fichier dans le même directory que l'ancien
If $iErrFlag <> 1 Then Exit MsgBox($MB_TOPMOST, "Erreur d'écriture !", _
      "Le fichier 'CodesClean.data' ne peut être écrit." & @CRLF & @CRLF & "Erreur : " & @error)
ConsoleWrite(@LF & "Temps total : " & Int(TimerDiff($hTimer) / 1000) & " secondes" & @LF)
_ArrayDisplay($aOutArray, "Après une bonne douche de " & Int(TimerDiff($hTimer) / 60000) & " minutes :") ; rien que pour le plaisir des yeux !

; C'est UN petit exemple de test d'intégrité du FORMAT des données, il y a plein d'autres à faire...
; 2 par jour, matin et soir, pendant 8 jours. A renouveller 3 fois.
Les RegEx (lisez-les de gauche à droite, la tête en haut et gardez au moins un pied au sol, avec l’autre vous pouvez vous gratter le menton, sauf pour les unijambistes et les culs de jatte qui pourraient peut-être se mettre à SQL, mais là, il faudrait l’avis d’un spécialiste) :
  • Ligne 22 : [0-9A-Z]{3}\s(;.+){3}
    Vérifie que le code IATA (n’importe quel caractère compris entre 0 et 9 ou entre A et Z, exactement trois répétitions) est suivi d’un caractère d'espacement puis d’un groupe comprenant un ‘ ;’ suivi de n’importe quel caractère sauf fin de ligne, ce groupe est répété très exactement 3 fois, pas plus, pas moins, ce n’est pas négociable.
  • Ligne 23 : [;][^;\r\n]+){3}
    Vérifie qu’il y a 3 cellules séparées par 1 ‘;’(1 unique ‘ ;’ suivi n’importe quel caractère sauf ‘ ;’, CR et LF et tout le bousin répété très précisément 3 fois. 3 fois ? Oui, 3 fois.
  • Ligne 28 : (?<=[0-9A-Z]{3})\s*
    Après le code IATA, supprime l’espace qu’il y a, qu’il n’y a pas, qu’il y a, qu’il n’y a pas… Une espèce d’espace clignotante (que voulez-vous que j’y fasse si les mots ‘espèces’ et ‘espace’ sont tous deux féminins et que personne ne vous l’a dit jusqu’à présent.)
Plus il y a d’algorithmes de contrôle différents, plus les données seront fiables, ou bonnes à jeter, c’est selon…
La construction du tableau me paraît suffisamment commentée dans le code. J’aimerai toutefois apporter une considération : le code est particulièrement lent (alors que ça doit-être quasi instantané avec SQL – j’ai été sidéré par la vitesse d’exécution d’un solveur de Sudoku écrit en SQL). Le ralentissement se fait à la ligne 33 et la durée du traitement augmente à chaque tour de boucle : c’est la fonction _ArrayAdd qui en est la cause (en jetant un œil dans l’include Array.au3, on y voit le nombre conséquent d’opérations de lecture-écriture effectuées). Puisque la charge augmente à chaque tour, j’aurais pu amenuiser problème en sortant 5 ou 6 tableaux puis en les concaténant à la fin ; d’autres possibilités existent probablement, mais du fait de son unicité, tant que le temps de génération du tableau est inférieur à 5 mnt, je ne vais pas en passer 20 à coder pour en gagner 4 lors de l’exécution surtout que pendant ces quelques minutes, vous pouvez en profiter pour assouvir un besoin physiologique comme manger un fruit ou fourrer vos doigts dans votre nez, :shock: si personne ne vous voit ; c’est vous qui voyez…

Un dernier mot, le code de ce post est à compléter puisqu’il y au moins une erreur dans le tableau initial qui n’a pas été corrigée. Comme nous ne sommes pas à Pâques, je laisse un indice : c’est entre KIF et PAF. Bonne chasse à l’œuf. :)
Maintenant, Yle, si tu ne retiens pas l’option Scripting.Dictionary, je la remets dans la culotte je ne vois pas l’intérêt d’écrire une seconde partie et c’est...

@ mikell
Et la chasse aux œufs en SQL, comment ça se passe  ? L’histoire qu’Yle fasse son choix en toute connaissance de cause, un ptit code pour illustrer ? :P :P C’est pas moi qui ai commencé ! :wink:
Modifié en dernier par amphase le lun. 12 sept. 2016 22:01, modifié 2 fois.
Avatar du membre
walkson
Modérateur
Modérateur
Messages : 1020
Enregistré le : ven. 12 août 2011 19:49
Localisation : Hurepoix
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#17

Message par walkson »

Bonjour,
Faut que j’arrête le whisky au petit déjeuné, je vois double :mrgreen:
Sinon, je ne vois pas de Scripting.Dictionary dans votre code (ou bien je n'ai rien compris à l'explication de Jchd)
Pour ma part, je partage totalement l'avis de Jchd et l'utilisation de sqlite qui est simple et très rapide. Je travaille actuellement sur des dicos en base sqlite dont le plus gros fait plus de 123 000 lignes. La recherche est instantanée.
La recherche sur 4 ou 5 bases cumulées demande 2 à 3 secondes.
Je suis comme Mikell, j'aimerais bien que Jchd nous donne un exemple, quand il aura le temps, bien sûr !
Cordialement,
Walkson
"Horas non numero nisi serenas " Le canon de midi
(Je ne compte que les heures heureuses)
amphase
Niveau 3
Niveau 3
Messages : 40
Enregistré le : mar. 30 août 2016 00:28
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#18

Message par amphase »

...J’aborderai Scripting.Dictionary dans la seconde partie avec des données propres et la conscience tranquille.
...Maintenant, Yle, si tu ne retiens pas l’option Scripting.Dictionary, je la remets...
@walkson
Le whisky, à moins qu'il ne soit frelaté, n'est pas un problème, ma foi... si ce n'est avec les règles du forum qui prohibent même les pétards, c'est pour dire si c'est strict ici. Je promets de ne pas le répéter et tu comprendras que par les temps qui courent que je préfère les espèces. :mrgreen:
Puis, il n'y a pas que l'alcool et le code dans la vie, il y a aussi les scoubidous et la méthodologie scientifique.
C'est ta fonction "lecture en diagonale" qu'il faudrait remplacer par "lecture rapide et attentive" comme le montrent les citations ci-dessus. Mais le problème n'est peut-être qu'oculaire... qu'oculaire ? :roll:
  • Vous-vous rendez-compte, Mame Simone, mon mari, i'm fait cocue !
    Vous en avez de la chance, Mame Ginette, le mien i'm fait partout.
    Coluche
L'humour, c'est comme le whisky, faut commencer de bonne heure.
Alimentaire, mon cher walson. Santé ! :lol:

En tous cas, merci de ton intervention, sans laquelle je n'aurais pas fait une relecture supplémentaire révélant que j'avais mis des smileys dans les commentaires du code (si, si, j'ai fait ça et aucun filtre ne m'en a empêché !). :?
Si en SQL ça ne prend que quelques lignes, ça a beau être pur AutoIt, mon script est pachydermique. J'avoue que je ne m'attendais pas à tant de lignes... et de temps pour les écrire. Scripting.Dictionary est totalement innocent. Le code le démontre par des instructions ultra-courtes et une exécution très rapide, même si ce n'est pas instantané comme SQL ou le café lyophilisé. :) Il est annoncé une comparaison entre 2 listes de 100000 éléments en moins d'une seconde.

La fonction _ArrayUnique utilise Scripting.Dictionary pour trier. C'est un autre exemple. :)

@Yle
T'es parti à Yeurs (Ile et Vilaine) en quête d'une base de données plus propre ?
Je fournis l'exemple promis au moins pour walson et laisse la chasse aux œufs ouverte.
Le premier qui trouve gagne une sucette en bois de 20 Kg.
Actuellement, le programme va fonctionner parfaitement sur une partie des destinations et de façon erratique sur une autre. Vérifier et revérifier ses données est essentiel. Dans l'état actuel des choses, le client qui veut partir à Tahiti peut se retrouver au Groenland avec un ours polaire et un phoque nommé You pour vahinés !
Enfin, il est toujours possible, comme dans certaines administrations, de déclarer que c'est la faute de l'ordinateur et que ces saletés de machines s'autoprogramment quand le valeureux fonctionnaire a le dos tourné.

J'ai écrit un exemple a priori absurde et utile uniquement à titre d'exemple. Absurde, parce qu'écrire quoique ce soit pour traiter des données vérolées l'est (et laid). L'exemple le plus motivant, à mon sens, dans l'utilisation de Scripting.Dictionary, est la comparaison de données. Il me fallait un second gros fichier (dont tout le monde dispose) aux côtés de CodesClean.data (version partiellement dégraissée de codesnew.csv). J'ai opté pour le dictionnaire français de Firefox contenant 80 000 éléments. La mission consiste à ne retenir que les destinations dont le code IATA figure dans le dictionnaire orthographique de Firefox. Il y a quand même 700 mots, sigles, abréviations et acronymes contenus dans les codes IATA. Ces mots vont servir a une loterie pour tirer au sort la destination de vos prochaines vacances (une par membre de la famille pour de vraies vacances). Et puis voilà...

En attendant, j’ai traité de l’adressage indexé et vous avez maintenant un dictionnaire français, certes basique, mais en temps réel, et que vous allez pouvoir intégrer dans vos scripts, pour faire des corrections lors de la saisie, des mots croisés, des anagrammes, Scrabble et jeux lettres en tous genres… :idea: Je ne me suis pas documenté pour connaître les limites juridiques d’utilisation dudit dico. S’il équipe un programme qui sort de chez vous, songez à vous documenter. Il existe des dictionnaires libres de droits sur le net, mais ça vous oblige à l'ajouter en FileInstall alors que presque tout le monde possède Firefox. Pour savoir s’ils sont fiables, croisez-les. Prenez-en une dizaine et composez la substantifique moelle du dictionnaire ! Créez un correcteur de commentaires de code AutoIt pour les abrutis qui mettent des smileys vivants à l’intérieur. :mrgreen: Avant toute demande de ptit code supplémentaires, j'indique que j'ai du retard sur les miens. Merci d’avoir supporté mes pitreries jusqu’au bout. :)

Le code est très commenté ; sauf demande, je ne détaille ici que l’expression régulière :
(?<=\n)[a-zA-Z]{3}(?=\W)
Cherche un saut de ligne (LF) qui ne sera pas capturé et qui précède 3 lettres précisément  et se termine par un caractère non alphanumérique qui ne sera pas capturé. –respire…
#include <Array.au3>
#include <File.au3>
#include <FileConstants.au3>
#include <MsgBoxConstants.au3>
#include <TrayConstants.au3>

Opt("MustDeclareVars", 1)

HotKeySet("{ESC}", "Terminette") ; dès fois que la machine s'emballe

;  **************************************************************************************
;  ***** décommentez/commentez (ou pas) les '_ArrayDisplay' disposés un peu partout *****
;  **************************************************************************************

Global $aIATAtout
Local $sFilePath = ".\codes\CodesClean.data" ; s'il est pas là, c'est qu'il est ailleurs
If Not FileExists($sFilePath) Then $sFilePath = FileOpenDialog("Merci de localiser 'CodesClean.data'", @WorkingDir, "(*.data)")
_FileReadToArray($sFilePath, $aIATAtout, $FRTA_NOCOUNT, "|") ; lit les codes ITA d'Yle
;~ _ArrayDisplay($aIATAtout, "$aIATAtout", "", 8)


Local $oDict_FF = _FFdictMake()
If Not IsObj($oDict_FF) Then Exit MsgBox($MB_TOPMOST, "Erreur !", "$oDict_FF n'est pas un objet !")
Local $oDict_IATA = _IATAdictMake()
If Not IsObj($oDict_IATA) Then Exit MsgBox($MB_TOPMOST, "Erreur !", "$oDict_IATA n'est pas un objet !")
Local $oDict_IATA_FF = ObjCreate("Scripting.Dictionary") ; va servir à copier le précédent
If Not IsObj($oDict_IATA_FF) Then Exit MsgBox($MB_TOPMOST, "Erreur !", "L'objet Dictionnaire n'a pas pu être créé !")
$oDict_IATA_FF = $oDict_IATA ; dans cet objet vont être comparés les codes IATA et le dico FF

Local $hTimer = TimerInit() ; top départ chrono

#cs
   $vKey est un variant puisqu'on y fourre tout type de données commestibles
   (c'est vrai pour les clefs comme pour les items)
   tous les codes IATA qui n'existent pas dans le dico Firefox sont retirés
   du dico IATA par les 3 (ni plus, ni moins) lignes qui suivent :
#ce
For $vKey In $oDict_IATA_FF.Keys
   If Not $oDict_FF.Exists($vKey) Then $oDict_IATA_FF.Remove($vKey) ; si pas dans dico FF, éliminé !
Next

Local $fDiff = TimerDiff($hTimer) ; top arrivée
Local $aDisplayArray = $oDict_IATA_FF.Keys
_ArrayDisplay($aDisplayArray, "Clefs IATA dans FF") ; pour la recherche, seules les clefs sont utilisées
$aDisplayArray = $oDict_IATA_FF.Items()
_ArrayDisplay($aDisplayArray, "Items IATA (index)") ; rappel : les items IATA (couplés aux clefs IATA) _représentent l'index du fichier global 'CodesClean.data' dans lequel on récupère les données

MsgBox($MB_TOPMOST, "Résultat chrono :", "Le temps d'exécution de Scripting.Dictionary" & @CRLF & @CRLF & "a duré : " & $fDiff & " mS.")

; GRANDE LOTERIE : destination surprise avec un code IATA à l'orthographe vérifiée (youpi !)
Local $hWnd = FileOpen($sFilePath)
If $hWnd = -1 Then Exit MsgBox($MB_TOPMOST, "Erreur !", "Impossible d'ouvrir 'CodesClean.data' !")
Local $iIndexIATA = 0
Local $IDbutton = 0
Local $aGoodTrip

Local $aTemp_IATA_FF = $oDict_IATA_FF.Keys ; crée un tableau 1D pour tirer une clef au sort - N.B. pas besoin de boucle, c'est instantané comme SQL et Nescafé
;~ _ArrayDisplay($aTemp_IATA_FF, "IATA_FF.Keys")
Local $sWinnerCode = 0

While True
   $iIndexIATA = Random(1, UBound($aTemp_IATA_FF), 1) ; tirage au sort dans la liste des items
   $sWinnerCode = $aTemp_IATA_FF[$iIndexIATA] ; la clef IATA gagnante
   $iIndexIATA = $oDict_IATA.Item($sWinnerCode) ; nvlle valeur >> index $aIATAtout
   $aGoodTrip = StringSplit(FileReadLine($hWnd, $iIndexIATA), "|", $STR_NOCOUNT)
   $IDbutton = MsgBox(BitOR($MB_TOPMOST, $MB_OKCANCEL), "Yle & Glloq vous souhaitent bon vol !", "Code" _
   & @TAB & ":" & @TAB & $aGoodTrip[0] & @CRLF & "Nom" & @TAB & ":" & @TAB & $aGoodTrip[1] & @CRLF & "Ville" _
   & @TAB & ":" & @TAB & $aGoodTrip[2] & @CRLF & "Pays" & @TAB & ":" & @TAB & $aGoodTrip[3])
   If $IDbutton = $IDCANCEL Then Terminette()
WEnd

FileClose($hWnd)

Func _FFdictMake()
   Local $sFilePath = @ProgramFilesDir & "\Mozilla Firefox\dictionaries\fr.dic" ; s'il n'est pas là, c'est qu'il faut chercher ailleurs...
   Local $hWnd = FileOpen($sFilePath)
   If $hWnd = -1 Then Exit MsgBox($MB_TOPMOST, "Erreur !", "Impossible d'ouvrir le dictionnaire Firefox !")
   Local $sFFdicFull = FileRead($hWnd) ; lit le dictionnaire de Firefox
   If @error Then Exit MsgBox($MB_TOPMOST, "Erreur !", "Impossible d'ouvrir le dictionnaire Firefox !")
   FileClose($hWnd)
   ; extrait les mots de 3 lettres
   Local $aFFdic3 = StringRegExp($sFFdicFull, "(*UCP)(?<=\n)[a-zA-Z]{3}(?=\W)", 3) ; !!
   Local $aFFdic3Out = _ArrayUnique($aFFdic3, Default, 1, 0, $ARRAYUNIQUE_NOCOUNT, $ARRAYUNIQUE_DISTINCT) ; la fonction _ArrayUnique utilise Scripting.Dictionary pour fonctionner ! ;)

   ;  ***** LES CLEFS DOIVENT ETRE UNIQUES, pas les Items, qui peuvent être de tous types mélangés
   ;  ***** LE NOMBRE DE CLEFS ET D'ITEMS DOIT ETRE == STRICTEMENT EGAL
   Local $oDict_FF = ObjCreate("Scripting.Dictionary") ; instancie le dico FireFox à 3 lettres
   If Not IsObj($oDict_FF) Then Exit MsgBox($MB_TOPMOST, "Erreur !", "L'objet Dictionnaire 'Dict_FF' n'a pas pu être créé !")
   $oDict_FF.CompareMode = 1 ; option 'compare' en mode texte (insensible à la casse)

   For $i = 0 To UBound($aFFdic3Out) - 1
      $oDict_FF.Add($aFFdic3Out[$i], $aFFdic3Out[$i]) ; liste unique
   Next
   Local $aDisplayArray = $oDict_FF.Keys
;~    _ArrayDisplay($aDisplayArray, "Clefs Firefox") ; les items sont ==
   Return ($oDict_FF)
EndFunc   ;==>_FFdictMake


Func _IATAdictMake()
   ;  ***** SI LES DONNEES SONT FIABLES LES CODES IATA SONT UNIQUES !
   ;  ***** SI LES DONNEES NE SONT PAS FIABLES, DES RESULTATS SERONT FAUX ! (LEQUELS ?)
   Local $oDict_IATA = ObjCreate("Scripting.Dictionary") ; instancie le dictionnaire IATA
   If Not IsObj($oDict_IATA) Then Exit MsgBox($MB_TOPMOST, "Erreur !", "L'objet Dictionnaire n'a pas pu être créé !")
   $oDict_IATA.CompareMode = 1 ; option compare en mode texte

   For $i = 0 To UBound($aIATAtout) - 1
      $oDict_IATA.Add($aIATAtout[$i][0], $aIATAtout[$i][4]) ; les codes IATA (col 0) dans les clefs, l'index (col 4) dans les Items
   Next

   Local $aDisplayArray = $oDict_IATA.Keys
   _ArrayDisplay($aDisplayArray, "Clefs IATA") ; pour la recherche, seules les clefs sont utilisées
   $aDisplayArray = $oDict_IATA.Items()
;~    _ArrayDisplay($aDisplayArray, "Items index(IATA)") ; l'index sert à retourner la ligne entière du tableau
   Return ($oDict_IATA)
EndFunc   ;==>_IATAdictMake


Func Terminette()
   ClipPut("https://msdn.microsoft.com/fr-fr/library/x4k5wbx4(v=vs.84).aspx")
   MsgBox($MB_TOPMOST, "Lien MSDN", "La liste des Méthodes et Propriétés de 'Scripting.Dictionary'" & @CRLF _
   & @CRLF & "est accessible par le lien MSDN copié dans le presse-papier.") ; si vous en ressentez le besoin ;)
   Exit
EndFunc   ;==>Terminette

;                 ============= FIN DU PTIT CODE ==============
Modifié en dernier par amphase le lun. 12 sept. 2016 22:17, modifié 1 fois.
Avatar du membre
orax
Modérateur
Modérateur
Messages : 1479
Enregistré le : lun. 23 mars 2009 04:50
Localisation : ::1
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#19

Message par orax »

amphase a écrit :J’apporte une approche pour contourner une petite limite d’AutoIt : celle de ne lire ni d’écrire des tableaux de plus de 2D.
Ce sont les fonctions de l'UDF Array.au3 qui ne gèrent pas des tableaux de plus de 2D (et seulement 1D pour certaines), mais il est quand même possible d'utiliser des tableaux de plus de 2 dimensions.
Local $a[5][4][3]

$a[0][0][0] = 1
$a[4][3][2] = 9

ConsoleWrite($a[0][0][0] & @CRLF) ; 1
ConsoleWrite($a[4][3][2] & @CRLF) ; 9

ConsoleWrite(UBound($a) & @CRLF)    ; 5
ConsoleWrite(UBound($a, 2) & @CRLF) ; 4
ConsoleWrite(UBound($a, 3) & @CRLF) ; 3
De petits détails peuvent faire toute la différence. — Quand la boule de neige commence à rouler… poussez-la. (Columbo)
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2272
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: Accès et recherche dans un gros fichier texte (10 000 lignes)

#20

Message par jchd »

J’apporte une approche pour contourner une petite limite d’AutoIt : celle de ne lire ni d’écrire des tableaux de plus de 2D. Ici, il m’en faut 4, plus 1 pour ajouter une colonne d’index (ça consiste juste à numéroter les lignes) pour un adressage indexé (seconde partie).
Ca ne fait pas 5 dimensions mais 2.
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
Répondre