0

I am trying to get the values I need from a string using sscanf, but I can't get it done. Here is what I am trying to do:

I have a string which has this pattern

2 7 A BUL

(integer space integer space character space string of 3 elements)

I have to get each value separated by spaces and store it into variables.

This is my code:

sscanf(string[i],"%d %d %s %s",&e,&m,id,modelo);

The problem I'm having is that it only stores the first integers, ignoring the chars.

How can I fix this?

Thank you.

EDIT:

Here's the whole code for the function:

void le_lista(lista *l) {
int e,m;
char id[1],modelo[3],frase[20][12];
int linha=0;
while (1) {
    fgets(frase[linha],12,stdin);
    //

    if (feof(stdin)) {
        break;
    }
    //
    linha++;
}
int i;
for(i=0;i<=linha;i++) {
    sscanf(frase[i],"%d %d %s %s",&e,&m,&id,&modelo);
    enfila(e,m,id,modelo,l);
    //printf("%s",frase[i]);
}
printf("Linhas: %d",linha+1);
return;

}

  • 2
    could you post more code? is there space allocated for the strings? – tay10r Jul 28 '13 at 19:39
  • @AndreasGrapentin that's probably not the problem, since the variables are likely of type `char *` – tay10r Jul 28 '13 at 19:41
  • @AndreasGrapentin no, it doesn't need `char **`. It wouldn't unless it was changing the value of the pointer itself (that usually happens if there was space being allocated). – tay10r Jul 28 '13 at 19:43
  • wow, I feel stupid now. sorry for talking nonsense! – Andreas Grapentin Jul 28 '13 at 19:45
  • @AndreasGrapentin it happens. I don't think anyone is going to figure out the problem until the OP posts more of the code – tay10r Jul 28 '13 at 19:46
  • I recommend posting an SSCCE: http://sscce.org/ – David Grayson Jul 28 '13 at 19:46
  • 1
    @PedroSilva there needs to be another byte in your arrays for the null-terminating character. Also, the `sscanf` call is different in your code than the line you wrote at the top of your question (there shouldn't be `&` in the last two parameters). Can you confirm whether or not this is causing problems in your program? – tay10r Jul 28 '13 at 19:47
  • 2
    `id[1],modelo[3],` --> `id[2],modelo[4],` , `"%d %d %s %s",&e,&m,&id,&modelo` --> `"%d %d %s %s",&e,&m, id, modelo` – BLUEPIXY Jul 28 '13 at 19:52
  • @TaylorFlores I added the bytes on the characters arrays and removed the &s form the two parameters. But I'm still getting only the integers. – Pedro Silva Jul 28 '13 at 19:57
  • @PedroSilva how do you know that you aren't getting the strings as well? Are you printf'ing them out? Can you show us? If you are still experiencing problems, it's not in any of the code you've shown us – tay10r Jul 28 '13 at 20:08

3 Answers3

2
char mystring[] = "2 7 A BUL"
x = strtok(mystring, " ");  //separates by spaces

More here Split string with delimiters in C

Community
  • 1
  • 1
paz
  • 1,155
  • 2
  • 11
  • 13
  • @TaylorFlores Why? Using `strtok()` is an even better solution than falling back to `scanf()` hackage. –  Jul 28 '13 at 20:26
  • It solves the problem better than sscanf in the case of the strings, especially when the data may not be 3 chars in length for `modelo`... Using `%3s` in an attempt to avoid buffer overflows is just asking for trouble. Strings and Xscanf just don't mix well... :-( –  Jul 28 '13 at 20:30
  • @H2CO3 `strtok` is probably a better solution, but it just doesn't answer why the OP is having the problem in the first place – tay10r Jul 28 '13 at 20:49
  • 2
    @TaylorFlores Well, yes, that's technically right but... if I wanted to be sarcastic, I'd say the OP is having the problem because he's not using the right tool for the task... If you get my point. –  Jul 28 '13 at 20:54
  • 1
    You have to convert the digits `'2'` and `'7'` to integers `2` and `7` like this. Granted, not hard, but `sscanf()` does that for you. (Plus I dislike `strtok()` — it is generally a plague-like function that cannot be used safely in library code. Use `strtok_s()` on Microsoft or `strtok_r()` on POSIX — that avoids the worst problems. You still can't tell what the delimiters were in scenarios where you have multiple delimiters possible because `strtok*()` zaps the delimiter. The fact that it carves up the string is the other reason I don't like the family of functions, but that is bearable.) – Jonathan Leffler Jul 28 '13 at 20:55
  • @JonathanLeffler I didn't want to split hair so I didn't get into a discussion about `strtok()` vs. `strtok_r()`, but in fact, I always prefer `strtok_r()`. –  Jul 28 '13 at 20:57
  • @H2CO3 in anycase, the OP (in a comment) said that he fixed the `&`s and array size but is still having the problem. Other than those errors, there's nothing wrong in the code he posted (so nobody can really give a valid answer). If I were to bet, I'd say that he's also using a `&` where he tries to `printf` the strings or there's just some other code he's not showing us that contains the error – tay10r Jul 28 '13 at 22:37
1

Given that you want to recognize 2 7 A BUL, you cannot safely use:

int e,m;
char id[1], modelo[3];

sscanf(frase[i], "%d %d %s %s", &e, &m, &id, &modelo);

First, you shouldn't pass char (*)[] values where a char * is expected; do not take the address of an array; pass id and modelo only. GCC will warn you about that if you turn on warnings. If you're learning C, you can't afford not to have the warnings turned on (use -Wall at minimum; -Wall -Wextra if at all possible).

Next, the first %s will read an arbitrary number of characters and null-terminate the result. This is going to overwrite the end of the id array. And you can't safely read 3 characters into modelo either. Because of this, you have two stack overflow problems.

You should write either:

int e, m;
char id[2], modelo[4];

if (sscanf(frase[i], "%d %d %1s %3s", &e, &m, id, modelo) != 4)
    ...oops...

or perhaps:

int e, m;
char id;
char modelo[4];

if (sscanf(frase[i], "%d %d %c %3s", &e, &m, &id, modelo) != 4)
    ...oops...

Or, you could use char id[1]; and %c, but that is dangerous; the result is not a null-terminated string.

Your primary input loop is suspect too. You can use feof() as you did, immediately after the fgets(), but it is much more conventional to test the result of fgets() itself; it tells you whether it succeeded or not. That code should probably be:

char frase[20][12];

for (int linha = 0; i < sizeof(frase) / sizeof(frase[0]); i++)
{
    if (fgets(frase[linha] ,sizeof(frase[linha]), stdin) == 0)
        break;
}

This avoids repeating the 20 or the 12 but protects you from too many lines. It does not protect you from overlong lines; you could add:

size_t len = strlen(frase[linha]);
assert(len != 0);
if (frase[len-1] != '\n')
    ...input was too long...

to the loop.

You could also think about doing the sscanf() and call to enfila() in the main input loop; you would not then need the frase array to be multi-dimensional.


Putting all the changes together:

char frase[4096];

while (fgets(frase, sizeof(frase), stdin) != 0)
{
    int e;
    int m;
    char id[2];
    char modelo[4];
    if (sscanf(frase, "%d %d %1s %3s", &e, &m, id, modelo) == 4)
        enfila(e, m, id, modelo, l);  // l is the parameter to the function
    else
        ...report error...
}

Using fgets() and sscanf() was definitely the correct way to go. It means that error reporting can show the whole line of input, rather than whatever mangled remains scanf() left behind as unreadable.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I'll just note that if Visual C++ is being used, you can turn on the warnings in the options (somewhere) or if you're using the MSVC command line, compile with `/Wall`. I just hope that doesn't still give warnings about the system header files like it used to. Then again, maybe I was compiling with `/W4` as well...because I thought it would give me more useful output like GCC's `-Wextra` option... –  Jul 28 '13 at 21:06
0

use instead strtok which is more simpler and IMHO more solid since you can do a simple add error handling

e.g.

int j = 0;
for (char* token = strtok(frase[i]," "; token != NULL; token = strtok(NULL," ")
{
  switch(j++)
  {
    case 0: 
      e = atoi(token); 
      break;
    case 1: 
      m = atoi(token); 
      break;
    case 2: 
      strncpy(id,token,sizeof(id)); // or strcpy_s
      break;
    case 3: 
      strncpy(modelo,token,sizeof(modelo));  // or strcpy_s
      break;
    default: 
      printf( "invalid number of arguments in line %d\n", i ); 
      break;
  }
}
AndersK
  • 35,813
  • 6
  • 60
  • 86
  • Wrong: leading blanks are ignored by `%s`, and the first blank after the first non-blank stops the parsing. The size of `id` is a problem; you must have enough space for the null terminator too, so the minimum size character array for `%1s` is `char id[2];`. – Jonathan Leffler Jul 28 '13 at 20:31