简体   繁体   中英

sscanf_s doesn't return first character of string

I'm trying to find the first string (max 4 characters) in a comma-separated list of strings inside a C char-array.

I'm trying to achieve this by using sscanf_s (under Windows) and the format-control string %[^,] :

char mystring[] = "STR1,STR2";
char temp[5];

if (sscanf_s(mystring, "%[^,]", temp, 5) != 0) {
    if (strcmp(temp, "STR1") == 0) { return 0; }
    else if (strcmp(temp, "STR2") == 0) { return 1; }
    else { return -1; }
}

After calling sscanf_s the content of temp is not STR1 but \0TR1 ( \0 being the ASCII-interpretation of 0 ). And the value -1 is returned.

Why do I get that behavior and how do I fix my code to get the right result (return of 0 )?

EDIT: changed char mystring to mystring[] (I should have made sure I typed it correcly here)

edited per the comment from chqrlie

regarding:

if(sscanf_s(mystring, "%[^,]",temp, 5) != 0){

The input format conversion specifier: %[..] always appends a NUL byte to the end of the input. So the input format conversion specifier should be: "%4[^,]" The result after the correction is:

if(sscanf_s(mystring, "%4[^,]",temp, 5) != 0){

also, no matter how many times this code snippet is executed, the returned value wnce the other problems are corrected will ALWAYS be STR1

regarding the statement;

char mystring = "STR1,STR2";

This is not a valid statement. Suggest:

char *mystring = "STR1,STR2";  // notice the '*'

--or--

char mystring[] = "STR1,STR2";  // notice the '[]'

There are multiple problems in your code:

  • mystring is defined as a char , not a string pointer.
  • the argument 5 following temp in sscanf_s() should have type rsize_t , which is the same as size_t . You should specify it as sizeof(temp) .
  • you should specify the maximum number of characters to store into the destination array in the format string, to avoid the counter-intuitive behavior of sscanf_s in case of overflow.
  • sscanf_s returns 1 if it can convert and store the string. Testing != 0 will also accept EOF which is an input failure, for which the contents of temp is indeterminate.

Here is a modified version:

const char *mystring = "STR1,STR2";
char temp[5];

if (sscanf_s(mystring, "%4[^,]", temp, sizeof temp) == 1) {
    if (strcmp(temp, "STR1") == 0) {
        return 0;
    } else
    if (strcmp(temp, "STR2") == 0) {
        return 1; 
    } else {
        return -1;
    }
}

UPDATE: The OP uses Microsoft Visual Studio, which seems to have a non-conforming implementation of the so-called secure stream functions. Here is a citation from their documentation page :

The sscanf_s function reads data from buffer into the location that's given by each argument. The arguments after the format string specify pointers to variables that have a type that corresponds to a type specifier in format. Unlike the less secure version sscanf , a buffer size parameter is required when you use the type field characters c , C , s , S , or string control sets that are enclosed in [] . The buffer size in characters must be supplied as an additional parameter immediately after each buffer parameter that requires it. For example, if you are reading into a string, the buffer size for that string is passed as follows:

 wchar_t ws[10]; swscanf_s(in_str, L"%9s", ws, (unsigned)_countof(ws)); // buffer size is 10, width specification is 9

The buffer size includes the terminating null. A width specification field may be used to ensure that the token that's read in will fit into the buffer. If no width specification field is used, and the token read in is too big to fit in the buffer, nothing is written to that buffer.

In the case of characters, a single character may be read as follows:

 wchar_t wc; swscanf_s(in_str, L"%c", &wc, 1);

This example reads a single character from the input string and then stores it in a wide-character buffer. When you read multiple characters for non-null terminated strings, unsigned integers are used as the width specification and the buffer size.

 char c[4]; sscanf_s(input, "%4c", &c, (unsigned)_countof(c)); // not null terminated

This example reads a single character from the input string and then stores it in a wide-character buffer. When you read multiple characters for non-null terminated strings, unsigned integers are used as the width specification and the buffer size.

 char c[4]; sscanf_s(input, "%4c", &c, (unsigned)_countof(c)); // not null terminated

This specification is incompatible with the C Standard, that specifies the type of the width arguments to be rsize_t and type rsize_t to be the same type as size_t .

As a conclusion, for improved portability, one should avoid using these secure functions and use the standard functions correctly, with the length prefix to prevent buffer overruns.

You can prevent the Visual Studio warning about deprecation of sscanf by inserting this definition before including <stdio.h> :

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS  // let me use standard functions
#endif

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM