J'essaie d'obtenir des données de l'utilisateur et de les envoyer à une autre fonction dans gcc. Le code est quelque chose comme ça.
printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
fprintf(stderr, "Error reading Name.\n");
exit(1);
}
Cependant, je trouve qu'il a un \n
caractère de nouvelle ligne à la fin. Donc, si j'entre, John
il finit par envoyer John\n
. Comment puis-je supprimer cela \n
et envoyer une chaîne appropriée.
if (!fgets(Name, sizeof Name, stdin))
(à tout le moins, n'utilisez pas deux négations,! et! =)if (fgets(Name, sizeof Name, stdin)) {
.if (fgets(Name, sizeof Name, stdin) == NULL ) {
!
:Réponses:
La manière légèrement laide:
La manière un peu étrange:
Notez que la
strtok
fonction ne fonctionne pas comme prévu si l'utilisateur entre une chaîne vide (c'est-à-dire qu'il appuie uniquement sur Entrée). Il laisse le\n
personnage intact.Il y en a bien sûr d'autres également.
la source
strtok()
sera thread-safe (elle utilisera le stockage local des threads pour l'état 'inter-appel'). Cela dit, il est généralement préférable d'utiliser lastrtok_r()
variante non standard (mais assez courante) .strtok
approche (et cela fonctionne avec des entrées vides). En fait, un bon moyen de mise en œuvrestrtok
consiste à utiliserstrcspn
etstrspn
.*strchrnul(Name, '\n') = '\0';
.strchr(Name, '\n') == NULL
, outre "l'entrée trop longue pour le tampon, erreur de drapeau", d'autres possibilités existent: le dernier texte destdin
ne s'est pas terminé par un'\n'
ou un rare caractère nul incorporé a été lu.Peut-être que la solution la plus simple utilise l'une de mes fonctions préférées les moins connues
strcspn()
:Si vous souhaitez qu'il gère également
'\r'
(par exemple, si le flux est binaire):La fonction compte le nombre de caractères jusqu'à ce qu'elle atteigne a
'\r'
ou a'\n'
(en d'autres termes, elle trouve le premier'\r'
ou'\n'
). S'il ne touche rien, il s'arrête à'\0'
(renvoyant la longueur de la chaîne).Notez que cela fonctionne bien même s'il n'y a pas de nouvelle ligne, car
strcspn
s'arrête à a'\0'
. Dans ce cas, la ligne entière est simplement remplacée'\0'
par'\0'
.la source
buffer
que commence avec'\0'
, ce qui cause la douleur de l'buffer[strlen(buffer) - 1] = '\0';
approche.strcspn()
. IMO est l'une des fonctions les plus utiles de la bibliothèque. J'ai décidé d'écrire et de publier un tas de hacks C courants comme celui-ci aujourd'hui; unestrtok_r
implémentation utilisantstrcspn
et astrspn
été l'une des premières: codepad.org/2lBkZk0w ( Attention: je ne peux pas garantir que c'est sans bugs; il a été écrit à la hâte et en a probablement quelques-uns). Je ne sais pas encore où je vais les publier, cependant, mais j'ai l'intention de le faire dans l'esprit des fameux "hacks de twiddling".fgets()
strcspn()
strlen
fgets()
fin d' entrée . Ce qui est toujours aussi la première nouvelle ligne.la source
fgets(buf, size, ....)
->strlen(buf) == 0
. 1) sefgets()
lit comme le premierchar
a'\0'
. 2)size == 1
3)fgets()
renvoieNULL
alors lebuf
contenu pourrait être n'importe quoi. (Le code OP teste NULL cependant)size_t ln = strlen(name); if (ln > 0 && name[ln-1] == '\n') name[--ln] = '\0';
ln
serait -1, sauf que le faitsize_t
n'est pas signé, écrivant ainsi dans une mémoire aléatoire. Je pense que vous voulez utiliserssize_t
et vérifierln
est> 0.strlen
) peut être implémentée beaucoup plus efficacement qu'une recherche simple caractère par caractère. Pour cette raison, je considérerais cette solution meilleure que celles basées surstrchr
oustrcspn
.Vous trouverez ci-dessous une approche rapide pour supprimer un potentiel
'\n'
d'une chaîne enregistrée parfgets()
.Il utilise
strlen()
, avec 2 tests.Maintenant, utilisez
buffer
etlen
au besoin.Cette méthode a l'avantage secondaire d'une
len
valeur pour le code suivant. Cela peut être facilement plus rapide questrchr(Name, '\n')
. Réf YMMV, mais les deux méthodes fonctionnent.buffer
, à partir de l'originalfgets()
ne contiendra pas dans"\n"
certaines circonstances:A) La ligne était trop longue pour
buffer
que seulement lechar
précédent'\n'
soit enregistré dansbuffer
. Les caractères non lus restent dans le flux.B) La dernière ligne du fichier ne s'est pas terminée par un
'\n'
.Si l'entrée contient des caractères nuls
'\0'
quelque part, la longueur signalée parstrlen()
n'inclura pas l''\n'
emplacement.Quelques autres questions de réponses:
strtok(buffer, "\n");
ne parvient pas à supprimer le'\n'
quandbuffer
est"\n"
. De cette réponse - modifié après cette réponse pour avertir de cette limitation.Ce qui suit échoue en de rares occasions lorsque la première
char
lecturefgets()
est'\0'
. Cela se produit lorsque l'entrée commence par un imbriqué'\0'
. Devientbuffer[len -1]
alorsbuffer[SIZE_MAX]
accéder à la mémoire certainement en dehors de la plage légitime debuffer
. Quelque chose qu'un pirate pourrait essayer ou trouver en lisant bêtement des fichiers texte UTF16. C'était l'état d'une réponse lorsque cette réponse a été écrite. Plus tard, un non-OP l'a édité pour inclure du code comme la vérification de cette réponse""
.sprintf(buffer,"%s",buffer);
est un comportement indéfini: Réf . En outre, il n'enregistre aucun espace de début, de séparation ou de fin. Maintenant supprimé .[Modifier en raison d'une bonne réponse ultérieure ] Il n'y a aucun problème avec la doublure
buffer[strcspn(buffer, "\n")] = 0;
autre que les performances par rapport à l'strlen()
approche. Les performances de découpage ne sont généralement pas un problème étant donné que le code fait des E / S - un trou noir du temps CPU. Si le code suivant nécessite la longueur de la chaîne ou est très sensible aux performances, utilisez cettestrlen()
approche. Sinon,strcspn()
c'est une bonne alternative.la source
strlen(buffer)
lorsque la taille du tampon est allouée dynamiquement en utilisantmalloc
?buffer = malloc(allocation_size); length = strlen(buffer);
est mauvais - les données de la mémoire pointées parbuffer
sont inconnues.buffer = malloc(allocation_size_4_or_more); strcpy(buffer, "abc"); length = strlen(buffer);
est OKDirect pour supprimer le '\ n' de la sortie fgets si chaque ligne a '\ n'
Autrement:
la source
strnlen
place destrlen
.n
n'augmente pas comme par magie la sécurité, dans ce cas cela rendrait en fait le code plus dangereux. De même avecstrncpy
, une fonction terriblement dangereuse. Le message auquel vous avez lié est un mauvais conseil.""
). Aussistrlen()
retournesize_t
pasint
.Pour un découpage '\ n' unique,
pour un découpage "\ n" multiple,
la source
if
quand vous pouvez simplement écrire une condition en utilisant&&
? Cettewhile
boucle a une structure étrange; ça pourrait être tout simplementwhile (length > 0 && string[length-1] == '\n') { --length; string[length] = '\0'; }
.size_t length = strlen(string); if (length > 0 && string[length-1] == '\n') { string[length-1] = '\0'; }
. Cela reflète également mieux la deuxième définition (en utilisant simplementif
au lieu dewhile
).My Newbie way ;-) Veuillez me faire savoir si c'est correct. Il semble fonctionner pour tous mes cas:
la source
Les étapes pour supprimer le caractère de nouvelle ligne de la manière peut-être la plus évidente:
NAME
en utilisantstrlen()
, headerstring.h
. Notez questrlen()
cela ne compte pas la terminaison\0
.\0
caractère (chaîne vide). Dans ce cassl
,0
carstrlen()
comme je l'ai dit ci-dessus ne compte pas le\0
et s'arrête à sa première occurrence:'\n'
. Si tel est le cas, remplacez-le\n
par un\0
. Notez que le nombre d'index commence à0
donc nous devrons faireNAME[sl - 1]
:Notez que si vous avez uniquement appuyé sur Entrée à la
fgets()
demande de chaîne (le contenu de la chaîne était uniquement composé d'un caractère de nouvelle ligne), la chaîne enNAME
sera une chaîne vide par la suite.if
déclaration en utilisant l'opérateur logique&&
:Si vous aimez plutôt une fonction pour utiliser cette technique en gérant
fgets
les chaînes de sortie en général sans retaper à chaque fois, voicifgets_newline_kill
:Dans votre exemple fourni, ce serait:
Notez que cette méthode ne fonctionne pas si la chaîne d'entrée contient des
\0
s incorporés . Si tel était le casstrlen()
, le nombre de caractères ne serait renvoyé que jusqu'au premier\0
. Mais ce n'est pas une approche assez courante, car la plupart des fonctions de lecture de chaîne s'arrêtent généralement au premier\0
et prennent la chaîne jusqu'à ce caractère nul.Mis à part la question seule. Essayez d'éviter la double négations qui font votre code unclearer:
if (!(fgets(Name, sizeof Name, stdin) != NULL) {}
. Vous pouvez simplement le faireif (fgets(Name, sizeof Name, stdin) == NULL) {}
.la source
\n
par un\0
à la fin d'une chaîne est un moyen de "supprimer" la nouvelle ligne. Mais le remplacement de\n
caractères dans une chaîne change fondamentalement la chaîne. Il n'est pas rare d'avoir des chaînes avec plusieurs caractères de nouvelle ligne intentionnels, et cela couperait effectivement les extrémités de ces chaînes. Pour supprimer ces nouvelles lignes, le contenu du tableau doit être déplacé vers la gauche pour remplacer le\n
.fgets()
?fgets()
. Mais je ne comprends pas votre objection: c'est vous qui proposez du code pour gérer plusieurs sauts de ligne.strlen
etc. Justification de ne pas être un doublon: 1. Explication du code par étapes. 2. Fourni comme une fonction et une solution contextuelle. 3. Astuce pour éviter les expressions de double négation.Tim Čas one liner est étonnant pour les cordes obtenues par un appel aux fgets, car vous savez qu'ils contiennent une seule nouvelle ligne à la fin.
Si vous êtes dans un contexte différent et que vous souhaitez gérer des chaînes pouvant contenir plusieurs sauts de ligne, vous recherchez peut-être strrspn. Ce n'est pas POSIX, ce qui signifie que vous ne le trouverez pas sur tous les Unices. J'en ai écrit un pour mes propres besoins.
Pour ceux qui recherchent un équivalent de chomp Perl en C, je pense que c'est ça (chomp ne supprime que la nouvelle ligne de fin).
La fonction strrcspn:
la source
'\n'
(ou si la chaîne l'est""
).strrcspn
pour quand il n'y a pas\n
.goto end;
au lieu dereturn len;
?goto
s dans votre code: un inutilegoto
qui peut être remplacé par unereturn
déclaration et un arrièregoto
qui est considéré comme mauvais. L'utilisationstrchr
aide à implémenterstrrspn
etstrrcspn
de manière plus simple:size_t strrspn(const char *s, const char *accept) { size_t len = strlen(s); while (len > 0 && strchr(accept, s[len - 1])) { len--; } return len; }
etsize_t strrcspn(const char *s, const char *reject) { size_t len = strlen(s); while (len > 0 && !strchr(reject, s[len - 1])) { len--; } return len; }
Si l'utilisation
getline
est une option - sans négliger ses problèmes de sécurité et si vous souhaitez renforcer les pointeurs - vous pouvez éviter les fonctions de chaîne car lagetline
renvoie le nombre de caractères. Quelque chose comme ci-dessousRemarque : Les [ problèmes de sécurité ] avec
getline
ne doivent pas être négligés cependant.la source
La fonction ci-dessous fait partie de la bibliothèque de traitement de chaînes que je gère sur Github. Il supprime et les caractères indésirables d'une chaîne, exactement ce que vous voulez
Un exemple d'utilisation pourrait être
Vous voudrez peut-être vérifier d'autres fonctions disponibles, ou même contribuer au projet :) https://github.com/fnoyanisi/zString
la source
*
in*src++;
et fairebad
,token
etd
const char *
. Aussi pourquoi ne pas utiliser à lastrchr
place dezChrSearch
?*src
ne peut pas être'\0'
dans votrezStrrmv
fonction.strchr
Tu devrais essayer. Ce code parcourt en boucle la chaîne jusqu'à ce qu'il trouve le '\ n'. Lorsqu'il est trouvé, le '\ n' sera remplacé par le terminateur de caractère nul '\ 0'
Notez que vous comparez des caractères et non des chaînes dans cette ligne, il n'est donc pas nécessaire d'utiliser strcmp ():
puisque vous utiliserez des guillemets simples et non des guillemets doubles. Voici un lien sur les guillemets simples vs doubles si vous voulez en savoir plus
la source
for(int i = 0; i < strlen(Name); i++ )
appellerastrlen(Name)
plusieurs fois (changements de boucleName[]
) donc avec une longueurN
, c'est uneO(N*N)
solution. Un seul appel àstrlen(Name)
, le cas échéant, est nécessaire pour fournir une solution O (N) `. On ne sait pas pourquoiint i
est utilisé à la place desize_t i
. Considérezfor(size_t i = 0; i < Name[i]; i++ )
for (size_t i = 0; Name[i]; i++) { if (Name[i] == '\n') { Name[i] = '\0'; break; } }
Essaye celui-là:
la source
len = strlen(str)
peut déborder:strlen
retournesize_t
pasint
. Qu'en est-il desif (len>0) if (...)
conditions étranges ? Tu ne sais pas&&
? Si vous comptez supprimer plusieurs instances finales de CR / LF, pourquoi vous limiter à 5? Pourquoi ne pas les supprimer tous? Pourquoi la fonction a-t-elle unint
type de retour alors qu'elle revient toujours0
? Pourquoi ne pas simplement revenirvoid
?