web-dev-qa-db-fra.com

Suppression du caractère de fin de ligne de la dernière entrée de fgets ()

J'essaie d'obtenir des données de l'utilisateur et de les envoyer à une autre fonction de 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 caractère newline \n à la fin. Donc, si je saisis John, cela finit par envoyer John\n. Comment puis-je supprimer ce \n et envoyer une chaîne appropriée.

182
sfactor

La manière légèrement laide: 

char *pos;
if ((pos=strchr(Name, '\n')) != NULL)
    *pos = '\0';
else
    /* input too long for buffer, flag error */

La manière un peu étrange:

strtok(Name, "\n");

Notez que la fonction strtok ne fonctionne pas correctement si l’utilisateur entre une chaîne vide (c’est-à-dire appuie uniquement sur Entrée). Il laisse le caractère \n intact.

Il y en a d'autres aussi, bien sûr.

117
Jerry Coffin

Peut-être que la solution la plus simple utilise l'une de mes fonctions préférées peu connues, strcspn() :

buffer[strcspn(buffer, "\n")] = 0;

Si vous voulez qu'il gère également '\r' (par exemple, si le flux est binaire):

buffer[strcspn(buffer, "\r\n")] = 0; // works for LF, CR, CRLF, LFCR, ...

La fonction compte le nombre de caractères jusqu'à atteindre un '\r' ou un '\n' (en d'autres termes, elle trouve le premier '\r' ou '\n'). S'il ne touche rien, il s'arrête au '\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 à '\0'. Dans ce cas, la ligne entière remplace simplement '\0' par '\0'.

334
Tim Čas
size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n') 
    name[ln] = '\0';
82
James Morris

Vous trouverez ci-dessous une méthode rapide pour supprimer un '\n' potentiel d'une chaîne enregistrée par fgets().
Il utilise strlen(), avec 2 tests.

char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) != NULL) {

  size_t len = strlen(buffer);
  if (len > 0 && buffer[len-1] == '\n') {
    buffer[--len] = '\0';
  }

Maintenant, utilisez buffer et len si nécessaire.

Cette méthode présente l’avantage supplémentaire d’une valeur len pour le code suivant. Cela peut être facilement plus rapide que strchr(Name, '\n'). Ref YMMV, mais les deux méthodes fonctionnent.


buffer, de la fgets() d'origine ne contiendra pas "\n" dans certaines circonstances:
A) La ligne était trop longue pour buffer, de sorte que seule char précédant le '\n' est enregistrée dans buffer. 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' incorporés quelque part, la longueur indiquée par strlen() n'inclura pas l'emplacement '\n'.


Quelques autres problèmes de réponses: 

  1. strtok(buffer, "\n"); ne parvient pas à supprimer le '\n' lorsque buffer est "\n". De cette réponse - amendée après cette réponse pour avertir de cette limitation. 

  2. Les éléments suivants échouent à de rares occasions où la première char lue par fgets() est '\0'. Cela se produit lorsque l’entrée commence par un '\0' incorporé. Alors buffer[len -1] devient buffer[SIZE_MAX] et accède à la mémoire certainement en dehors de la plage légitime de buffer. Quelque chose qu'un pirate informatique 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 tel que le contrôle de cette réponse pour "".

    size_t len = strlen(buffer);
    if (buffer[len - 1] == '\n') {  // FAILS when len == 0
      buffer[len -1] = '\0';
    }
    
  3. sprintf(buffer,"%s",buffer); est un comportement non défini: Ref . En outre, cela ne sauvegarde pas les espaces de début, de séparation ou de fin. Maintenant supprimé .

  4. [Editer pour une bonne raison plus tard answer ] Il n'y a pas de problème avec la buffer[strcspn(buffer, "\n")] = 0; à 1 couche autre que la performance par rapport à l'approche strlen(). Les performances de rognage ne sont généralement pas un problème, car le code effectue des E/S - un trou noir du temps processeur. Si le code suivant a besoin de la longueur de la chaîne ou est très soucieux des performances, utilisez cette approche strlen(). Sinon, la strcspn() est une excellente alternative.

16
chux

Direct pour supprimer le '\ n' de la sortie de fgets si chaque ligne a '\ n'

line[strlen(line) - 1] = '\0';

Autrement:

