REMPLACER LE CIN.GETLINE( )

vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 - 27 janv. 2006 à 18:01
victorcoasne Messages postés 1101 Date d'inscription jeudi 24 avril 2003 Statut Membre Dernière intervention 23 juillet 2023 - 31 janv. 2006 à 22:32
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/35764-remplacer-le-cin-getline

victorcoasne Messages postés 1101 Date d'inscription jeudi 24 avril 2003 Statut Membre Dernière intervention 23 juillet 2023 7
31 janv. 2006 à 22:32
Vecchio56 > J'ai la même solution que toi mais certains sont restés fidèle au NOIR ET BLANC (à croire que la couleur n'a pas été inventé). Mais on peut colorer le texte me direz-vous, donc je peux être plus clair et vous dire que si on fait maintenant des environnement 64bits c'est pas pour retourner sous (Console) DOS !
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
29 janv. 2006 à 21:36
C'est concis et plein de bon sens, surtout que la console pour trouver des clients...
vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 14
29 janv. 2006 à 21:27
Moi j'ai résolu tous ces problèmes en arrêtant de travailler dans la console
victorcoasne Messages postés 1101 Date d'inscription jeudi 24 avril 2003 Statut Membre Dernière intervention 23 juillet 2023 7
29 janv. 2006 à 21:18
Le string est la manière C d'être fénéant !

Ce qui est encore plus fénéant c'est de donner un code qui n'est pas de soi.

Perso, jamais eu de problème avec getline (sauf si je met un cin.getline puis un cin forcement mais tout se résouds en ajoutant un deuxième getline qui sert à rien sauf à éviter le plantage du cin suivant).

Bonne prog,
@++
cs_lacousine Messages postés 58 Date d'inscription mardi 6 janvier 2004 Statut Membre Dernière intervention 13 juillet 2007
29 janv. 2006 à 16:46
Premièrement : http://support.microsoft.com/default.aspx?scid=kb;EN-US;q240015

Deuxièmement : je ne peux rien dire sur le fait que la réallocation de mémoire. Mais voici une explication détaillée : (qui n'est pas de moi) Et que j'aurais du utiliser peut-être...


-----------------------------------------------------------------------

Le langage C++ offre une variété d'outils d'entrée/ sortie sur des flux de données, et utilise les classes std::istream et std::ostream comme abstractions fondamentales pour représenter (respectivement) les flux en entrée et les flux en sortie. Appliquant héritage et polymorphisme, ceci permet de gérer sur un même pied les entrées/ sorties à la console (avec, en particulier, std::cin, une instance de std::istream liée à l'entrée standard?typiquement le clavier?et std::cout, une instance de std::ostream liée à la sortie standard?typiquement l'écran en mode console), sur un lien de communication, sur un fichier ou sur tout conteneur respectueux de certaines règles de base.

ex :
#include <string>
#include <fstream>
#include
// lit chaque mot d'un fichier texte et les affiche
// à la sortie standard, séparés par des tabulations
int main ()
{
std::string Mot;
std::ifstream fichierEntree ("in.txt");
while (fichierEntree >> Mot)
std::cout << Mot << '\t';
}


Pour l'entrée de chaînes de caractères, on aura tendance à utiliser l'opérateur >> tel que défini sur un opérande de gauche de type std::istream& et sur un opérande de droite de type std::string&, profitant ainsi de la grande souplesse du type std::string, dont les instances ont une capacité d'entreposage capable de croître en fonction des besoins (ce qui élimine les problèmes de dépassement de capacité des zones tampons, aussi nommées Buffer Overflow Problems).

L'opérateur >> sur un flux en entrée et une std::string a pour comportement de lire sur le flux jusqu'à la rencontre du premier caractère d'espacement.

Si on désire lire une ligne à la fois, par exemple dans un cas où les espaces sont significatifs, alors ce n'est pas le meilleur outil.

Dans ces circonstances, on a habituellement recours à std::getline().

Il se trouve que std::getline() est une fonction très efficace qui prend en paramètre un std::istream& et un std::string& et qui lit des caractères du flux jusqu'à un délimiteur de fin de chaîne (on peut décider soi-même de lire jusqu'à un caractère particulier qui soit différent de '\n' en insérant le délimiteur désiré comme troisième paramètre de la fonction).

Il est très fréquent que des étudiants manipulant ces deux outils rencontrent des problèmes à l'usage de std::getline(). En effet, dans un programme comme celui-ci, la lecture de la chaîne s va être escamotée.

On peut comprendre ce qui se passe en allant tracer le code de std::getline(), qui est un template exposé dans le fichier d'en-tête <string>, mais voici en gros ce qui se passe :

- lorsqu'on lit le premier entier (Val), l'entier en soi est lu, mais std::cin garde dans son tampon interne le caractère de nouvelle ligne ('\n') ayant servi à compléter l'entrée de données, parce que ce caractère ne fait pas partie de l'entier lu et parce qu'il pourrait être utile lors de la prochaine lecture (std::cin ne sait pas que nous, le '\n', on n'en veut pas);

- par défaut (et c'est ce qu'on veut), std::getline() lit ce qui apparaît dans le tampon du flux en entrée reçu en paramètre (ici: std::cin) jusqu'à ce qu'apparaisse un premier '\n'. Dans notre cas, s'il traîne un tel caractère dans le tampon, c'est moche;

- le truc élégant est de vérifier au besoin s'il y a, au début du tampon du flux en entrée utilisé pour std::getline() (donc au début du tampon de std::cin), un changement de ligne ('\n') ou un retour de chariot ('\r') qui traîne dans le cas où on sait qu'il est possible qu'on ait laissé traîner un tel caractère, et de l'enlever du flux s'il y a lieu.

