web-dev-qa-db-fra.com

C - Ignorer les espaces dans scanf ()

J'essaie de faire une acquisition de chaîne simple. Ce dont j'ai besoin, c’est d’écrire une chaîne d’entrée (stdin), qui peut contenir des espaces, et de la sauvegarder sans aucun espace entre les mots. 

Jusqu'ici, j'ai écrit ce code simple qui enregistre tout (y compris les espaces), mais je ne sais pas comment faire en sorte que scanf() ignore les espaces.

int main(){
    char str[10];
    scanf("%[^\n]s, str);
    printf("%s", str;
}

Par exemple

si mon entrée est: I love C programming! ma sortie devrait être: IloveCprogramming!

J'ai essayé d'utiliser %*, utilisé pour ignorer les caractères, mais sans succès.

Je sais aussi que je pourrais "réanalyser" la chaîne une fois enregistrée et supprimer tous les espaces, mais je dois effectuer cette acquisition aussi efficacement que possible, et réanalyser chaque chaîne pour supprimer les espaces augmentera considérablement le temps de calcul (au lieu de juste numériser et ignorer, ce qui a la complexité de O (n))

2
Mattia Surricchio

Jusqu'ici, j'ai écrit ce code simple qui enregistre tout (ainsi que Espaces), mais je ne sais pas comment faire en sorte que la fonction scanf() ignore les espaces.

Vous abordez la question en sens inverse de ce que font la plupart des nouveaux programmeurs C. Le problème n'est généralement pas de faire scanf ignorer des espaces, comme c'est le cas par défaut pour la plupart des types de champs, et en particulier pour les champs %s. Les espaces sont généralement reconnus comme des délimiteurs de champs. Ainsi, non seulement les espaces de début sont ignorés, mais ils ne sont pas non plus lus à l'intérieur des champs. Je suppose que c'est parce que vous le savez que vous utilisez un champ %[.

Mais vous ne pouvez pas avoir votre gâteau et le manger aussi. La directive de champ %[^\n] indique que les données à lire consistent en une série de caractères non nouveaux. scanf lira fidèlement tous ces caractères et les transférera dans le tableau que vous désignez . Vous n'avez pas la possibilité de demander à scanf d'éviter de transférer certains des caractères que vous avez décrits comme faisant partie du champ.

Si vous souhaitez continuer à utiliser scanf, vous avez deux options:

  • supprimer les espaces après avoir lu les données, ou
  • lire et transférer les pièces séparées par des espaces en tant que champs séparés.

Une autre réponse décrit déjà comment faire le premier. Voici comment vous pouvez faire le dernier:

int main(void) {
    int field_count;

    do {
        char str[80];
        char tail;

        field_count = scanf("%79[^ \n]%c", str, &tail));
        if (field_count == 0) {
            // No string was scanned this iteration: the first available char
            // was a space or newline.  Consume it, then proceed appropriately.
            field_count = scanf("%c", &tail);
            if (field_count != 1 || tail == '\n') {
                // newline, end-of-file, or error: break out of the loop
                break;
            } // else it's a space -- ignore it
        } else if (field_count > 0) {
            // A string was scanned; print it:
            printf("%s", str);

            if (field_count == 2) {
                // A trailing character was scanned, too; take appropriate action:
                if (tail == '\n') {
                    break;
                } else if (tail != ' ') {
                    putchar(tail);
                } // else it is a space; ignore it
            }
        } // else field_count == EOF
    } while (field_count != EOF);
}

Choses à noter :

  • La largeur du champ (maximum) de 79 caractères dans la directive scanf%79[^ \n]. Sans une largeur de champ, il existe un risque sérieux de dépasser la limite de votre tableau (ce qui doit être au moins un caractère plus long que le champ pour permettre un terminateur de chaîne).
  • [ est un type de champ, pas un qualificatif. s est un type de champ distinct qui gère également les chaînes, mais a un comportement différent; aucun champ s n'est utilisé ici.
  • La valeur de retour de scanf vous indique le nombre de champs ayant été analysés avec succès, ce qui peut être inférieur à celui décrit dans la chaîne de format en cas d'incompatibilité entre entrée et format, si la fin de l'entrée est atteinte ou si une erreur d'E/S est atteinte. se produit. Ces possibilités doivent être prises en compte.
  • Dans le cas où le second champ, %c, est effectivement analysé, il vous permet de déterminer si le champ de chaîne précédent s'est terminé parce que la largeur du champ était épuisée sans atteindre un espace ou une nouvelle ligne, car un espace a été observé ou un nouveau trait a été utilisé. observé. Chacun de ces cas nécessite une manipulation différente.
  • Bien que scanf ignore les espaces de type pour les types most , %[ et %c sont deux des trois exceptions.
  • Cette approche ignore spécifiquement les espaces (' '); elle ne saute pas d'autres caractères d'espacement, tels que les tabulations horizontales et verticales, les retours à la ligne, les sauts de formulaire, etc. Cette approche peut également être adaptée, mais ce qui est présenté suffit à démontrer.
1
John Bollinger

Vous utilisez le mauvais outil pour le travail. Vous devez utiliser getc

Et faire ce qui suit

int ch;
char str[10];

// Loop until either loop reaches 9 (need one for null character) or EOF is reached
for (int loop = 0; loop < 9 && (ch = getc(stdin)) != EOF; ) {
   if (ch != ' ' ) {
     str[loop] = ch;
     ++loop;
   }
}
str[loop] = 0;

printf("%s", str);

Pas de re-scan nécessaire

4
Ed Heal

Si vous souhaitez supprimer d'autres espaces blancs (en plus de ''), vous pouvez également incorporer la fonction de bibliothèque Cisspace (.), qui teste les caractères d'espaces blancs standard suivants:

'' (0x20) espace (SPC)
'\ t' (0x09) onglet horizontal (TAB)
'\ n' (0x0a) nouvelle ligne (LF)
'\ v' (0x0b) onglet vertical (VT)
'\ f' (0x0c) alimentation (FF)
'\ r' (0x0d) retour chariot (CR)

Cet exemple incorpore une fonction utilisant la fonction de bibliothèque isspace(.); et fournit une méthode pour effacer tous les espaces blancs standard d'une chaîne C. 

int main(void)
{
    char string[] = {"this contain's \n whitespace\t"};
    int len = strlen(string);
    char out[len+1];// +1 for null terminator 
                    //(accommodates when input contains no whitespace)
    int count = clean_whitespace(string, out);

    return 0;
}

int clean_whitespace(const char *in, char *out)
{
    int len, count=0, i;
    if((in) && (out))
    {
        len = strlen(in);
        for(i=0;i<len;i++)
        {
            if(!isspace(in[i]))
            {
                out[count++] = in[i];
            }
        }
        out[count]=0;//add null terminator.
    }
    return count;
}
2
ryyker

scanf() n'est pas utile dans ce but, en effet vous n'avez même pas besoin d'un tampon pour effacer les espaces d'une ligne d'entrée: lisez simplement les octets un par un, ignorez les espaces, affichez les autres et arrêtez-vous à la nouvelle ligne ou à l'EOF:

#include <stdio.h>

int main(void) {
    int c;
    while ((c = getchar()) != EOF) {
        if (c != ' ') {
            putchar(c);
        }
        if (c == '\n') {
            break;
        }
    }
    return 0;
}

Notez également que votre code a des problèmes:

  • la chaîne de formatage scanf() n'est pas terminée
  • la dernière s est incorrecte, le format est simplement %[^\n]
  • il est plus sûr de spécifier le nombre maximal d'octets à stocker dans le tableau avant le terminateur nul: scanf("%9[^\n]", str);
  • vous devez tester la valeur de retour scanf() pour éviter de transmettre un tableau non initialisé à printf si la conversion échoue, par exemple sur une ligne ou un fichier vide.

Vous pouvez utiliser scanf() comme un moyen inefficace de lire les caractères tout en ignorant les espaces, avec char c; while (scanf(" %c", &c) == 1) { putchar(c); } mais vous ne pourriez pas détecter la fin de la ligne.

2
chqrlie

Je poste ceci pour démontrer qu'il est également possible de résoudre ce problème simplement avec scanf.

 int main() {
   char a[10];
    for(int i = 0; i < 10 ; i++){
        scanf("%c", &a[i]);
        if( a[i] == ' ')
        i--;
    }
}

celui ci-dessus scanne simplement 10 caractères sans espaces entre eux.

    for(int i = 0; i < 9; i++){
       printf("%c,", a[i]);
     }

    printf("%c", a[9]);

et c'est le moyen d'utiliser si vous voulez remplacer les espaces par autre chose, par exemple: ','

Si vous voulez que l'entrée soit composée de plus de caractères, définissez simplement une nouvelle variable x et changez le 10 en x et le 9 en x-1