void remove_newline_ch(char *line)
{
    int new_line = strlen(line) -1;
    if (line[new_line] == '\n')
        line[new_line] = '\0';
}
3
Amitabha

Pour un seul '\ n' enregistrement,

void remove_new_line(char* string)
{
    size_t length = strlen(string);
    if((length > 0) && (string[length-1] == '\n'))
    {
        string[length-1] ='\0';
    }
}

pour plusieurs '\ n' rognage,

void remove_multi_new_line(char* string)
{
  size_t length = strlen(string);
  while((length>0) && (string[length-1] == '\n'))
  {
      --length;
      string[length] ='\0';
  }
}
1
Naveen Kumar

Mon chemin Newbie ;-) S'il vous plaît laissez-moi savoir si c'est correct. Cela semble fonctionner pour tous mes cas:

#define IPT_SIZE 5

int findNULL(char* arr)
{
    for (int i = 0; i < strlen(arr); i++)
    {
        if (*(arr+i) == '\n')
        {
            return i;
        }
    }
    return 0;
}

int main()
{
    char *input = malloc(IPT_SIZE + 1 * sizeof(char)), buff;
    int counter = 0;

    //Prompt user for the input:
    printf("input string no longer than %i characters: ", IPT_SIZE);
    do
    {
        fgets(input, 1000, stdin);
        *(input + findNULL(input)) = '\0';
        if (strlen(input) > IPT_SIZE)
        {
            printf("error! the given string is too large. try again...\n");
            counter++;
        }
        //if the counter exceeds 3, exit the program (custom function):
        errorMsgExit(counter, 3); 
    }
    while (strlen(input) > IPT_SIZE);

//rest of the program follows

free(input)
return 0;
}
0
Pawel Flajszer

Tim Čas une ligne est incroyable pour les chaînes obtenues par un appel à fgets, parce que vous savez qu'ils contiennent une nouvelle ligne à la fin. 

Si vous êtes dans un contexte différent et que vous souhaitez gérer des chaînes pouvant contenir plusieurs nouvelles lignes, 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.

/* Returns the length of the segment leading to the last 
   characters of s in accept. */
size_t strrspn (const char *s, const char *accept)
{
  const char *ch;
  size_t len = strlen(s);

more: 
  if (len > 0) {
    for (ch = accept ; *ch != 0 ; ch++) {
      if (s[len - 1] == *ch) {
        len--;
        goto more;
      }
    }
  }
  return len;
}

Pour ceux qui recherchent un équivalent de Perl chomp en C, je pense que c’est la solution (chomp ne supprime que la nouvelle ligne finale).

line[strrspn(string, "\r\n")] = 0;

La fonction strrcspn:

/* Returns the length of the segment leading to the last 
   character of reject in s. */
size_t strrcspn (const char *s, const char *reject)
{
  const char *ch;
  size_t len = strlen(s);
  size_t origlen = len;

  while (len > 0) {
    for (ch = reject ; *ch != 0 ; ch++) {
      if (s[len - 1] == *ch) {
        return len;
      }
    }
    len--;
  }
  return origlen;
}
0
Philippe A.

Si l'option getline est une option (ne pas négliger ses problèmes de sécurité et si vous souhaitez renforcer les pointeurs), vous pouvez éviter les fonctions de chaîne, car getline renvoie le nombre de caractères. Quelque chose comme ci-dessous

#include<stdio.h>
#include<stdlib.h>
int main(){
char *fname,*lname;
size_t size=32,nchar; // Max size of strings and number of characters read
fname=malloc(size*sizeof *fname);
lname=malloc(size*sizeof *lname);
if(NULL == fname || NULL == lname){
 printf("Error in memory allocation.");
 exit(1);
}
printf("Enter first name ");
nchar=getline(&fname,&size,stdin);
if(nchar == -1){ // getline return -1 on failure to read a line.
 printf("Line couldn't be read.."); 
 // This if block could be repeated for next getline too
 exit(1);
}
printf("Number of characters read :%zu\n",nchar);
fname[nchar-1]='\0';
printf("Enter last name ");
nchar=getline(&lname,&size,stdin);
printf("Number of characters read :%zu\n",nchar);
lname[nchar-1]='\0';
printf("Name entered %s %s\n",fname,lname);
return 0;
}

Note: Les [problèmes de sécurité] avec getline ne doivent cependant pas être négligés.

0
sjsam