-3

I'm trying to use scanf with a char pointer to store a string. In case of ENTER key stroke, I want it to be catched. I know I could use fgets but I'm curious to know if it's possible so I managed to write the following code:

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

int main(void)
{
    char *string = NULL;

    printf("Type anything : ");

    scanf("%*[^\n]%10[^\n]ms", string);

    printf("Your input : %s\n", string);

    free(string);

    return EXIT_SUCCESS;
}

But doesn't seems to work well. Here's the output :

Type anything : 123
Your input : (null)
Type anything : 
Your input : (null)

How can I make my program to display the string correctly ?

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
Amin NAIRI
  • 2,292
  • 21
  • 20
  • 1
    1) Tehere is no regex in your code. `scanf` does not support them. 2) Please learn how memory allocaltion works in C. Your code shows major missconception. 3) Your code invokes _undefined behaviour_ (serach for it!) – too honest for this site Jul 02 '16 at 10:49
  • 2
    `scanf("%*[^\n]%10[^\n]ms", string);` --> `scanf("%m[^\n]%*c", &string);` with glibc. – BLUEPIXY Jul 02 '16 at 10:53
  • @BLUEPIXY Care to elaborate what you mean? – Kusalananda Jul 02 '16 at 10:57
  • 1
    The null string remains null. You need to preallocate the string yourself. – Paul Stelian Jul 02 '16 at 10:58
  • 2
    @Kusalananda see [Example](http://linux.die.net/man/3/sscanf). glibc supports the `m`. OP knows `m` but wrong usage. – BLUEPIXY Jul 02 '16 at 11:03
  • @BLUEPIXY Tho OP does not mention glibc. He does, however, use `scanf()` with a `NULL` value (having failed to allocate `string`). He also frees an unallocated pointer. – Kusalananda Jul 02 '16 at 11:18
  • @BLUEPIXY thanks it worked that way. Could you explain why am I seing %*c but the resultat gives me a nice text (not that it bothers me) ? – Amin NAIRI Jul 02 '16 at 11:18
  • @Kusalananda Thanks a lot for the link. Gaves me some intel. Should have scrolled a little bit more. Next time i'll do. – Amin NAIRI Jul 02 '16 at 11:20
  • 2
    `%*c` : It will consume the newline. (Since `[^\n]` does not accept the newline) – BLUEPIXY Jul 02 '16 at 11:23

2 Answers2

1

The first thing you need to know before I explain your issue is that %[^\n] will fail if the first character to be read is a \n.

Now lets analyze what scanf("%*[^\n]%10[^\n]ms", string); means:

  1. %*[^\n] scans everything until a newline character, but doesn't scan in the final \n.
  2. %10[^\n]ms doesn't really do what you think it does:

    • Remember! %s and %[ are two different format specifiers! The trailing s is not part of the %[ specifier.
    • You got the m at the wrong position.


    Correcting these, it should be %10m[^\n] which scans everything until a newline character or a maximum of 10 characters, whichever happens first, and also allocates enough memory to hold this string.

Another bug is that m expects a char**, not a char* and so, you should be supplying &string and not just string.

Now, lets execute your scanf (after correcting the above mistakes)!

  1. Execution reaches the scanf and scanf waits for input.
  2. You type 123\n.
  3. %*[^\n] scans and discards the 123, sees the \n, and stops scanning, not consuming the \n.
  4. %10m[^\n] sees the \n and fails for the reason given in the first sentence of my answer.
  5. scanf returns 0 (since nothing was scanned and assigned successfully) and scanf completes execution.
  6. printf prints (null) since string is still NULL.

Ok, so we now saw what went wrong. The real question is, why do you have %*[^\n] at the start of scanf for this small program? Remove it and everything will work as expected!


Suggestions:

  1. Add scanf("%*[^\n]"); getchar(); after the scanf so that the rest of the input (if any) along with the final \n gets popped out of the stdin and future reads will not be problematic.
  2. Check the return value of scanf to see if it was successful or not. RTFM to know what it returns.

Fixed code (Untested)

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

int main(void)
{
    char *string = NULL;

    printf("Type anything : ");

    if(scanf("%10m[^\n]", &string) != 1)
    {
        fputs("Uh, oh! `scanf` failed! Exiting...\n", stderr);
        return EXIT_FAILURE;
    }
    scanf("%*[^\n]");
    getchar();

    printf("Your input : %s\n", string);

    free(string);

    return EXIT_SUCCESS;
}

This answer assumes that your implementation supports the m format specifier

Spikatrix
  • 20,225
  • 7
  • 37
  • 83
  • Thanks, it worked. What would you suggest if I ever wanted to manage the case "user type enter key right away" ? – Amin NAIRI Jul 02 '16 at 13:01
  • Good question. You could use `if(scanf("%10m[^\n]", &string) != 1){ if(getchar() == '\n'){ string = malloc(2); if(!string) { fputs("Uh, oh! \`malloc\` failed! Exiting...\n", stderr); return EXIT_FAILURE; } strcpy(string, " "); } else { fputs("Uh, oh! \`scanf\` failed! Exiting...\n", stderr); return EXIT_FAILURE; } else if(string != NULL){ scanf("%*[^\n]"); getchar(); } else { puts("EOF, exiting..."); return EOF; }`. Note: You need `string.h` for `strcpy`. – Spikatrix Jul 02 '16 at 13:13
  • Warning: Untested Code ↑ – Spikatrix Jul 02 '16 at 13:20
0

The [ format specifier (conversion, really) for scanf() specifies a character range of accepted (or, if it starts with [^, rejected) characters. It is not a regular expression.

You need to either declare string as a fixed-length array, or use malloc() to allocate memory for it.

The following modified version uses a fixed-length string of 99 characters plus a string terminating \0 (i.e. 100 char long):

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

int main(void)
{
        char string[100];

        printf("Type anything : ");

        scanf("%99s", string);

        printf("Your input : %s\n", string);

        return EXIT_SUCCESS;
}

The following variation of the code uses a dynamic array for doing the same thing:

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

int main(void)
{
        char *string;

        string = malloc(100);

        if (string == NULL) {
                puts("Something went wrong");
                abort();
        }

        printf("Type anything : ");

        scanf("%99s", string);

        printf("Your input : %s\n", string);

        free(string);

        return EXIT_SUCCESS;
}

To use regular expressions in C un a Unix system, look up the regcomp() function declared in the regex.h header.

Kusalananda
  • 14,885
  • 3
  • 41
  • 52
  • `"%s.99"` --> `"%99[^\n]"` (or `"%99s"`) – BLUEPIXY Jul 02 '16 at 11:19
  • @BLUEPIXY Thanks, `%s.99` should be `%.99s` (fixed). `%99s` would allow the allocated buffer to be overflown, while `%.99s` truncates input to 99 characters. `%99[^\n]` would have the same issue with possibly overrunning the buffer, and a newline will not be read into the string in any case. – Kusalananda Jul 02 '16 at 11:24
  • 1
    Thanks for the answer, although it's not exactly what I was asking for. But lot's of useful informations here. I do already know about static and dynamically allocated arrays. – Amin NAIRI Jul 02 '16 at 11:25
  • @Gradiuss Well, the main issue with the code was exactly that (allocation). – Kusalananda Jul 02 '16 at 11:28
  • @BLUEPIXY Sigh, you're right. I'll fix it. Mixing up formats for `scanf()` and `printf()`... – Kusalananda Jul 02 '16 at 11:31
  • @BLUEPIXY BTW, although slightly annoyed for being asked to "RTFM", I'm happy you pointed me to the `[` format specifier as it was unknown to me. Thanks. – Kusalananda Jul 02 '16 at 11:35
  • 1
    There is no UB. `free(NULL)` is perfectly valid and is a no-op – Spikatrix Jul 02 '16 at 12:23