2

I made this function to get input:

void entrada_dados(Time* time, int i){
    scanf("%s %d %d", time[i].nome, &time[i].gols_marcados, &time[i].gols_sofridos);
};

The input is in this form:

2
Campinense 
23
12
ABC
30
13

The main is:

int main(void) {

  int n = 0;
  scanf("%d", &n);
  
  for(int i = 0; i < n; i++){
    entrada_dados(time, i);
 }
....

My problem is when the team name have some space like to "São Paulo". I have tried some forms to solve, but no one solved my problem.

I tried:

void entrada_dados(Time* time, int i){
    fscanf(stdin, "%[^\n] %d %d", time[i].nome, &time[i].gols_marcados, &time[i].gols_sofridos);
};

and:

void entrada_dados(Time* time, int i){
    fgets(time[i].nome, 100, stdin);
    scanf("%d", &time[i].gols_marcados);
    scanf("%d", &time[i].gols_sofridos);
  }

but in the first case the output have nothing, and second case the output miss some cases. Someone can help me to understand this problem?

Edit 1: The definition of .name is:

typedef struct Time{
  char nome[100];
  int gols_marcados;
  int gols_sofridos;
} Time;

Edit 2:

Solution: One way to solve it:

Try two fscanfs fscanf(stdin, " %[^\n]", time[i].nome);
fscanf(stdin, "%d %d", &time[i].gols_marcados, &time[i].gols_sofridos);

Thank you guys.

Vinicius
  • 21
  • 2
  • 2
    Vinicius, `"%[^\n]"` and `"%s"`, without a _width_ limit are poor coding. – chux - Reinstate Monica Jun 21 '22 at 21:39
  • Try using fgets instead of scanf. Scanf stands for "scan formatted" and you need to have total control of the input to avoid issues like buffer overflow. With fgets you can set your own size limit. Here's how to use it: https://stackoverflow.com/a/1248017/2205089 – Alin Gabriel Arhip Jun 21 '22 at 21:40
  • Thank you user3121023 for the help. Can you explain why i have to add this space in `fscanf(stdin, " %[^\n]", time[i].nome);` I have tried to add two fscanf to solve this, but without this blank space in `fscanf(stdin, " %[^\n]", time[i].nome);` And i didn't work. – Vinicius Jun 21 '22 at 21:49
  • 1
    Some explanation: most of the format specifiers for `scanf` automatically filter leading whitespace, but `%c` and `%[]` and `%n` do not. Adding a space in front of the `%` instructs `scanf` to filter leading whitespace here too. – Weather Vane Jun 21 '22 at 23:30

1 Answers1

1

Because you have to handle strings with spaces, it's better to use fgets for those.

But mixing fgets and scanf doesn't work too well. We can replace scanf with fgets followed by sscanf.

To decode numbers, we can use strtol or sscanf

We take advantage of the fact that each element/member of Time appears on a separate line in the input file, so we can do fgets for every line. This simplifies the code and makes error checking easier.


Here is the refactored code. It is annotated.

I didn't do this, but, if these sequences are done a lot, we can combine some of these sequences in helper functions to reduce some code replication (e.g. a function that combines the fgets followed by the sscanf)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Time {
    char nome[100];
    int gols_marcados;
    int gols_sofridos;
} Time;

// RETURNS: 1=valid, 0=syntax error
int
entrada_dados(Time *timelist, int i)
{
    char buf[100];
    char *cp;
    Time *tim = &timelist[i];
    int valid = 0;

    do {
        // get name
        if (fgets(tim->nome,sizeof(tim->nome),stdin) == NULL)
            break;

        // strip newline
        tim->nome[strcspn(tim->nome,"\n")] = 0;

        // get number using strtol
        if (fgets(buf,sizeof(buf),stdin) == NULL)
            break;
        tim->gols_marcados = strtol(buf,&cp,10);
        if (*cp != '\n')
            break;

        // get number using sscanf
        if (fgets(buf,sizeof(buf),stdin) == NULL)
            break;
        if (sscanf(buf,"%d",&tim->gols_sofridos) != 1)
            break;

        // all input is okay
        valid = 1;
    } while (0);

    return valid;
};

int
main(void)
{

    int n = 0;
#if 0
    scanf("%d", &n);
#else
    char buf[100];
    if (fgets(buf,sizeof(buf),stdin) == NULL)
        exit(1);
    sscanf(buf,"%d",&n);
#endif

    // allocate sufficient space
    Time *timelist = malloc(sizeof(*timelist) * n);

    // read in data
    int valid = 0;
    for (int i = 0; i < n; i++) {
        valid = entrada_dados(timelist, i);
        if (! valid)
            break;
    }

    // show the data
    if (valid) {
        for (int i = 0; i < n; i++) {
            Time *tim = &timelist[i];
            printf("nome='%s' gols_marcados=%d gols_sofridos=%d\n",
                tim->nome,tim->gols_marcados,tim->gols_sofridos);
        }
    }

    return 0;
}

Here is the program input:

3
Campinense
23
12
ABC
30
13
São Paulo
17
82

Here is the program output:

nome='Campinense' gols_marcados=23 gols_sofridos=12
nome='ABC' gols_marcados=30 gols_sofridos=13
nome='São Paulo' gols_marcados=17 gols_sofridos=82
Craig Estey
  • 30,627
  • 4
  • 24
  • 48