[C++] lecture de caractères / isoler des mots

the_moon -  
mamiemando Messages postés 33778 Date d'inscription   Statut Modérateur Dernière intervention   -
Salut à tous !

Je suis étudiant en IUT, et après un projet en script Bash et un en Java, je dois maintenant réaliser un projet en C++.

J'aurai donc quelques questions à poser afin d'avancer dans sa réalisation, sur une étape qui me bloque:

mon programme doit séparer les mots contenus dans une ligne d'un fichier texte; cette ligne est obtenue avec la commande "getline" (pas de problème pour ça, ça fonctionne)

Ce que je voudrais faire, c'est arriver à isoler les mots contenus dans cette ligne, sachant qu'elle peut contenir des symboles, et qu'il faudra en tenir compte (certains ne doivent pas être récupérés, d'autres si)

Par exemple, la ligne peut être une ligne de code en C++, dans ce cas je voudrais récuperer tous les mots qu'elle contient, mais pas les symboles propres au langage (comme
= + . ;
etc...).
Le mot peut aussi être un nom de variable et contenir d'autres symboles comme
_



J'ai comme idée de lire la ligne caractère par caractère, et quand je trouve une lettre, je regarde le caractère suivant et ainsi de suite tant que je trouve une lettre ou un symbole accepté.
Je ne m'arrète que quand je tombe sur un caractère ne faisant pas partie d'un mot (espace ou caractère propre au langage). Je veux ensuite récuperer le mot trouvé pour pouvoir le traiter et le stocker.

Auriez vous une idée sur la manière de réaliser la lecture de cette ligne caractère par caractère, en séparant les mots ? Quelles commandes dois-je utiliser ??


Voici un exemple sur un morceau de code C++:

  if (compteur != NULL)
        return true;
    else
        return false;


la commande
getline
utilisée avec
while
me renvoie les lignes 1 par 1, je voudrais ensuite que ça récupère les mots:
if compteur NULL pour la 1ère ligne
return true pour la 2nde
else pour la 3ème
return false pour la 4ème.
Les symboles ( ) ! = ; ne doivent pas être relevés, de même que des mots entre guillements (pas d'exemple ici).



Voilà, je suis pour le moment bloqué à cette étape du programme, je n'arrive pas à trouver comment réaliser cette fonction de lecture, et je ne peux donc pas passer à la suite.

En espérant que quelqu'un parviendra à trouver une solution qui fonctionne, merci beaucoup !
A voir également:

16 réponses

mamiemando Messages postés 33778 Date d'inscription   Statut Modérateur Dernière intervention   7 884
 
getline marche très bien c'est ce que j'utilise ;o)

1) Méthode C++ :
a) Utiliser les flux sur les fichiers :

#include <fstream>
...
//ouvrir le fichier
ifstream fichier;
fichier.open("tonfichier", ios::in);
if(fichier.bad()){
     cerr<<"Fichier ["<<filename<<"] invalide."<<endl;
     exit(-1);
}
//Lire un entier, une chaine un double sur la ligne
int i, string s, double d;
fichier>>i>>s>>d;

fichier.close();
...

b) Utiliser getline + les outils fournis par <string>

2)Méthode C :
1)Utiliser fscanf (pour un fichier)


%i %d un entier
%lf un flottant ou un double
%s une chaine
\n retour chariot
\t tabulation

2) sscanf (pour une chaine) combiné à un getline()...

Exemple :
...
int entier, string chaine, double dbl;
getline(fichier,ligne);
if (sscanf(ligne.c_str(),"%i %s %lf",&entier,&chaine,&dbl)==3)  
{
//ok
...
}else{
//pb !
...
}
...
6
mamiemando Messages postés 33778 Date d'inscription   Statut Modérateur Dernière intervention   7 884
 
Tu as la méthode length() pour mesurer la longueur d'un string et la fonction strlen (string.h) si ton tableau de char contient une chaîne terminée par un \0. Je trouve ta méthode un peu louche que veux tu faire exactement ?
2
Char Snipeur Messages postés 9813 Date d'inscription   Statut Contributeur Dernière intervention   1 299
 
