Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++/CLI Discussion :

Lecture de fichier ASCII / UNICODE. DEBUTANT


Sujet :

C++/CLI

  1. ###raw>post.musername###
    Membre à l'essai
    Lecture de fichier ASCII / UNICODE. DEBUTANT
    Bonjour à tous,
    C'est la première fois que je viens poster sur ce forum. Je débute en c++, même si j'ai longtemps 'bricoler' en C. Je bloque aujourd'hui sur un problème extrêmement bête que je ne comprend pas.
    Voici mon code :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
    #include <string>  
    #include <fstream>
    using namespace std;
    int main()
    {
    	string line;
    	fstream text("monfichier.txt");
    	while (getline(text,line))
    	{
    		cout << line << endl;	
    	}
    return 0;
    }

    Il n'y a, pour la plupart des fichiers que je traite, aucun problème, par contre j'ai pu identifier, sur des fichiers registre (*.reg) un problème lors de la sortie de ma chaine.
    En effet, celle ci contient des espaces entre chaque caractère, c'est très pénible à traiter.
    Quand j'affiche la variable çà écrit par exemple ce genre de chose :
    [ H K E Y _ C U R R E N T _ C O N F I G \ \ S y s t e m \ C u r r e n t C o n t r o l S e t \ C o n t r o l \ V I D E O ]
    alors que mon fichier d'origine contient bien :
    [HKEY_CURRENT_CONFIG\\System\CurrentControlSet\Control\VIDEO]
    Et du coup impossible de , par exemple, réinjecter les chaine obtenu dans un nouveau fichier.
    J'ai testé avec des .reg fabriqué manuellement il n'y a aucun probléme.
    Les fichiers qui présente des problèmes sont fabriqué à partir de la commande regedit /e.
    Ce que je ne comprend absolument pas c'est pourquoi cette différence de résultat alors que les fichiers sont traités de la même manière?
    J'ai clairement loupé un truc essentiel mais quoi?
    Si quelqu'un veux bien m'éclairer?
    Merci d'avance
      0  0

  2. #2
    Expert éminent sénior
    Ce ne sont pas des espaces, mais des octets nuls, je parie. Problème d'encodage, entre UTF-16 et les ASCIIs étendus.

    Je ne connais pas assez la bibliothèque standard du C++ natif (surtout avec les normes récentes) pour savoir s'il y a quelque chose pour gérer ça directement.
    Mon conseil: Puisque tu as posté dans le sous-forum consacré au langage C++/CLI pour le Framework .net, utilise le framework .Net pour de bon, car lui est fait pour ça (un System::IO::StreamReader permet de spécifier l'encodage des fichiers).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Expert éminent sénior
    C'est la première fois que je viens poster sur ce forum
    Et vraisemblablement la dernière dans cette partie du forum.
    Ne le prend pas mal, c'est juste que cette partie du forum est lié à "C++/CLI", une version étendue par M$ du C++.
    Il y a donc beaucoup moins de "flux" ici que dans les parties du forum plus généraliste.
    https://www.developpez.net/forums/f1095/c-cpp/cpp/debuter/

    T'es loin d'être le premier et tu ne seras pas le dernier à faire cette petite erreur.

    J'ai clairement loupé un truc essentiel mais quoi?
    Le fait que distinguer des fichiers en ASCII et en UNICODE, c'est la mer.. .

    Votre code ne fonctionne qu'avec des fichiers textes en ASCII (étendu 8bits).
    Le fichier "*.reg" que vous lisez n'est pas en ASCII, mais en UNICODE-16 (sans BOM (Byte Order Mark)).
    Pour des caractères contenus dans la table des ASCII 7bits, la version UNICODE-16 de ce caractère est composé d'un octet à '\0' suivi du même octet que son encodage en ASCII.
    'H' en ASCII : 72 (ou 0x48)
    'H' en UNICODE-16 (en little endian): 00-72 (ou 0x0048)

    La console que vous utilisez doit afficher un '\0' sous forme d'un espace.

    La mer.., c'est que rien n'indique l'encodage d'un fichier.
    Je crois que l'encodage officiel des .reg est UNICODE-16 (ou UTF-8 ?).
    Il est possible que les interpréteurs de ".reg" utilisent des heuristiques pour détecter un mauvais encodage pour encaisser des fichiers "mals" encodés.

    C'est quoi votre objectif ?

    Pour lire directement de UTF-16/UNICODE-16 :
    http://www.cplusplus.com/reference/fstream/wfstream/

  4. #4
    Membre à l'essai
    Bonjour et merci pour vos réponses rapides.
    En effet, je suis pas du tout dans la bonne partie du forum et j'en suis désolé (je découvre ) ).
    Si un modérateur veux bien déplacer le sujet je le remercie d'avance.
    Effectivement quand j’écris ma chaine dans un fichier et que je visionne celui-ci je n'ai pas d'espace mais des petit logos 'NULL'.
    Donc clairement un problème ASCII vs UNICODE. J'avoue que je n'avais jamais eu ce problème en C donc j’étais un peu perplexe.
    Je cherche une solution de ce coté la et je viens poster la solution ici, au cas ou quelqu'un rencontre le même problème.
    Mon objectif est de pouvoir extraire les données registre avant et après l'installation , par exemple, d'un programme, afin de pouvoir 'fabriquer' un point reg permettant de 'bricoler' un déploiement réseau pour les rares logiciels qui ne le permettent pas par défaut. Également pour différencier ou se trouve certaine information dans le registre avant et après modification de certaines configurations etc...
    Bref, c'est un problème auquel je suis parfois confronté et je me bricole mon outil maison.
    Merci de votre regard extérieur qui m'a permis d'avancer , un peu plus, dans cette direction.

  5. #5
    Membre à l'essai
    A première vue je suis loin d’être le seul à avoir eu ce genre de soucis.
    Je pense qu'il y a une solution viable de ce coté la
    https://msdn.microsoft.com/fr-fr/library/system.io.streamreader(v=vs.110).aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-2
    (Je poste aussi ce lient pour mémoire à moi même).
    Par contre j'ai énormément de mal avec la syntaxe et je n'y connais rien dans l'implémentation (arrêtè moi si le mot n'est pas bon) des framework en c++.
    J'ai copié/collé le code en exemple tel quel sans aucun autre résultat qu'une longue série d'erreur.
    Je continu de chercher/fouillé et je viendrais de toutes façons posté ma réponse ici.

  6. #6
    Expert éminent sénior
    J'avoue que je n'avais jamais eu ce problème en C donc j’étais un peu perplexe.
    Le problème n'a rien à voir avec le langage.
    Vous aurez le même problème en C qu'en C++.

    Je pense que vous n'aviez pas conscience du problème car UNICODE c'est répandu qu'à la fin des années 1990 dans les systèmes "desktop" et je pense qu'il a pris du temps pour arriver dans les systèmes embarqués.
    Donc si vous faisiez du C dans un périmètre (temporel / plateforme) où l'UNICODE n'était pas répandu, vous n'étiez pas tombé dessus, mais c'est pas "grâce" au C.

    Je ne comprend pas trop pourquoi la version "wide" des manipulations de stream dont j'ai posté le lien (http://www.cplusplus.com/reference/fstream/wfstream/) n'est pas une solution viable ?

    Avec cette version, vous ne manipulez que de l'UNICODE, donc vous êtes d'équerre avec le format des ".reg" en UTF-16.

    Vos besoins sont assez classique, me semble-t-il, c'est pourquoi j'ai l'impression qu'il serait peut-être possible de faire ces choses juste avec des outils pré-existant, non ?
    Peut-être avec des fichiers ".bat" de quelques lignes ?

    Je pense qu'il y a une solution viable de ce coté la
    Ce que vous regardez, ce n'est pas du C++ "standard", c'est du code en "C++/CLI" (oui, celui de ce forum ).
    Pour pouvoir se servir de ces trucs, il faut être dans un contexte d'exécution .NET.
    Là, je pense que c'est un peu overkill et un peu dangereux pour une personne sans un background en C++ ou dans un langage .NET.
    En plus, avec ces API, il faut aussi spécifier l'encodage du fichier que l'on cherche à lire.

    Donc, à moins d'avoir oublier un détail, je pense que l'approche "wfstream" sera bien plus simple.

  7. #7
    Expert éminent
    Citation Envoyé par bacelar Voir le message
    La mer.., c'est que rien n'indique l'encodage d'un fichier.
    Si le BOM (Byte order mark)

    Ensuite si tu parses un fichier .html, il y a les méta-données.


    Citation Envoyé par neokal Voir le message
    Donc clairement un problème ASCII vs UNICODE.
    Pas vraiment, puisque en C, il y a char [*] VS wchar_t



    *: signé ou pas, quoique pour l'UTF8 j'utilise du non signé

  8. #8
    Expert éminent sénior
    Je ne comprend pas trop pourquoi la version "wide" des manipulations de stream dont j'ai posté le lien (http://www.cplusplus.com/reference/f...ream/wfstream/) n'est pas une solution viable ?
    La dernière fois que j'avais essayé un truc de ce genre, il m'a ouvert le fichier en tant qu'ASCII et faisait la conversion (voire un bête cast, je ne suis pas sûr) en wchar_t à la volée...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Expert confirmé
    Convertir un fichier de UTF-16 vers UTF-8 avec la STL, en passant par UTF-32.
    Au cas où mon code soit utile à quelqu'un, voici un programme qui lit un fichier encodé en UTF-16 et le convertit en UTF-8 :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    #include <codecvt>
    #include <fstream>
    #include <iostream>
    #include <locale>
     
    int main(int argc, char** argv)
    {
    	try {
     
    		if(argc < 2) {
    			throw std::runtime_error("Pas assez d'arguments. Il en faut deux :"
    				" l'adresse d'un fichier en entree encode en UTF-16"
    				" et l'adresse d'un fichier en sortie qui sera encode en UTF-8.");
    		}
     
    		const char* const cheminFichierEntreeUTF16 = argv[1];
    		const char* const cheminFichierSortieUTF8  = argv[2];
     
    		std::basic_ifstream<char32_t> fic_entree_utf16(cheminFichierEntreeUTF16, std::ios::binary);
    		if(!fic_entree_utf16)
    			throw std::runtime_error("Impossible d'ouvrir le fichier en entree.");
    		fic_entree_utf16.imbue(std::locale(
    			fic_entree_utf16.getloc(),
    			new std::codecvt_utf16<char32_t, 0x10ffff, std::consume_header>
    		));
     
    		std::basic_ofstream<char32_t> fic_sortie_utf8(cheminFichierSortieUTF8, std::ios::binary);
    		if(!fic_sortie_utf8)
    			throw std::runtime_error("Probleme avec le fichier en sortie.");
    		fic_sortie_utf8.imbue(std::locale(
    			fic_sortie_utf8.getloc(),
    			new std::codecvt_utf8<char32_t, 0x10ffff, std::generate_header>
    		));
     
    		char32_t carac_utf32;
    		while(fic_entree_utf16.good())
    		{
    			fic_entree_utf16.get(carac_utf32);
    			if(fic_entree_utf16.fail() == false)
    			{
    				fic_sortie_utf8 << carac_utf32;
    				if(fic_sortie_utf8.fail())
    					throw std::runtime_error("Probleme lors de l'ecriture du fichier en sortie.");
    			}
    		}
     
    	} catch(const std::exception& e) {
    		std::cerr << e.what();
    		return 1;
    	} catch(...) {
    		std::cerr << "Erreur inconnue.";
    		return 2;
    	}
     
    	return 0;
    }

    Remarques :
    • S'il n'y a pas de caractère BOM dans le fichier en entrée, il faut remplacer std::consume_header par (std::codecvt_mode)0 (qui est déjà l'argument de template par défaut) ou std::little_endian.
    • Si on ne veut pas de caractere BOM dans le fichier en sortie, il faut remplacer std::generate_header par (std::codecvt_mode)0.

  10. #10
    Expert confirmé
    Citation Envoyé par Médinoc Voir le message
    La dernière fois que j'avais essayé un truc de ce genre, il m'a ouvert le fichier en tant qu'ASCII et faisait la conversion (voire un bête cast, je ne suis pas sûr) en wchar_t à la volée...
    C'est normal. En fait, il faut distinguer la suite d'octets du fichier et la suite de caractères de type CharT stockée dans le buffer de type std::basic_filebuf<CharT, Traits> encapsulé par std::basic_ifstream<CharT, Traits> et std::basic_ofstream<CharT, Traits>.
    Dans le cas où CharT n'est pas char (par exemple char32_t dans le code de mon précédent message), il y a une conversion avec un std::codecvt. Pour maîtriser la conversion, il faut alors changer la locale utilisée par le flux.

  11. #11
    Expert éminent sénior
    Ah merci, je me disais bien (avec le recul; ma tentative d'utiliser wifstream remonte à des années) que les locales seraient impliquées, mais je ne me souvenais plus du tout comment.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #12
    Membre à l'essai
    J'ai utilisé cette petite fonction, elle permet d'avoir en retour une chaine string au format utf8 de la chaine string utf16 passé en argument,
    C'est juste une fonction qui fait sauter un caractère sur deux , pas forcément super élégant mais ça m'a bien dépanné.

    Merci en tout cas pour l'aide apportée.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    string utf16_to_utf8(string into)    {
    	string outo;
    	int i=0;
    	bool pendulum = 0;
    	for(i=0;i<into.size();i++)
    	{
    		if (pendulum==1)	
    			outo+=into[i];
    		if (pendulum == 0) pendulum=1;
    			else pendulum = 0;
    	}
    	return outo;
    }	//END utf16utf8

  13. #13
    Expert éminent sénior
    Le nom de ta fonction est archi-faux: Même en supposant que le "un octet sur deux" est systématiquement nul, le résultat n'est pas de l'UTF-8, mais de l'ISO 8859-1.

    Si tu veux faire de la vraie conversion en UTF-8 sans appeler de fonctions non-standard et sans avoir à comprendre comment marchent les locales, il te faudra une fonction de ce genre:
    https://www.developpez.net/forums/d1...e/#post8502203
    (note que la fonction de conversion prend un code point en entrée; cela veut dire que lui passer directement les paires d'octets lus est approprié pour convertir depuis l'UCS-2; pour convertir depuis du vrai UTF-16, il faut un peu plus de travail).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.