Pourquoi est-ce scanf
mauvais?
Le principal problème est qu'il scanf
n'a jamais été conçu pour gérer les entrées des utilisateurs. Il est destiné à être utilisé avec des données formatées "parfaitement". J'ai cité le mot "parfaitement" car ce n'est pas tout à fait vrai. Mais il n'est pas conçu pour analyser des données aussi peu fiables que les entrées utilisateur. Par nature, l'entrée de l'utilisateur n'est pas prévisible. Utilisateurs instructions se méprend, fait des fautes de frappe, accidentellement appuyez sur Entrée avant d' être fait , etc. On peut raisonnablement se demander pourquoi une fonction qui ne doit pas être utilisé pour l' entrée utilisateur lit à partir stdin
. Si vous êtes un utilisateur expérimenté de * nix, l'explication ne vous surprendra pas, mais cela pourrait dérouter les utilisateurs de Windows. Dans les systèmes * nix, il est très courant de créer des programmes qui fonctionnent via la tuyauterie,stdout
stdin
de la seconde. De cette façon, vous pouvez vous assurer que la sortie et l'entrée sont prévisibles. Dans ces circonstances, scanf
fonctionne vraiment bien. Mais lorsque vous travaillez avec une entrée imprévisible, vous risquez toutes sortes de problèmes.
Alors, pourquoi n'y a-t-il pas de fonctions standard faciles à utiliser pour la saisie par l'utilisateur? On ne peut que deviner ici, mais je suppose que les vieux hackers hardcore C pensaient simplement que les fonctions existantes étaient assez bonnes, même si elles sont très maladroites. De plus, lorsque vous regardez des applications de terminal typiques, elles lisent très rarement les entrées des utilisateurs stdin
. Le plus souvent, vous passez toutes les entrées utilisateur comme arguments de ligne de commande. Bien sûr, il existe des exceptions, mais pour la plupart des applications, la saisie par l'utilisateur est une chose très mineure.
Alors que peux-tu faire?
Mon préféré est fgets
en combinaison avec sscanf
. J'ai écrit une fois une réponse à ce sujet, mais je publierai à nouveau le code complet. Voici un exemple avec une vérification et une analyse des erreurs décentes (mais pas parfaites). C'est assez bon pour le débogage.
Remarque
Je n'aime pas particulièrement demander à l'utilisateur de saisir deux choses différentes sur une seule ligne. Je ne fais cela que lorsqu'ils appartiennent les uns aux autres de manière naturelle. Comme par exemple printf("Enter the price in the format <dollars>.<cent>: ")
, puis utilisez sscanf(buffer "%d.%d", &dollar, ¢)
. Je ne ferais jamais quelque chose comme ça printf("Enter height and base of the triangle: ")
. Le point principal de l'utilisation fgets
ci-dessous est d'encapsuler les entrées pour s'assurer qu'une entrée n'affecte pas la suivante.
#define bsize 100
void error_function(const char *buffer, int no_conversions) {
fprintf(stderr, "An error occurred. You entered:\n%s\n", buffer);
fprintf(stderr, "%d successful conversions", no_conversions);
exit(EXIT_FAILURE);
}
char c, buffer[bsize];
int x,y;
float f, g;
int r;
printf("Enter two integers: ");
fflush(stdout); // Make sure that the printf is executed before reading
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) error_function(buffer, r);
// Unless the input buffer was to small we can be sure that stdin is empty
// when we come here.
printf("Enter two floats: ");
fflush(stdout);
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) error_function(buffer, r);
// Reading single characters can be especially tricky if the input buffer
// is not emptied before. But since we're using fgets, we're safe.
printf("Enter a char: ");
fflush(stdout);
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%c", &c)) != 1) error_function(buffer, r);
printf("You entered %d %d %f %c\n", x, y, f, c);
Si vous en faites beaucoup, je pourrais recommander de créer un wrapper qui videra toujours:
int printfflush (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
fflush(stdout);
va_end (arg);
return done;
}```
Faire cela éliminera un problème commun, qui est la nouvelle ligne de fin qui peut perturber l'entrée du nid. Mais il y a un autre problème, qui est si la ligne est plus longue que bsize
. Vous pouvez vérifier cela avec if(buffer[strlen(buffer)-1] != '\n')
. Si vous souhaitez supprimer la nouvelle ligne, vous pouvez le faire avec buffer[strcspn(buffer, "\n")] = 0
.
En général, je vous conseille de ne pas vous attendre à ce que l'utilisateur saisisse une entrée dans un format étrange que vous devez analyser en fonction de différentes variables. Si vous souhaitez affecter les variables height
et width
, ne demandez pas les deux en même temps. Autorisez l'utilisateur à appuyer sur Entrée entre eux. De plus, cette approche est très naturelle dans un sens. Vous n'obtiendrez jamais l'entrée stdin
avant d'avoir appuyé sur Entrée, alors pourquoi ne pas toujours lire toute la ligne? Bien sûr, cela peut toujours entraîner des problèmes si la ligne est plus longue que le tampon. Ai-je pensé à mentionner que l'entrée utilisateur est maladroite en C? :)
Pour éviter des problèmes avec des lignes plus longues que le tampon, vous pouvez utiliser une fonction qui alloue automatiquement un tampon de taille appropriée, vous pouvez utiliser getline()
. L'inconvénient est que vous aurez besoin free
du résultat par la suite.
Intensifier le jeu
Si vous êtes sérieux au sujet de la création de programmes en C avec une entrée utilisateur, je recommanderais de jeter un œil à une bibliothèque comme ncurses
. Parce qu'alors vous voudrez probablement aussi créer des applications avec des graphiques de terminaux. Malheureusement, vous perdrez une partie de la portabilité si vous le faites, mais cela vous donne un bien meilleur contrôle des entrées utilisateur. Par exemple, il vous donne la possibilité de lire une pression de touche instantanément au lieu d'attendre que l'utilisateur appuie sur Entrée.
(r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2
ne détecte pas aussi mauvais le texte non numérique de fin.fgets()
of"1 2 junk"
,if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) {
ne signale rien de mal à l'entrée même s'il a des "ordures".scanf
est destiné à être utilisé avec des données parfaitement formatées Mais même ce n'est pas vrai. Outre le problème avec "junk" comme mentionné par @chux, il y a aussi le fait qu'un format comme"%d %d %d"
est heureux de lire l'entrée à partir d'une, deux ou trois lignes (ou même plus, s'il y a des lignes vides intermédiaires), qu'il n'y a pas Un moyen de forcer (par exemple) une entrée à deux lignes en faisant quelque chose comme"%d\n%d %d"
, etc.scanf
pourrait être approprié pour une entrée de flux formatée , mais ce n'est pas du tout bon pour quoi que ce soit basé sur une ligne.scanf
est génial quand vous savez que votre contribution est toujours bien structurée et bien conduite. Autrement...OMI, voici les plus gros problèmes avec
scanf
:Risque de dépassement de la mémoire tampon - si vous ne spécifiez pas de largeur de champ pour les spécificateurs de conversion
%s
et%[
, vous risquez un dépassement de mémoire tampon (en essayant de lire plus d'entrée qu'une taille de mémoire tampon ne peut contenir). Malheureusement, il n'y a pas de bon moyen de spécifier cela comme argument (comme avecprintf
) - vous devez soit le coder en dur dans le cadre du spécificateur de conversion, soit faire quelques manigances de macro.Accepte les entrées qui doivent être rejetées - Si vous lisez une entrée avec le
%d
spécificateur de conversion et que vous tapez quelque chose comme12w4
, vous vous attendezscanf
à rejeter cette entrée, mais ce n'est pas le cas - elle convertit et attribue avec succès le12
, laissantw4
dans le flux d'entrée pour salir la lecture suivante.Alors, que devez-vous utiliser à la place?
Je recommande généralement de lire toutes les entrées interactives sous forme de texte en utilisant
fgets
- cela vous permet de spécifier un nombre maximum de caractères à lire à la fois, afin que vous puissiez facilement empêcher le débordement de la mémoire tampon:Une particularité
fgets
est qu'il stockera la nouvelle ligne de fin dans le tampon s'il y a de la place, vous pouvez donc vérifier facilement si quelqu'un a tapé plus de données que vous attendiez:La façon dont vous gérez cela dépend de vous - vous pouvez soit rejeter l'entrée entière d'emblée, soit récupérer toute entrée restante avec
getchar
:Ou vous pouvez traiter l'entrée que vous avez obtenue jusqu'à présent et relire. Cela dépend du problème que vous essayez de résoudre.
Pour symboliser l'entrée (la diviser en fonction d'un ou de plusieurs délimiteurs), vous pouvez utiliser
strtok
, mais attention -strtok
modifiez son entrée (elle écrase les délimiteurs avec le terminateur de chaîne), et vous ne pouvez pas conserver son état (c.-à-d., Vous pouvez ' t tokeniser partiellement une chaîne, puis commencer à tokeniser une autre, puis reprendre là où vous vous étiez arrêté dans la chaîne d'origine). Il existe une variante,strtok_s
qui préserve l'état du tokenizer, mais AFAIK son implémentation est facultative (vous devrez vérifier qu'elle__STDC_LIB_EXT1__
est définie pour voir si elle est disponible).Une fois que vous avez symbolisé votre entrée, si vous devez convertir des chaînes en nombres (c'est-à-dire
"1234"
=>1234
), vous avez des options.strtol
etstrtod
convertira les représentations de chaînes d'entiers et de nombres réels en leurs types respectifs. Ils vous permettent également d'attraper le12w4
problème que j'ai mentionné ci-dessus - l'un de leurs arguments est un pointeur vers le premier caractère non converti dans la chaîne:la source
%*[%\n]
, ce qui est utile pour traiter les lignes trop longues plus loin dans la réponse).snprintf()
),.isspace()
cela - il accepte les caractères non signés représentés commeint
, vous devez donc casterunsigned char
pour éviter UB sur les plates-formes oùchar
est signé.Dans cette réponse, je vais supposer que vous lisez et interprétez des lignes de texte . Vous invitez peut-être l'utilisateur, qui tape quelque chose et appuie sur RETOUR. Ou peut-être que vous lisez des lignes de texte structuré à partir d'un fichier de données quelconque.
Puisque vous lisez des lignes de texte, il est logique d'organiser votre code autour d'une fonction de bibliothèque qui lit, eh bien, une ligne de texte. La fonction Standard est
fgets()
, bien qu'il y en ait d'autres (y comprisgetline
). Et puis l'étape suivante consiste à interpréter cette ligne de texte d'une manière ou d'une autre.Voici la recette de base pour appeler
fgets
pour lire une ligne de texte:Cela lit simplement une ligne de texte et l'imprime. Tel qu'il est écrit, il a quelques limitations, que nous verrons dans une minute. Il a également une très grande fonctionnalité: le nombre 512 que nous avons passé comme deuxième argument
fgets
est la taille du tableau dansline
lequel nous demandonsfgets
de lire. Ce fait - que nous pouvons direfgets
combien il est autorisé à lire - signifie que nous pouvons être sûrs quefgets
le tableau ne débordera pas en y lisant trop.Alors maintenant, nous savons lire une ligne de texte, mais que faire si nous voulions vraiment lire un entier, ou un nombre à virgule flottante, ou un seul caractère, ou un seul mot? (Autrement dit, si l'
scanf
appel que nous essayons d'améliorer avait été l' aide d' un spécificateur de format comme%d
,%f
,%c
ou%s
?)Il est facile de réinterpréter une ligne de texte - une chaîne - comme n'importe laquelle de ces choses. Pour convertir une chaîne en entier, la façon la plus simple (mais imparfaite) de le faire est d'appeler
atoi()
. Pour convertir en nombre à virgule flottante, il y aatof()
. (Et il existe également de meilleures façons, comme nous le verrons dans une minute.) Voici un exemple très simple:Si vous vouliez que l'utilisateur tape un seul caractère (peut
y
- être oun
comme réponse oui / non), vous pouvez littéralement simplement saisir le premier caractère de la ligne, comme ceci:(Cela ignore, bien sûr, la possibilité que l'utilisateur tape une réponse à plusieurs caractères; il ignore silencieusement tous les caractères supplémentaires qui ont été saisis.)
Enfin, si vous vouliez que l'utilisateur tape une chaîne ne contenant certainement pas d' espace, si vous vouliez traiter la ligne d'entrée
comme la chaîne
"hello"
suivie par autre chose (ce que lescanf
format%s
aurait fait), eh bien, dans ce cas, j'ai un peu tordu, ce n'est pas si facile de réinterpréter la ligne de cette façon, après tout, donc la réponse à cela une partie de la question devra attendre un peu.Mais je veux d'abord revenir sur trois choses que j'ai ignorées.
(1) Nous avons appelé
à lire dans le tableau
line
, et où 512 est la taille du tableau saitline
doncfgets
ne pas le déborder. Mais pour vous assurer que 512 est le bon nombre (en particulier, pour vérifier si quelqu'un a peut-être modifié le programme pour changer la taille), vous devez relire où il aline
été déclaré. C'est une nuisance, il existe donc deux bien meilleures façons de synchroniser les tailles. Vous pouvez, (a) utiliser le préprocesseur pour donner un nom à la taille:Ou, (b) utilisez l'
sizeof
opérateur de C :(2) Le deuxième problème est que nous n'avons pas recherché d'erreur. Lorsque vous lisez une entrée, vous devez toujours vérifier la possibilité d'erreur. Si, pour une raison quelconque,
fgets
vous ne pouvez pas lire la ligne de texte à laquelle vous lui avez demandé, cela indique cela en renvoyant un pointeur nul. Nous aurions donc dû faire des choses commeEnfin, il y a le problème que, pour lire une ligne de texte,
fgets
lit les caractères et les remplit dans votre tableau jusqu'à ce qu'il trouve le\n
caractère qui termine la ligne, et il remplit également le\n
caractère dans votre tableau . Vous pouvez le voir si vous modifiez légèrement notre exemple précédent:Si je lance ceci et tape "Steve" quand il me le demande, il s'imprime
Cela
"
sur la deuxième ligne est dû au fait que la chaîne lue et imprimée était en fait"Steve\n"
.Parfois, cette nouvelle ligne supplémentaire n'a pas d'importance (comme lorsque nous avons appelé
atoi
ouatof
, car ils ignorent tous les deux les entrées non numériques supplémentaires après le numéro), mais parfois cela compte beaucoup. Si souvent, nous voulons supprimer cette nouvelle ligne. Il y a plusieurs façons de le faire, que j'aborderai dans une minute. (Je sais que j'ai souvent dit cela. Mais je reviendrai à toutes ces choses, je le promets.)À ce stade, vous pensez peut-être: "Je pensais que vous aviez dit que ce
scanf
n'était pas bon, et cette autre façon serait tellement mieux. Mais celafgets
commence à ressembler à une nuisance. Appelerscanf
était si facile ! Je ne peux pas continuer à l'utiliser? "Bien sûr, vous pouvez continuer à utiliser
scanf
, si vous le souhaitez. (Et pour des choses vraiment simples, à certains égards, c'est plus simple.) Mais, s'il vous plaît, ne venez pas me pleurer quand il vous échoue en raison de l'un de ses 17 caprices et faiblesses, ou entre dans une boucle infinie à cause de l'entrée de votre ne vous attendiez pas, ou quand vous ne pouvez pas comprendre comment l'utiliser pour faire quelque chose de plus compliqué. Et regardonsfgets
les nuisances réelles de:Vous devez toujours spécifier la taille du tableau. Eh bien, bien sûr, ce n'est pas du tout une nuisance - c'est une fonctionnalité, car le débordement de tampon est vraiment une mauvaise chose.
Vous devez vérifier la valeur de retour. En fait, c'est un lavage, car pour l'utiliser
scanf
correctement, vous devez également vérifier sa valeur de retour.Vous devez retirer le
\n
dos. C'est, je l'avoue, une véritable nuisance. J'aurais aimé qu'il y ait une fonction standard sur laquelle je pourrais vous indiquer qui n'a pas eu ce petit problème. (S'il vous plaît, personne n'évoquegets
.) Mais par rapport àscanf's
17 nuisances différentes, je prendrai celle-là defgets
n'importe quel jour.Alors, comment voulez - vous supprimer cette nouvelle ligne? Trois façons:
(a) Manière évidente:
(b) Façon délicate et compacte:
Malheureusement, celui-ci ne fonctionne pas toujours.
(c) Une autre manière compacte et légèrement obscure:
Et maintenant que c'est terminé, nous pouvons revenir à une autre chose que j'ai ignorée: les imperfections de
atoi()
etatof()
. Le problème avec ceux-ci est qu'ils ne vous donnent aucune indication utile de succès ou d'échec: ils ignorent tranquillement l'entrée non numérique de fin et ils retournent tranquillement 0 s'il n'y a pas d'entrée numérique du tout. Les alternatives préférées - qui présentent également certains autres avantages - sontstrtol
etstrtod
.strtol
vous permet également d'utiliser une base autre que 10, ce qui signifie que vous pouvez obtenir l'effet (entre autres)%o
ou%x
avecscanf
. Mais montrer comment utiliser correctement ces fonctions est une histoire en soi, et serait trop distraire de ce qui se transforme déjà en un récit assez fragmenté, donc je ne vais pas en dire plus à ce sujet maintenant.Le reste de la narration principale concerne une entrée que vous essayez d'analyser, c'est plus compliqué qu'un simple chiffre ou caractère. Que faire si vous souhaitez lire une ligne contenant deux nombres, ou plusieurs mots séparés par des espaces, ou une ponctuation de cadrage spécifique? C'est là que les choses deviennent intéressantes, et où les choses devenaient probablement compliquées si vous essayiez de faire des choses en utilisant
scanf
, et où il y a beaucoup plus d'options maintenant que vous avez lu proprement une ligne de texte en utilisantfgets
, bien que l'histoire complète de toutes ces options pourrait probablement remplir un livre, donc nous allons seulement pouvoir gratter la surface ici.Ma technique préférée est de diviser la ligne en «mots» séparés par des espaces, puis de faire quelque chose de plus avec chaque «mot». L'une des principales fonctions standard pour ce faire est
strtok
(qui a également ses problèmes, et qui évalue également toute une discussion séparée). Ma préférence est une fonction dédiée pour construire un tableau de pointeurs vers chaque "mot" séparé, une fonction que je décris dans ces notes de cours . Quoi qu'il en soit, une fois que vous avez des "mots", vous pouvez poursuivre le traitement de chacun, peut-être avec les mêmes fonctionsatoi
/atof
/strtol
/ questrtod
nous avons déjà examinées.Paradoxalement, même si nous avons passé
scanf
pas mal de temps et d'efforts ici à trouver un moyen de nous éloigner , une autre bonne façon de gérer la ligne de texte que nous venons de lirefgets
est de la transmettresscanf
. De cette façon, vous vous retrouvez avec la plupart des avantages descanf
, mais sans la plupart des inconvénients.Si votre syntaxe d'entrée est particulièrement compliquée, il peut être approprié d'utiliser une bibliothèque "regexp" pour l'analyser.
Enfin, vous pouvez utiliser toutes les solutions d'analyse ad hoc qui vous conviennent. Vous pouvez parcourir la ligne un caractère à la fois avec un
char *
pointeur vérifiant les caractères que vous attendez. Ou vous pouvez rechercher des caractères spécifiques en utilisant des fonctions commestrchr
oustrrchr
, oustrspn
oustrcspn
, oustrpbrk
. Ou vous pouvez analyser / convertir et ignorer des groupes de caractères numériques à l'aide des fonctionsstrtol
oustrtod
que nous avons ignorées plus tôt.Il y a évidemment beaucoup plus à dire, mais j'espère que cette introduction vous aidera à démarrer.
la source
sizeof (line)
plutôt que simplementsizeof line
? Le premier donne l'impression queline
c'est un nom de type!sscanf
comme moteur de conversion mais collecter (et éventuellement masser) l'entrée avec un outil différent. Mais cela vaut peut-être la peine d'être mentionnégetline
dans ce contexte.fscanf
nuisances réelles", voulez-vous direfgets
? Et la nuisance # 3 me dérange vraiment, surtout étant donné quescanf
renvoie un pointeur inutile vers le tampon plutôt que de renvoyer le nombre de caractères entrés (ce qui rendrait la suppression de la nouvelle ligne beaucoup plus propre).sizeof
style. Pour moi, il est facile de se souvenir de quand on veut les parens: je pense(type)
que c'est comme un casting sans valeur (parce que nous ne sommes intéressés que par le type). Une autre chose: vous dites questrtok(line, "\n")
cela ne fonctionne pas toujours, mais ce n'est pas évident quand ce n'est pas le cas. Je suppose que vous pensez au cas où la ligne était plus longue que le tampon, donc nous n'avons pas de nouvelle ligne etstrtok()
renvoie null? C'est vraiment dommage defgets()
ne pas retourner une valeur plus utile afin que nous puissions savoir si la nouvelle ligne est là ou non.Au lieu de
scanf(some_format, ...)
, considérezfgets()
avecsscanf(buffer, some_format_and %n, ...)
En utilisant
" %n"
, le code peut simplement détecter si tout le format a été scanné avec succès et qu'aucune jonque non-espace supplémentaire n'était à la fin.la source
Disons les exigences de l'analyse comme:
une entrée valide doit être acceptée (et convertie sous une autre forme)
une entrée non valide doit être rejetée
lorsqu'une entrée est rejetée, il est nécessaire de fournir à l'utilisateur un message descriptif qui explique (en clair "facilement compréhensible par les gens normaux qui ne sont pas programmeurs") pourquoi il a été rejeté (afin que les gens puissent comprendre comment corriger le problème)
Pour garder les choses très simples, considérons l'analyse d'un seul entier décimal simple (qui a été tapé par l'utilisateur) et rien d'autre. Les raisons possibles du rejet de la saisie de l'utilisateur sont les suivantes:
Définissons également correctement "l'entrée contient des caractères inacceptables"; et dire que:
5" sera traité comme "5")
À partir de cela, nous pouvons déterminer que les messages d'erreur suivants sont nécessaires:
De ce point, nous pouvons voir qu'une fonction appropriée pour convertir une chaîne en un entier devrait faire la distinction entre des types d'erreurs très différents; et que quelque chose comme "
scanf()
" ou "atoi()
" ou "strtoll()
" est complètement et totalement sans valeur parce qu'ils ne vous donnent aucune indication de ce qui n'allait pas avec l'entrée (et utilisent une définition complètement hors de propos et inappropriée de ce qui est / n'est pas "valide" contribution").Au lieu de cela, commençons à écrire quelque chose qui n'est pas inutile:
Pour répondre aux exigences énoncées; cette
convertStringToInteger()
fonction est susceptible de constituer à elle seule plusieurs centaines de lignes de code.Maintenant, c'était juste "l'analyse d'un seul entier décimal simple". Imaginez si vous vouliez analyser quelque chose de complexe; comme une liste de structures "nom, adresse, numéro de téléphone, adresse e-mail"; ou peut-être comme un langage de programmation. Dans ces cas, vous devrez peut-être écrire des milliers de lignes de code pour créer une analyse qui n'est pas une blague paralysée.
En d'autres termes...
Écrivez (potentiellement des milliers de lignes) de code vous-même, selon vos besoins.
la source
Voici un exemple d'utilisation
flex
pour numériser une entrée simple, dans ce cas un fichier de nombres à virgule flottante ASCII qui pourrait être au format US (n,nnn.dd
) ou européen (n.nnn,dd
). Ceci est juste copié à partir d'un programme beaucoup plus vaste, il peut donc y avoir des références non résolues:la source
D'autres réponses donnent les bons détails de bas niveau, donc je me limiterai à un niveau supérieur: d'abord, analysez à quoi vous attendez chaque ligne d'entrée. Essayez de décrire l'entrée avec une syntaxe formelle - avec de la chance, vous constaterez qu'elle peut être décrite en utilisant une grammaire régulière , ou au moins une grammaire sans contexte . Si une grammaire régulière suffit, vous pouvez coder une machine à états finisqui reconnaît et interprète chaque ligne de commande un caractère à la fois. Votre code lira alors une ligne (comme expliqué dans d'autres réponses), puis analysera les caractères du tampon via la machine à états. À certains états, vous vous arrêtez et convertissez la sous-chaîne analysée jusqu'à présent en un nombre ou autre. Vous pouvez probablement «rouler le vôtre» si c'est aussi simple que cela; si vous trouvez que vous avez besoin d'une grammaire complète et sans contexte, vous feriez mieux de trouver comment utiliser les outils d'analyse existants (re:
lex
et /yacc
ou leurs variantes).la source
errno == EOVERFLOW
après utilisationstrtoll
) sont possibles.