Salut !
à ta place deja j'utiliserai l'en tete <string> qui te pêrmet de faire des recherche dans la chaine de caractère, et plein d'autre fonctions deja incorporer.
Ensuite, pourquoi utilisé getline? cin>>string1 te permettrai de récupérer les mots dejà en partie séparé.
1
Nashouille
 
A priori si tu veux un tableau dont la taille varie il serait plus simple d'utiliser un vecteur ( vector<string> ). Tu le remplit au fur à et à mesure et à la fin il sera de la taille du nombre d'éléments que tu as insérer
1

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Z3uS-Su3Z Messages postés 94 Date d'inscription   Statut Membre Dernière intervention   12
 
Il suffit que quand tu crée ton tableau, tu mette un caractère déterminé à la fin genre : "\0"

Comme ca d'une part tu peux utiliser les methodes déjà énoncées ci-dessus et d'autre part tu peux faire la tienne, il te suffira d'incrémenter une variable jusqu'à ce que tu tombe sur ton caractère de fin, si tu prèfere un autre caractère que \0
1
mamiemando Messages postés 33778 Date d'inscription   Statut Modérateur Dernière intervention   7 884
 
Pour finir de reprendre ce que tu disais il n'y a pas de mystère.
- Soit quand tu génères tes char * tu dois placer le \0. Si par exemple s fait 100 char, il faut rajouter :
s[100] = '\0';

Après quoi tu peux faire un strcpy.

- Soit une variable n stocke cette longueur de chaîne et lors de ta recopie tu peux utiliser strncpy

http://www.linux-kheops.com/doc/man/manfr/man-html-0.9/man3/strcpy.3.html

Bonne chance
1
the_moon
 
PS: désolé pour la forme bizarre de mon message, je ne pensais pas que le fait de mettre les balises "code" forçait le passage à la ligne !
0
the_moon
 
Merci beaucoup !
J'ai reussi à faire ce que je voulais, mais en utilisant une methode assez compliquée, qui necessite plusieurs fichiers temporaires pour obtenir ce que je veux... Je vais donc tenter la "methode C++" qui m'a l'air plus simple !
Quant à la methode cin>>string1, il me semble que ça sert pour lire une chaine de caractères tapée par l'utilisateur, non ?


J'ai une autre petite question qui est une variante simplifiée de la première:
je voudrais récuperer 1 par 1 tous les mots d'un fichier (sans distinguer les éventuels caractères spéciaux), dans une variable pour pouvoir faire un traitement sur chacun de ces mots, avant de les stocker ailleurs. Ces mots sont dans tous les cas séparés par un ou plusieurs espaces.

exemple: je récupère le 1er mot du fichier XXX.txt dans une variable de type string, je fais mon traitement dessus puis je l'enregistre ailleurs. Jusque là pas de problème. Je voudrais ensuite créer une boucle qui recommence les mêmes actions pour le 2ème mot, puis le 3ème, et ainsi de suite jusqu'à la fin du fichier.

Quelle commande dois-je utiliser pour la lecture mot par mot jusqu'à la fin du fichier ?
Puisqu'il existe un moyen de lire un fichier ligne par ligne et caractère par caractère, j'imagine qu'il peut y avoir une methode "mot par mot" aussi mais je n'arrive pas à la trouver !

En tout cas, merci pour vos réponses, je vais tester tout ça dès demain matin !
0
Char Snipeur Messages postés 9813 Date d'inscription   Statut Contributeur Dernière intervention   1 299
 
Salut.
En effet c'est possible. C'est la qu'intervient ma méthode.
en effet, je n'avais pas compris ce que tu voulais faire cin c'est bien le flux standard. mais il fonctionne tout comme le flux de fichier.
Donc, comme cin>>string récupére mot par mot
fc>>string récupére mot par mot. fc est le flux ouvert sur ton fichier.
si il ne recupere pas mot par mot, essai de changer les flags.
http://www.cppreference.com/io_flags.html#format_flags
si tu veux en savoir plus sur les flags
0
the_moon
 
Merci !
pour le cin, ça me paraissait bizarre, mais je comprends mieux avec fc>>string qui apparement lit mot par mot le fichier ouvert.
Je vais tester ce que ça donne.

