The scanf_s family are secure versions of the standard scanf functions with enhanced security features to prevent buffer overruns. This guide focuses on sscanf_s and swscanf_s for parsing strings.
| Function | Description | Character Type |
|---|---|---|
sscanf_s |
Reads from narrow (char) string | Single-byte characters |
_sscanf_s_l |
Same as sscanf_s with locale parameter |
Single-byte characters |
swscanf_s |
Reads from wide (wchar_t) string | Wide characters |
_swscanf_s_l |
Same as swscanf_s with locale parameter |
Wide characters |
int swscanf_s(
const wchar_t *buffer, // Input string to parse
const wchar_t *format, // Format specification string
[argument] ... // Variables to store parsed values
);int sscanf_s(
const char *buffer, // Input string to parse
const char *format, // Format specification string
[argument] ... // Variables to store parsed values
);The input string containing data to be parsed.
Format control string that specifies how to interpret the input. Contains:
- Literal characters (must match exactly in input)
- Format specifiers (start with
%) - Whitespace (matches any amount of whitespace in input)
Variables where parsed values will be stored. Must be pointers to the appropriate type.
The locale to use for parsing numbers and dates.
| Return Value | Meaning |
|---|---|
| Positive integer | Number of fields successfully converted and assigned |
| 0 | No fields were assigned |
| EOF | Error occurred, or end of string reached before first conversion |
| -1 | buffer or format is NULL (invalid parameter) |
- Return value does NOT include fields that were read but not assigned
- A successful parse of "2023-01-01" with format
"%4d-%2d-%2d"returns 3 (three integers parsed)
%[*][width][length]type
Reads the field but does NOT assign it to a variable.
int year;
swscanf_s(L"2023-01-01", L"%4d-*2d-*2d", &year);
// Only year is stored, month and day are skippedMaximum number of characters to read for this field.
swscanf_s(L"2023", L"%4d", &year); // Read max 4 digits%c and %s, width specifies EXACT number of characters, not maximum!
See detailed type table below.
| Type | Description | Example Input | C Type | Size Arg? |
|---|---|---|---|---|
%d |
Decimal integer | "123", "-456" |
int* |
No |
%i |
Integer (auto-detects base) | "0x1F", "077", "99" |
int* |
No |
%u |
Unsigned decimal | "123" |
unsigned int* |
No |
%f |
Float/double | "3.14", "1.5e-3" |
float* |
No |
%s |
String (stops at whitespace) | "hello" |
char* or wchar_t* |
YES |
%c |
Character(s) | "a" |
char* or wchar_t* |
YES |
%x |
Hexadecimal integer | "1A", "0xFF" |
int* |
No |
%o |
Octal integer | "77", "012" |
int* |
No |
The _s (secure) versions REQUIRE a buffer size parameter for %c, %s, and character sets [].
- For strings (
%s): Buffer size includes the null terminator - For characters (
%c): Buffer size is the character count - Size parameter type:
unsigned int(NOTsize_t) - Size parameter position: Immediately after the buffer parameter
int year, month, day;
if (swscanf_s(L"2023-01-01", L"%4d-%2d-%2d", &year, &month, &day) == 3) {
// Successfully parsed all three values
// year = 2023, month = 1, day = 1
}How it works:
%4d- Read up to 4 decimal digits β year-- Expect a literal hyphen character%2d- Read up to 2 decimal digits β month-- Expect another literal hyphen%2d- Read up to 2 decimal digits β day- Returns 3 because three fields were assigned
wchar_t name[50];
// CORRECT: Buffer size is 50 (includes null terminator)
if (swscanf_s(L"John", L"%s", name, (unsigned)50) == 1) {
// name = L"John"
}
// With width specification (recommended)
if (swscanf_s(L"John", L"%49s", name, (unsigned)50) == 1) {
// Reads max 49 chars, leaves room for null terminator
}wchar_t ch;
// Size parameter is 1 (one character)
if (swscanf_s(L"A", L"%c", &ch, 1) == 1) {
// ch = L'A'
}
// Reading multiple characters
wchar_t chars[4];
if (swscanf_s(L"ABCD", L"%4c", chars, 4) == 1) {
// chars = {'A', 'B', 'C', 'D'}
// NOTE: NOT null-terminated!
}char buffer[100];
// Best practice: use _countof to get array size
sscanf_s(input, "%99s", buffer, (unsigned)_countof(buffer));wchar_t name[50];
int age;
float salary;
const wchar_t* input = L"Alice 30 55000.50";
int parsed = swscanf_s(input, L"%49s %d %f",
name, (unsigned)_countof(name),
&age,
&salary);
if (parsed == 3) {
// name = L"Alice"
// age = 30
// salary = 55000.50
}int value;
int result = swscanf_s(L"invalid", L"%d", &value);
if (result == 0) {
// No fields assigned - input doesn't match format
}
else if (result == EOF) {
// Error or end of string reached
}// WRONG - Missing size parameter for %s
wchar_t name[50];
swscanf_s(L"John", L"%s", name); // COMPILER ERROR!// CORRECT
wchar_t name[50];
swscanf_s(L"John", L"%s", name, (unsigned)50);// WRONG - Using size_t on 64-bit systems
wchar_t name[50];
swscanf_s(L"John", L"%s", name, _countof(name)); // May fail!// CORRECT - Cast to unsigned
wchar_t name[50];
swscanf_s(L"John", L"%s", name, (unsigned)_countof(name));// WRONG - Assuming parse succeeded
int year, month, day;
swscanf_s(dateStr, L"%4d-%2d-%2d", &year, &month, &day);
// Variables may contain garbage if parse failed!// CORRECT - Always check return value
int year, month, day;
if (swscanf_s(dateStr, L"%4d-%2d-%2d", &year, &month, &day) == 3) {
// Safe to use year, month, day
} else {
// Handle error - invalid format
}// For %c, width specifies EXACT character count, not maximum
wchar_t c;
swscanf_s(L"AB", L"%c", &c, 1); // Reads only 'A'
swscanf_s(L"AB", L"%2c", &c, 1); // WRONG - tries to read 2 chars into 1 space!| Feature | sscanf |
sscanf_s |
|---|---|---|
| Buffer overflow protection | β No | β Yes |
| Requires size for %c, %s | β No | β Yes |
| Security | Less secure | More secure |
| Complexity | Simpler syntax | Requires size parameters |
| Recommended for new code | β No | β Yes |
Any whitespace character in the format string matches any amount of whitespace in the input (including none).
int a, b;
// These are all equivalent:
swscanf_s(L"10 20", L"%d %d", &a, &b);
swscanf_s(L"10 20", L"%d%d", &a, &b);
swscanf_s(L"10 20", L"%d %d", &a, &b); // Multiple spacesNon-whitespace, non-% characters must match exactly.
int year, month;
// Expects exactly "Year: XXXX Month: XX"
swscanf_s(L"Year: 2023 Month: 12", L"Year: %d Month: %d", &year, &month);If buffer or format is NULL:
- Invalid parameter handler is invoked
- If execution continues, function returns
-1 errnois set toEINVAL
If end of string is reached before any conversion:
- Returns
EOF
If a field cannot be converted:
- Parsing stops at that field
- Returns count of successfully assigned fields
- Failed field and remaining fields are not assigned
- Always check the return value against expected field count
- Use
_countof()for array sizes and cast to(unsigned) - Use width specifications to limit input size
- Initialize variables before parsing (in case of failure)
- Use appropriate function for your character type (
sscanf_sforchar,swscanf_sforwchar_t)
- Don't forget size parameters for
%c,%s, or[] - Don't use
size_tfor size parameters (useunsigned) - Don't assume parsing succeeded without checking return value
- Don't mix narrow and wide character functions/strings
- Header:
<stdio.h> - Minimum: Windows 2000 Professional
- Header:
<stdio.h>or<wchar.h> - Minimum: Windows 2000 Professional