2

With int snprintf(char *str, size_t size, const char *format, ...); I can write specific number of characters into str .

How can I read specific number of characters from a c-string ?

Regards

JAN
  • 21,236
  • 66
  • 181
  • 318
  • What exactly do you mean by "read from a c-string"? If you already have the string, you just access the array (taking the nul terminator into account) and read whatever you want. – sinelaw Jul 18 '12 at 10:53

5 Answers5

6

You can pass a number in the format argument:

char buf[21];
sscanf(str, "%20s", buf);

This reads up to 20 characters into the buf.

If you do not know how much data you are going to read at compile time, you can prepare the format string at runtime:

char format[20];
sprintf(format, "%%%ds", howMuchToRead);
sscanf(str, format, buf);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
4

Alternative to both sscanf and strncpy solutions you could also make use of precision specifier (like "%.20s" to print just 20 characters). Precision specifier for C strings has been discussed in this SO post. The steps are basically same as in sscanf solution, you create a format string & use it to capture the needed characters.
While using sscanf if you use the mentioned format specifier i.e. "%20s" and such, it is not guaranteed that you will get 20 characters if string contains a whitespace; if a whitespace is encountered then you will get the characters only till the whitespace (as whitespace is delimiter for scanf family of functions). Instead you should make use of something like %20[^\n]. [^\n] indicates read till newline whereas 20 specifies the number of characters to be scanned. All these can be seen in the following sample:

#include <stdio.h>
#include <string.h>

int main(void) {
        char format[20] = {0};
        char buf[50] = {0};
        char str[] = "Hello World! How are you?";
        size_t howMuchToRead = 8;

        /* Using sscanf - till whitespace or size specified */
        snprintf(format, sizeof format, "%%%zus", howMuchToRead);
        sscanf(str, format, buf);
        printf("Using sscanf with %%8s format :%s\n", buf);

        /* Using sscanf - read everything upto newline */
        snprintf(format, sizeof format, "%%%zu[^\n]", howMuchToRead);
        sscanf(str, format, buf);
        printf("Using sscanf with %%8[^\\n] format :%s\n", buf);

        /* Using precision specifier */
        snprintf(format, sizeof format, "%%.%zus", howMuchToRead);
        snprintf(buf, sizeof buf, format, str);
        printf("Using precision specifier :%s\n", buf);
        return 0;
}
/*
Output:
Using sscanf with %8s format :Hello
Using sscanf with %8[^\n] format :Hello Wo
Using precision specifier :Hello Wo
*/

And sinelaw's answer make very important points which you should be careful about.
Hope this helps!

Community
  • 1
  • 1
another.anon.coward
  • 11,087
  • 1
  • 32
  • 38
2

You can use either strncpy or memcpy. However, beware that strncpy may in some cases not append the terminating '\0' character, and that memcpy never does. Also, memcpy doesn't check for end of the string at all, it just copies the number of bytes you have asked it to.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
2

You can use sscanf:

int sscanf(const char *str, const char *format, ...);

For instance:

char out[4];
sscanf("foo", "%3s", &out);

Of course, you can also do some more interesting things:

int value;
sscanf("value=10", "value=%d", &value);
betabandido
  • 18,946
  • 11
  • 62
  • 76
2

sscanf, like the scanf function, is problematic. As the GNU manpage says:

There must be sufficient address arguments for the given format specifiers; if not the results are unpredictable and likely disasterous.

So there's one problem you need to avoid (the compiler can't verify for you that you're passing the right amount or types of arguments, although some compilers have a hack that warns you specifically about these functions). Part of this problem is also that you need to hard-code the maximum buffer size in the format string (which makes it harder to use an integer constant to prevent buffer overflows).

Since your use case is simply to copy up to a certain number of characters from a string, I'd just use strncpy (and be sure to set a terminating '\0') as suggested by Joachim Pileborg's answer. For the length parameter you should use the minimum length of both the input and output buffers, minus 1 to accommodate the terminating nul.

In other words (untested):

strncpy(dest, src, 
        MIN(NUM_OF_CHARS_TO_COPY, 
            MIN(DEST_BUFFER_SIZE, SRC_BUFFER_SIZE)) - 1);

where MIN(a,b) is equivalent to a < b ? a : b on integers.

sinelaw
  • 16,205
  • 3
  • 49
  • 80