Je vous propose donc la version ci-dessous, qui (elle) fonctionne bien:

#include <string>
#include
int main ()
{
std::cout << "Entrez un entier...";
int Val;
std::cin >> Val;
std::cout << "Entrez une ligne de texte...";
std::string s;
char c = std::cin.peek ();
if (c '\n' || c '\r')
std::cin.get (c);
// lire une ligne avec std::cin, la mettre dans s
std::getline (std::cin, s);
std::cout << s << std::endl;
}


L'appel à std::cin.peek() retourne le premier caractère au début du flux en entrée. S'il s'agit d'une fin de ligne, alors on le retire du flux. Il est important de vérifier la présence ou non d'un tel caractère, d'ailleurs, car deux cas sont possibles:

- soit l'usager entre un entier, puis presse <return>, puis entre du texte. Dans ce cas, la lecture de l'entier se terminera au caractère de nouvelle ligne, et ce caractère sera encore au début dans le tampon du flux en entrée juste avant l'appel à std::getline(). Dans un tel cas, retirer le caractère de flux sera important;

- soit l'usager entre un entier puis peut-être un espace puis du texte, puis appuie sur <return>. Dans ce cas, le caractère retourné par l'appel à std::cin.peek() sera le premier caractère suivant l'entier dans le tampon du flux, et ce caractère ne sera manifestement pas un changement de ligne, et risque d'être significatif. Dans un tel cas, il faut laisser ce caractère dans le flux pour que std::getline() en prenne possesion.

Et pour les curieux, voici la fonction getline :

template<class _Elem,
class _Traits,
class _Alloc> inline
basic_istream<_Elem, _Traits>& __cdecl getline(
basic_istream<_Elem, _Traits>& _Istr,
basic_string<_Elem, _Traits, _Alloc>& _Str,
const _Elem _Delim)
{ // get characters into string, discard delimiter
typedef basic_istream<_Elem, _Traits> _Myis;
ios_base::iostate _State = ios_base::goodbit;
bool _Changed = false;
const typename _Myis::sentry _Ok(_Istr, true);

if (_Ok)
{ // state okay, extract characters
_TRY_IO_BEGIN
_Str.erase();
const typename _Traits::int_type _Metadelim =
_Traits::to_int_type(_Delim);
typename _Traits::int_type _Meta = _Istr.rdbuf()->sgetc();

for (; ; _Meta = _Istr.rdbuf()->snextc())
if (_Traits::eq_int_type(_Traits::eof(), _Meta))
{ // end of file, quit
_State |= ios_base::eofbit;
break;
}
else if (_Traits::eq_int_type(_Meta, _Metadelim))
{ // got a delimiter, discard it and quit
_Changed = true;
_Istr.rdbuf()->sbumpc();
break;
}
else if (_Str.max_size() <= _Str.size())
{ // string too large, quit
_State |= ios_base::failbit;
break;
}
else
{ // got a character, add it to string
_Str += _Traits::to_char_type(_Meta);
_Changed = true;
}
_CATCH_IO_(_Istr)
}

if (!_Changed)
_State |= ios_base::failbit;
_Istr.setstate(_State);
return (_Istr);
}
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
28 janv. 2006 à 23:19
Il est certain qu'on ne "voit" rien quand on utilise des bidules comme 'string' ou autres trucs de ce genre. Pour autant il faut bien mettre les octets quelque part et c'est bien à coup de 'new' que la classe string obtiendra la place, ce qui est une calamité en performance et en fragmentation de l'espace mémoire du processus.
vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 14
28 janv. 2006 à 21:40
et += ne fait pas réallocation peut être?
cs_lacousine Messages postés 58 Date d'inscription mardi 6 janvier 2004 Statut Membre Dernière intervention 13 juillet 2007
28 janv. 2006 à 21:15
En passant, je trouve ca facultatif de dire que : sLue = sLue + cLu; va surement poser beaucoup de réallocation, car je ne vois aucune place ou la fonction fait un new, puisqu'elle utilise l'opérateur +=.
cs_lacousine Messages postés 58 Date d'inscription mardi 6 janvier 2004 Statut Membre Dernière intervention 13 juillet 2007
28 janv. 2006 à 21:12
vecchio56 : Après avoir programmé et avoir eu des problèmes avec getline() en rapport avec le flux de retour, le professeur nous a pondu ce petit bout de code. Tous comme l'autre d'ailleurs que j'ai affiché. Le but de ses 2 petites fonctions est pour ceux qui vont subir le même sort un jour au l'autre. Et sur le WebChat plusieurs m'ont demandé dans la même semaine de résoudre leur problème... et après avoir essayer les 2 fonctions fournis, ça réglé leur problème. Et j'étais tannée de toujours chercher pour trouver mon code et c'est le pourquoi je les ai affichiées ici, afin que ca soit une aide pour ceux qui en auront besoin.

ps. Je ne suis pas l'auteur des fonctions, donc inutile de me demander pourquoi j'ai fait ca de cette façon. :P
vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 14
27 janv. 2006 à 18:01
Tu oublies un petit détail: sLue = sLue + cLu; va provoque probablement beaucoup de réallocation
getLine n'a pas d'erreur de comportement puisque tu spécifie la taille maximum d'une ligne.
Comme ton autre code, je trouve que c'est un peu léger (tu aurais au moins pu les rassembler)
Rejoignez-nous