Puisque j'ai la réponse à toutes mes questions (actuelles, j'en aurai peut-être d'autres plus tard), il ne me reste plus qu'à appliquer tout ça à mon projet !

Encore une fois, merci beaucoup !
0
the_moon
 
UP !

J'ai une nouvelle question pour mon projet :

J'ai besoin de trouver une fonction qui me permette de compter le nombre de caractères stockés dans un tableau de caractères.

Je n'ai pas de problèmes pour stocker des caractères dans un tableau
char s [200];

mais lorsque je veux récuperer le contenu de ce tableau pour l'écrire dans un nouveau fichier texte, je récupère mon texte suivi de caractères incompréhensibles. Or je n'ai aucun moyen de différencier ces caractères incompréhensibles du texte à conserver, donc je voudrais pouvoir arrèter la lecture à la fin de la chaine voulue !

pour remplir le tableau, j'utilise:

string fichier = "fichier.txt";
ofstream ofichier(fichier.c_str());

for(int i = 0 ;i<200;i++)
{
     ofichier.put(s[i]);
}
ofichier.close();


Dans la boucle "for", j'ai mis 200 car c'est le nombre maximal de caractères que peut contenir le tableau s[]. Mais dans le fichier de sortie, je récupère systématiquement des caractères incompréhensibles à la suite de ce que je veux conserver.

Est-ce qu'il existe une commende du genre "size" ou "getsize" pour un tableau ? Ou comment puis-je arrèter la lecture de ce tableau à l'endroit voulu pour que mon fichier final ne contienne que le texte désiré ?

Merci beaucoup pour votre aide !
0
Char Snipeur Messages postés 9813 Date d'inscription   Statut Contributeur Dernière intervention   1 299
 
il existe des fonctions pour savoir la taille d'un tableau, mais dans ton cas, ça retournera 200.
En revanche, essai :
ofichier<<string(s);
à la place du put.
sinon, avant le put tu peux essayer
if (s[i]=='\0') break;
la question que je me pose, c'est : est-ce que ton programme fini bien le stockage de la chaine de caractère dans s par le caractère nul '\0'?
0
Char Snipeur Messages postés 9813 Date d'inscription   Statut Contributeur Dernière intervention   1 299
 
pas besoin de passer par vector, string suffit tout à fait.
Il est même beaucoup mieu, tu ajouter suprimer des elements du debut, de la fin, ou même du milieu.
0
the_moon
 
Merci pour toutes ces réponses ! Et désolé de répondre si tard, mais j'ai été obligé de déplacer mon PC pour bosser, donc je n'avais plus internet pendant ce temps !


"En revanche, essai :
ofichier<<string(s);
à la place du put.
sinon, avant le put tu peux essayer
if (s[i]=='\0') break;
la question que je me pose, c'est : est-ce que ton programme fini
bien le stockage de la chaine de caractère dans s par le caractère
nul '\0'? "


Alors, mon gros problème est que le stockage s'arrète avant le caractère "\0", cette methode ne marche pas !
Je vais tester le "ofichier<<string(s)", mais ça me parait bizarre vu que "s" est un tableau de char.


"Tu as la méthode length() pour mesurer la longueur d'un string
et la fonction strlen (string.h) si ton tableau de char contient une chaîne
terminée par un \0. Je trouve ta méthode un peu louche que veux
tu faire exactement ?"


J'avais trouvé cette methode "lenght()" pour les string, mais je suis obligé de conserver mon tableau de char. Et dans ce cas ça ne fonctionne pas ! (le tableau de char ne se termine malheureusement pas par "\0")

En fait, ce que je dois faire, c'est traiter un fichier contenant du texte et des symboles divers (ce fichier peut être un *.cpp) dont l'utilisateur du programme entre le nom au début de l'execution.
Mon programme ne doit garder que certains mots (j'ai déja fait toutes les conditions pour ne garder que ce qui m'interresse), et les placer dans un arbre, chaque noeud étant composé d'un mot et d'une liste contenant les numéros de lignes du fichier initial où l'on retrouve ce mot.
Donc pour utiliser certaines methodes, je suis obligé de conserver ce tableau de char qui me pose tant de problèmes !

