6

I am trying to save one character and 2 strings into variables. I use sscanf to read strings with the following form :

N "OldName" "NewName"

What I want : char character = 'N' , char* old_name = "OldName" , char* new_name = "NewName" .

This is how I am trying to do it :

sscanf(mystring,"%c %s %s",&character,old_name,new_name);
printf("%c %s %s",character,old_name,new_name);

The problem is , my problem stops working without any outputs . (I want to ignore the quotation marks too and save only its content)

5
  • How did you declare old_name and new_name? I bet char* old_name; Commented Apr 21, 2016 at 12:23
  • char* old_name , char* new_name ; Commented Apr 21, 2016 at 12:24
  • You have to allocate space for your strings, at least using malloc Commented Apr 21, 2016 at 12:24
  • 1
    You can use a formatting string of "%c \"%[^\"]\" \"%[^\"]\"". Commented Apr 21, 2016 at 12:27
  • @SouravGhosh That won't do. See my comment . unwind's ↑ approach is correct. Commented Apr 21, 2016 at 13:23

3 Answers 3

6

When you do

char* new_name = "NewName";

you make the pointer new_name point to the read-only string array containing the constant string literal. The array contains exactly 8 characters (the letters of the string plus the terminator).

First of all, using that pointer as a destination for scanf will cause scanf to write to the read-only array, which leads to undefined behavior. And if you give a string longer than 7 character then scanf will also attempt to write out of bounds, again leading to undefined behavior.

The simple solution is to use actual arrays, and not pointers, and to also tell scanf to not read more than can fit in the arrays. Like this:

char old_name[64];  // Space for 63 characters plus string terminator
char new_name[64];

sscanf(mystring,"%c %63s %63s",&character,old_name,new_name);

To skip the quotation marks you have a couple of choices: Either use pointers and pointer arithmetic to skip the leading quote, and then set the string terminator at the place of the last quote to "remove" it. Another solution is to move the string to overwrite the leading quote, and then do as the previous solution to remove the last quote.

Or you could rely on the limited pattern-matching capabilities of scanf (and family):

sscanf(mystring,"%c \"%63s\" \"%63s\"",&character,old_name,new_name);

Note that the above sscanf call will work iff the string actually includes the quotes.

Second note: As said in the comment by Cool Guy, the above won't actually work since scanf is greedy. It will read until the end of the file/string or a white-space, so it won't actually stop reading at the closing double quote. The only working solution using scanf and family is the one below.

Also note that scanf and family, when reading string using "%s" stops reading on white-space, so if the string is "New Name" then it won't work either. If this is the case, then you either need to manually parse the string, or use the odd "%[" format, something like

sscanf(mystring,"%c \"%63[^\"]\" \"%63[^\"]\"",&character,old_name,new_name);
Sign up to request clarification or add additional context in comments.

2 Comments

Already a good answer (+1) but how does OP read in the string without picking up the quotation marks? (I want to ignore the quotation marks...
sscanf(mystring,"%c \"%63s\" \"%63s\"",&character,old_name,new_name); won't work. Remember: %s doesn't stop scanning on seeing a "!
2

You must allocate space for your strings, e.g:

char* old_name = malloc(128); 
char* new_name = malloc(128);

Or using arrays

char old_name[128] = {0};
char new_name[128] = {0};

In case of malloc you also have to free the space before the end of your program.

free(old_name);
free(new_name);

2 Comments

Worked like charm ! Is there any way to read the content of the quotation marks , without including them ? I mean , the result of printf to be : NewName and not "NewName"
@kata You mean read them by scanf or outputting them by printf?
2

Updated:...

The other answers provide good methods of creating memory as well as how to read the example input into buffers. There are two additional items that may help:

1) You expressed that you want to ignore the quotation marks too.
2) Reading first & last names when separated with space. (example input is not)

As @Joachim points out, because scanf and family stop scanning on a space with the %s format specifier, a name that includes a space such as "firstname lastname" will not be read in completely. There are several ways to address this. Here are two:

Method 1: tokenizing your input.
Tokenizing a string breaks it into sections separated by delimiters. Your string input examples for instance are separated by at least 3 usable delimiters: space: " ", double quote: ", and newline: \n characters. fgets() and strtok() can be used to read in the desired content while at the same time strip off any undesired characters. If done correctly, this method can preserve the content (even spaces) while removing delimiters such as ". A very simple example of the concept below includes the following steps:
1) reading stdin into a line buffer with fgets(...)
2) parse the input using strtok(...).

Note: This is an illustrative, bare-bones implementation, sequentially coded to match your input examples (with spaces) and includes none of the error checking/handling that would normally be included.

int main(void)
{
    char line[128];
    char delim[] = {"\n\""};//parse using only newline and double quote
    char *tok;

    char letter;
    char old_name[64];  // Space for 63 characters plus string terminator
    char new_name[64];

    fgets(line, 128, stdin);
    tok = strtok(line, delim);     //consume 1st " and get token 1 
    if(tok) letter = tok[0];       //assign letter
    tok = strtok(NULL, delim);     //consume 2nd " and get token 2
    if(tok) strcpy(old_name, tok); //copy tok to old name
    tok = strtok(NULL, delim);     //consume 3rd " throw away token 3
    tok = strtok(NULL, delim);     //consume 4th " and get token 4
    if(tok) strcpy(new_name, tok); //copy tok to new name

    printf("%c %s %s\n", letter, old_name, new_name);


    return 0;
}

Note: as written, this example (as do most strtok(...) implementations) require very narrowly defined input. In this case input must be no longer than 127 characters, comprised of a single character followed by space(s) then a double quoted string followed by more space(s) then another double quoted string, as defined by your example:

N "OldName" "NewName"

The following input will also work in the above example:

N    "old name"             "new name"

N "old      name" "new        name"

Note also about this example, some consider strtok() broken, while others suggest avoiding its use. I suggest using it sparingly, and only in single threaded applications.

Method 2: walking the string.
A C string is just an array of char terminated with a NULL character. By selectively copying some characters into another string, while bypassing the one you do not want (such as the "), you can effectively strip unwanted characters from your input. Here is an example function that will do this:

 char * strip_ch(char *str, char ch) 
 {

    char *from, *to;
    char *dup = strdup(str);//make a copy of input

    if(dup)
    {
        from = to = dup;//set working pointers equal to pointer to input
        for (from; *from != '\0'; from++)//walk through input string 
        {
            *to = *from;//set destination pointer to original pointer 
            if (*to != ch) to++;//test - increment only if not char to strip
                                //otherwise, leave it so next char will replace
        }
        *to = '\0';//replace the NULL terminator
        strcpy(str, dup);
        free(dup);
    }
    return str; 
}

Example use case:

int main(void)
{
    char line[128] = {"start"};

    while(strstr(line, "quit") == NULL)
    {
        printf("Enter string (\"quit\" to leave) and hit <ENTER>:");
        fgets(line, 128, stdin);
        sprintf(line, "%s\n", strip_ch(line, '"')); 
        printf("%s", line);
    }
    return 0;   
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.