Lookup table et fichier texte

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Lookup table et fichier texte

Message par JagV12 »

Bonjour,

Sous W7x64, je désire pouvoir lister mes répertoires et sous-répertoires dans un fichier texte. J'ai trouvé une solution sur internet : ajouter une entrée "Chaîne" dans la base de registres contenant cmd.exe /c dir "%1" /A /S /C > "_Listing.txt".

Mais le listing obtenu est rempli de caractère étranges : il est encodé en ASCII Code Page 1252 et affiché (par Notepad et autres) en Code Page 480.
J'ai donc écrit un programme faisant la conversion inverse au moyen d'une LookUp Table et dont voici la partie utile :

Code : Tout sélectionner

EnableExplicit

Enumeration File
  #SourceFile
  #DestFile
EndEnumeration

Procedure ConvertFile(sFileName.s)
  Static.s sCP1252to480LUT = "ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»¦¦¦¦¦ÁÂÀ©¦¦++¢¥++--+-+ãÃ++--¦-+¤ðÐÊËÈiÍÎÏ++¦_¦Ì¯ÓßÔÒõÕµþÞÚÛÙýݯ´-±=¾¶§÷¸°¨·¹³²¦ "
  
      If ReadFile(#SourceFile, sFileName, #PB_Ascii)
        If CreateFile(#DestFile, sDestFileName, #PB_Ascii)
          WriteStringN(#DestFile, "Converted", #PB_Ascii) ; Tag the new file as "Converted"
          
          While Eof(#SourceFile) = 0
            sString = ReadString(#SourceFile, #PB_Ascii)
            *sString = Ascii(sString)
            sResult = ""
            iStrLength = MemorySize(*sString)
;             ShowMemoryViewer(*sString, iStrLength)
            For iStrIndex = 0 To iStrLength - 1
              Debug "sCharVal = '" + Str(PeekA(*sString + iStrIndex)) + "'"
              If PeekA(*sString + iStrIndex) > 127
                sResult = sResult + Mid(sCP1252to480LUT, PeekA(*sString + iStrIndex) - 127, 1)
                Debug "New  Val = '" + Mid(sCP1252to480LUT, PeekA(*sString + iStrIndex) - 127, 1) + "'"
              Else
                sResult = sResult + Mid(sString, iStrIndex + 1, 1)
              EndIf
            Next
            
            WriteStringN(#DestFile, sResult, #PB_Ascii)
            FreeMemory(*sString)
          Wend
          
          CloseFile(#DestFile)
          MessageRequester("Information", "File '" + sDestFileName + "' created")
        
        Else
          MessageRequester("Warning", "Can't create Destination File !", #PB_MessageRequester_Warning)
        EndIf
        CloseFile(#SourceFile)
      Else
        MessageRequester("Warning", "Can't open '" + sFileName + "' !", #PB_MessageRequester_Warning)
      EndIf
EndProcedure 

  

  
Define.i iProgramParametersNum
Define.s sFile

iProgramParametersNum = CountProgramParameters()
If iProgramParametersNum
  sFile = ProgramParameter(0)
  ConvertFile(sFile)
  
EndIf

End
Pour ce Thread de forum, il est expurgé des contrôles de création de fichier et autres (d'où l'indentation (2 espaces) perturbée)
C'est évidemment la boucle For-Next qui réalise la conversion. (Ça fonctionne mais vos suggestions concernant des améliorations seront bienvenues)

Je voudrais maintenant sauvegarder la transformation dans le même fichier (plutôt que d'en créer un nouveau). Or si je fais :

Code : Tout sélectionner

  If OpenFile(#SourceFile, sFileName, #PB_Ascii)
      
    While Eof(#SourceFile) = 0
      sString = ReadString(#SourceFile, #PB_Ascii)
      sResult = bla bla bla ; nouvelle chaîne de même longueur que la chaîne source
      WriteStringN(#SourceFile, sResult, #PB_Ascii)
    Wend
    
    CloseFile(#SourceFile)
je détruits la "structure" du fichier (WriteStringN écrase la ligne suivante au lieu d'écraser la ligne lue).
Quelle est la solution ?
Par avance merci
Demivec
Messages : 90
Inscription : sam. 18/sept./2010 18:13

Re: Lookup table et fichier texte

Message par Demivec »

Voici une façon:

Code : Tout sélectionner

  If OpenFile(#SourceFile, sFileName, #PB_Ascii)
      
    While Eof(#SourceFile) = 0
      iPrevLoc = Loc(#SourceFile) ;start of string
      sString = ReadString(#SourceFile, #PB_Ascii)
      iNextLoc = Loc(#SourceFile) ;after string
      sResult = bla bla bla ; nouvelle chaîne de même longueur que la chaîne source
      FileSeek(#SourceFile, iPrevLoc)
      ;Évitez d'écrire la fin de la ligne car elle peut être différente de celle lue.
      WriteString(#SourceFile, sResult, #PB_Ascii)
      FileSeek(#SourceFile, iNextLoc) ;skip past EOL in file
    Wend
    
    CloseFile(#SourceFile)
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Re: Lookup table et fichier texte

Message par JagV12 »

Merci pour votre réponse. Loc() et FileSeek() sont effectivement la solution pour se déplacer dans un fichier. Merci pour l'info

J'ai légèrement changé de stratégie : j'utilise "ReadData" à la place de "ReadString" car le format 8bit ASCII est conservé (ReadString convertit en 16bit UNICODE , il faudrait le faire suivre d'un *ptr = Ascii()). Voici donc mon nouveau code : (un EDIT en fin de post pour expliquer que j'ai trouvé l'erreur et poser les questions suivantes...)

Code : Tout sélectionner

  If ReadFile(#SourceFile, sFileName, #PB_Ascii)
    
    If ReadCharacter(#SourceFile, #PB_Ascii) = $A0 ; Make sure the file hasn't been already converted
      MessageRequester("Information", "File '" + sFileName + "' allready converted")
    Else
      FileSeek(#SourceFile, 0)
      
      iFileLength = Lof(#SourceFile)
      Debug "File Length: " + Str(iFileLength)
      *MemoryID = AllocateMemory(iFileLength)
      If *MemoryID
        iBytes = ReadData(#SourceFile, *MemoryID, iFileLength)   ; Read the Data in the File and store then in the Memory block
        Debug "Nombre d'octets lus: " + Str(iFileLength)
        iFileLength = iFileLength ; Both should be identical but it's safer to work with actually read data size
        ShowMemoryViewer(*MemoryID, iFileLength)
;        ShowMemoryViewer(@sCP1252to480LUT, 256) ; LUT is 16bit UNICODE 128 char => 256 bytes
  
  
        PokeA(*MemoryID, $A0) ; invisible tag (in replacement of SPC = $20) for the new file
        
        For iStrIndex = 1 To iFileLength - 1
          aCharVal = PeekA(*MemoryID + iStrIndex)
;           Debug "aCharVal = '" + Chr(aCharVal) + "' = Chr(" + aCharVal + ")"
          If aCharVal > 127
            aResult = Asc(Mid(sCP1252to480LUT, aCharVal - 127, 1))
;             Debug "New  Val = '" + aResult + "'"
            PokeA(*MemoryID + iStrIndex, aResult)
;             ShowMemoryViewer(*MemoryID, iFileLength)
          EndIf
        Next
        
        ShowMemoryViewer(*MemoryID, iFileLength)
        
        FileSeek(#SourceFile, 0)
        iBytes = WriteData(#SourceFile, *MemoryID, iFileLength)
        
        FreeMemory(*MemoryID)
      Else
        MessageRequester("Warning", "Memory Allocation Error !", #PB_MessageRequester_Warning)
      EndIf
    
      CloseFile(#SourceFile)
      MessageRequester("Information", "File '" + sFileName + "' converted")
    EndIf
    
  Else
    MessageRequester("Warning", "Can't open '" + sFileName + "' !", #PB_MessageRequester_Warning)
  EndIf
La conversion fonctionne bien mais la ligne "iBytes = WriteData(#SourceFile, *MemoryID, iFileLength)" répond 0 (erreur, aucun octet écrit dans le fichier) sans que je sache pourquoi. J'avais, dans un premier temps, désactivé le "FileSeek(#SourceFile, 0)" qui précède pour voir si le fichier doublait de volume (append) ce qui ne fut pas le cas : fichier inchangé. Mais même avec ce FileSeek, mon fichier n'est pas mis à jour.

Pourquoi ?

EDIT !!! Oups, juste après avoir posté, je me suis rendu compte que j'avais ouvert le fichier par "ReadFile" et non pas "OpenFile". Après correction, ça fonctionne comme prévu (y compris le comportement de FileSeek(#SourceFile, 0) qui "ajoute" au lieu de "remplacer" s'il est omis)

L'étape suivante est d'intégrer la commande CLI "cmd.exe /c dir "%1" /A /S /C > "_Listing.txt"" pour que les deux opération s’enchaînent : mon programme déclenche "cmd.exe /c dir "%1" /A /S /C > "_Listing.txt"" puis convertit le fichier.

Pour cela, il faut que je lise l'aide de PureBasic. Quel chapitre me conseilleriez-vous ? WriteConsoleData() ?
Marc56
Messages : 2148
Inscription : sam. 08/févr./2014 15:19

Re: Lookup table et fichier texte

Message par Marc56 »

Sous W7x64, je désire pouvoir lister mes répertoires et sous-répertoires dans un fichier texte. J'ai trouvé une solution sur internet : ajouter une entrée "Chaîne" dans la base de registres contenant cmd.exe /c dir "%1" /A /S /C > "_Listing.txt".

Mais le listing obtenu est rempli de caractère étranges : il est encodé en ASCII Code Page 1252 et affiché (par Notepad et autres) en Code Page 480.
Il y a plus simple: lister directement en Utf-8 en changeant le codepage de la console

Pour cela, dans la console, tu tapes

Code : Tout sélectionner

C:\>chcp 65001
Page de codes active : 65001
Maintenant la console est en Utf-8
(chcp seul indique le code actuel)

ensuite tu fais ta commande dir, ex:

Code : Tout sélectionner

dir /s > dir.txt
Ton fichier dir.txt sera en Utf-8 avec tous les bons caractères
Demivec
Messages : 90
Inscription : sam. 18/sept./2010 18:13

Re: Lookup table et fichier texte

Message par Demivec »

JagV12 a écrit :Pour cela, il faut que je lise l'aide de PureBasic. Quel chapitre me conseilleriez-vous ? WriteConsoleData() ?
Process, RunProgram()
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Re: Lookup table et fichier texte

Message par JagV12 »

@Demivec : je vais regarder ça, merci

@Marc56 : Lorsque j'ouvre une console (et même en admin) et que j'entre chcp, la réponse est 850 (en non pas 480 comme je l'ai mis par erreur en haut de page). Si j'entre chcp 65001 et que je redemande chcp, je n'ai pas de réponse, seulement le prompt. Si je quitte et ouvre une nouvelle console, je suis à nouveau (ou toujours) en CP850. D'ailleurs, le réponse à la commande chcp xxx apparaît pour 850 (Page de codes active : 850) mais pas pour 65001. Je n'ose pas tester d'autres valeurs sans conseil.

Une suggestion ?
Marc56
Messages : 2148
Inscription : sam. 08/févr./2014 15:19

Re: Lookup table et fichier texte

Message par Marc56 »

Ah, oui, Windows 7
Essayes chcp 1252 ce sera déjà mieux que 850
(mais pourquoi Windows continu-t-il à ouvrir la console sur 850 alors que les programmes 16 bits qui utilisaient ce code page (DOS) ne fonctionnent plus maintenant ?)
Oui, il est normal qu'à chaque ouverture, cela reviennent sur le codepage par défaut.
Cela dit tu peux faire un batch ou passer plusieurs commandes en une seule ligne (séparées par &)
chcp 1252 & dir /s > listing.txt
je désire pouvoir lister mes répertoires et sous-répertoires dans un fichier texte
Cela peut se faire aussi directement dans PB à l'aide des fonctions ExamineDirectory() / NextDirectoryEntry() que tu fais tourner mode récursif.
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Re: Lookup table et fichier texte

Message par JagV12 »

@Marc56 : Ça fonctionne dans une console. Et si maintenant je veux le mettre dans ma base de registre (pour l'avoir à portée de clic droit), dois-je faire un batch ou puis-je l'intégrer dans cmd.exe /c dir "%1" /A /S /C > "_Listing.txt" ? Et de quelle manière ? (batch ou BdR)

Je viens d'essayer cmd.exe /c "chcp 1252 & dir "%1" /A /S /C > "_Listing.txt"" sans changement de résultat. Idem avec &&.

Mais avec cmd.exe /k[/b][/b] "chcp 1252 & dir "%1" /A /S /C > "_Listing.txt"", la console reste ouverte et je peux coller dir /A /S /C > "_Listing.txt" qui fonctionnera correctement (donc en CP1252)

Au passage, cmd.exe /c "chcp 1252 & dir "%1" /A /S /C >" "_Listing.txt" (notez la position des guillemets) crée un fichier CP850 nommé " _Listing.txt" (le nom est précédé d'un espace qu'on ne peut plus enlever, sauf avec un nom intermédiaire comme "a.txt")

Bref, l'option & ou && serait pratique si je savais comment l'utiliser...
J'ai bien fait chcp 1252 & cmd /? >cmd_help.txt (qui fonctionne; dans une console ouverte) mais sans trouver de solution. Il se pourrait que le caractère > pose problème... ainsi que &, ce qui est absurde...

Un avis ?

Rappel :
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Folder\shell\Listing]
[HKEY_CLASSES_ROOT\Folder\shell\Listing\command]
@="cmd.exe /c dir "%1" /A /S /C > "_Listing.txt""



Edit : Pour info, je viens de créer un fichier "C:\Windows\listing.bat"contenant

Code : Tout sélectionner

chcp 1252
dir "%1" /A /S /C > "_Listing.txt"
avec ma clé de registre pointant dessus et ça fonctionne donc (c'est presque décevant...) mais si vous savez quelle commande j'aurais dû mettre dans ma clé pour ne pas passer par un batch, ça m'intéresse... Enfin, ma version via PureBasic est finalement aussi indirecte qu'un batch...
Dernière modification par JagV12 le mar. 10/mars/2020 0:11, modifié 1 fois.
Marc56
Messages : 2148
Inscription : sam. 08/févr./2014 15:19

Re: Lookup table et fichier texte

Message par Marc56 »

Le cmd.exe de Windows 10 a un paramètre /U pour effectuer une sortie unicode

Je n'ai pas de Windows 7 pour tester, mais tapes CMD /? pour avoir l'aide.

:wink:
Avatar de l’utilisateur
cage
Messages : 506
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Lookup table et fichier texte

Message par cage »

Bonjour,
Voici une solution possible si le répertoire a lister n'est pas trop volumineux.
Compiler ce code en getdir.exe au format Console et non Windows
Ce code utilise la commande clip disponible depuis Vista dans Windows
Utilisation : getdir.exe "Dossier a lister" ou getdir.exe "Dossier a lister" > "Fichier texte"
Dans la base de registre, remplacer cmd.exe /c dir "%1" /A /S /C > "_Listing.txt"
par
getdir.exe "%1" > "_Listing.txt"
Copier getdir.exe dans C:\Windows\System32
cage

Code : Tout sélectionner

;
;************************************************************************
;Titre      : getdir.pb
;Auteur     : (C) 2020 CAGE
;Date       : 2020/03/09
;Version PB : PureBasic 5.71 LTS (Windows - x86)
;Version PB : PureBasic 5.71 LTS (Windows - x64)
;Compiler Options
;Compiler Options : Executable format: Console
;Compile/Run      : Create temporary executable in the source directory
;
;Libairies: aucunes
;
;************************************************************************
;

EnableExplicit

OnErrorGoto(?ErrorHandler)

Define count, listing$, mydir$

count = CountProgramParameters()

If count=0 : End #False : EndIf

mydir$ = ProgramParameter(0) ;: Debug mydir$

ClearClipboard()

RunProgram("CMD.EXE", "/C DIR " + mydir$ + " /A /S /C 2>&1 | CLIP", "", #PB_Program_Wait | #PB_Program_Hide)

listing$ = GetClipboardText() ;: Debug listing$

If listing$ <> #Null$
  If OpenConsole()
    Print(listing$)
    CloseConsole()
    ClearClipboard()
    End #True
  EndIf
EndIf

ErrorHandler:

End
■ Win10 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.00 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
Avatar de l’utilisateur
Naheulf
Messages : 191
Inscription : dim. 10/mars/2013 22:22
Localisation : France

Re: Lookup table et fichier texte

Message par Naheulf »

Heuu... cage, c'est quoi l'intérêt de ta solution ? Si c'est pour faire appel à la comande "dir" de windows autant passer par un fichier batch non ?
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Re: Lookup table et fichier texte

Message par JagV12 »

@ Naheulf : finalement, Cage répond à ma question un peu plus haut
L'étape suivante est d'intégrer la commande CLI "cmd.exe /c dir "%1" /A /S /C > "_Listing.txt"" pour que les deux opération s’enchaînent : mon programme déclenche "cmd.exe /c dir "%1" /A /S /C > "_Listing.txt"" puis convertit le fichier.

Pour cela, il faut que je lise l'aide de PureBasic. Quel chapitre me conseilleriez-vous ? WriteConsoleData() ?
mais effectivement, un batch est beaucoup plus simple.

@Marc56 : j'ai bien l'option /u citée dans mon fichier d'aide
CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
[[/S] [/C | /K] chaîne]

/C Exécute la commande donnée par la chaîne de caractères puis se termine.
/K Exécute la commande donnée par la chaîne de caractères et reste actif.
...
/A Redirige la sortie de commandes internes vers un canal ou un fichier
ANSI.
/U Redirige la sortie de commandes internes vers un canal ou un fichier
UNICODE.
mais je ne sais pas comment l'utiliser :

Code : Tout sélectionner

cmd.exe /k /u dir "%1" /A /S /C >"_Listing.txt"
affiche '/u' n'est pas reconnu en tant que commande interne ou externe, un programme exécutable ou un fichier de commandes.
Mais cette question concerne peut-être plus un forum Windows... EDIT : À ce propos, en avez-vous un à conseiller (ou à déconseiller) ?
Avatar de l’utilisateur
cage
Messages : 506
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Lookup table et fichier texte

Message par cage »

@Naheulf
Heuu... cage, c'est quoi l'intérêt de ta solution ? Si c'est pour faire appel à la comande "dir" de windows autant passer par un fichier batch non ?
C'est du PB pur et dur, mais de plus, l'utilisation de clip ("C:\Windows\System32\clip.exe") formate directement la sortie correctement dans le bon "code page".

@JagV12
Fais un tour sur mon site http://pbcage.free.fr et notamment la page http://pbcage.free.fr/pb/appconsole.html
En fin de page, tu trouveras 2 fichiers a télécharger, un chm et un pdf
Ces 2 fichiers listent toutes les commandes DOS avec leurs paramètres.
Je pense que c'est un bon début.
Pour trouver de bons sites, je te conseille https://www.google.fr
Personnellement, j'en fais un usage intensif.

cage
■ Win10 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.00 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
Avatar de l’utilisateur
cage
Messages : 506
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Lookup table et fichier texte

Message par cage »

Au final ,la solution est la suivante:

CMD.EXE /U /C DIR "C:\" /A /S /C > "Y:\"_Listing.txt"

CMD.EXE /A donne une sortie au format ansi
CMD.EXE /U donne une sortie au format unicode

/U ou /A doit être le premier paramètre de CMD

Lors de l'ouverture d'une fenêtre cmd, elle est toujours au format ansi
Pour la passer au format unicode, tapez cmd /u dans cette fenêtre.

Code : Tout sélectionner

Microsoft Windows [version 10.0.18363.693]
(c) 2019 Microsoft Corporation. Tous droits réservés.

EBCAGE::ADMIN C:\Users\admin>cmd /u
Microsoft Windows [version 10.0.18363.693]
(c) 2019 Microsoft Corporation. Tous droits réservés.

EBCAGE::ADMIN C:\Users\admin>cmd /a
Microsoft Windows [version 10.0.18363.693]
(c) 2019 Microsoft Corporation. Tous droits réservés.

EBCAGE::ADMIN C:\Users\admin>exit

EBCAGE::ADMIN C:\Users\admin>exit

EBCAGE::ADMIN C:\Users\admin>
cage
■ Win10 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.00 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
JagV12
Messages : 20
Inscription : sam. 06/janv./2018 11:39

Re: Lookup table et fichier texte

Message par JagV12 »

@ cage (je le prononce à l'anglaise, est-ce à cause de l'acteur ou parce qu'en français, c'est enfermant ... ?) : merci pour ce prêt-à-consommer, lire des page de docs pour me rendre compte que j'ai inversé deux paramètres aurait été utile si j'avais voulu approfondir DOS (-like) mais comme ce n'est pas le cas ici, autant profiter des compétences des autres.

Restent deux questions :
  • le fichier généré par CMD.EXE /u /c DIR "%1" /A /S /C >"_Listing.txt" s'affiche correctement dans Notepad mais illisible dans Chrome (très pratique pour ce qui est des recherches d’occurrences). Si, dans Notepad, je le sauvegarde sur lui-même en format ANSI (dommage qu'ìl n'y ait pas de raccourci (comme F12 pour UltraEdit) pour ça, sauf erreur), il réduit de taille et devient Chrome compatible. Est-automatisable (encodage UNICODE -> ANSI-ASCII) ?
  • La précédente solution (cmd.exe /c "chcp 1252 & dir "%1" /A /S /C > "_Listing.txt"") ne fonctionne pas (l'encodage reste CP850) sauf si je la décompose dans un batch. Savez-vous pourquoi et quel serait le remède ?
Et une troisième : à l'origine, cette clé de registre permet, par clic droit sur un dossier, d'afficher une entrée Listing au menu contextuel. Si c'est la clé de registre qui exécute CMD.EXE /c DIR "%1" /A /S /C >"_Listing.txt", c'est bien le dossier cliqué qui est listé. Mais si elle renvoie sur un batch (contenant DIR "%1" /A /S /C >"_Listing.txt), c'est le dossier actuel (son parent, donc) qui est listé. Là encore, pourquoi ?

J'espère ne pas avoir été trop bavard ni posé trop de questions...
Répondre