Pour la création de l'arbre, ou même la comparaison de chacun des mots trouvés avec les mots d'un autre arbre, je n'ai aucun problème, par contre je poserai surement bientôt des questions sur la manière de stocker 2 éléments dans un même noeud (comment réaliser la classe adéquate). Mais avant ça, je vais essayer de m'y pencher un peu plus !


"A priori si tu veux un tableau dont la taille varie il serait plus simple
d'utiliser un vecteur ( vector<string> ). Tu le remplit au fur à et à mesure
et à la fin il sera de la taille du nombre d'éléments que tu as insérer"


C'est aussi une possibilité que j'ai envisagé, mais les methodes que j'emploie necessitent un tableau de char, que je dois redécouper lettre par lettre pour réaliser tous les traitements.


"Il suffit que quand tu crée ton tableau, tu mette un caractère
déterminé à la fin genre : "\0"
Comme ca d'une part tu peux utiliser les methodes déjà énoncées
ci-dessus et d'autre part tu peux faire la tienne, il te suffira d'incrémenter
une variable jusqu'à ce que tu tombe sur ton caractère de fin, si tu prèfere
un autre caractère que \0"


Pour le moment, je n'ai pas réussi à faire ça. Je n'arrive pas à placer "\0" à la fin, et tous les autres symboles sont traités par mon programme (je vire tout ce que je ne veux pas conserver dans l'arbre), donc je ne peux pas mettre autre chose pour indiquer la fin de la chaine.

Ce que j'ai par contre réussi à faire, c'est complèter toutes les cases du tableau avec des espaces, que je saute quand je réécris ce tableau dans un fichier temporaire (fichier mis en forme que je relis pour créer l'arbre). Cette methode n'est pas extraordinaire, mais j'ai réussi à la faire fonctionner ce soir.

Merci beaucoup pour toutes vos réponses, je vais tester tout ce que vous m'avez proposé, j'espère arriver à faire marcher une solution meilleure que celle que j'utilise actuellement !
0
Char Snipeur Messages postés 9813 Date d'inscription   Statut Contributeur Dernière intervention   1 299
 
Salut.
Je n'ai pas bien compris pourquoi il te faux absolument un tableau de char et pas un string, mais bon...
En revanche, j'ai peux être une solution (pas tester)
tu fait :
char s[200];
for (int i=0;i<200;i++)
    s[i]='\0';
En initialisant ainsi le tableaux, tu aura toujours le\0 à la fin de ta chaine, et la les méthodes habituel devraient fonctionner
0
mamiemando Messages postés 33778 Date d'inscription   Statut Modérateur Dernière intervention   7 884
 
Comme ça, si sa chaîne fait 100 caractères et que les 100 suivants sont initialisés à n'importe quoi, il a sa chaîne suivie de 100 caractères parasites...
0
boulanoirpr
 
si j'ai bien compris tu veux juste analysé une chaine de textes

la meilleur façons de le faire est de déterminer la position du caractère à chercher (-, ., *, ....)
puis d'utiliser la fonction substr qui permet de prendre une copie d'un fragment d'une chaine et le mettre dans une autre chaine.

CH1="Bonjour Mr :Samir"
Positon=12 // c'est la position de : facile à determiner par une boucle.
CH2=CH1.substr(Position+ 1, strlen(CH1));// Copie le segment de caractère depuis Position jusqu'à la fin de la chaine
donc CH2 contient Samir



c'est ca marche Tu me fais signe: Mr boulanoir Hamma
0
mamiemando Messages postés 33778 Date d'inscription   Statut Modérateur Dernière intervention   7 884
 
Non ce n'est certainement pas la meilleure méthode car la chaîne est parcourue 3 fois alors que c'est inutile :
- une fois pour calculer strlen,
- une fois pour calculer substr,
- une fois pour recopier la chaine CH1 dans CH2.

Surtout que si CH1 et CH2 sont deux std::string, il y a directement un opérateur =. Et si ce sont des char * il y a la fonction strcpy, et avec ces deux approches la chaîne n'est parcourue qu'une seule fois.

Enfin dans le code que tu proposes il y a un mélange entre CH1 qui est tantôt de type char * (pour strlen) et de type std::string (pour le substr).
0