How to pass variable length width specifier in sscanf? - c

sscanf(input_str, "%5s", buf); //reads at max 5 characters from input_str to buf
But I need to use something like %MACRO_SIZEs instead of %5s
A trivial solution is to create a format string for the same
char fmt_str[100] = "";
snprintf(fmt_str, 100, "%%%ds", MACRO_SIZE);
sscanf(input_str, fmt_str, buf);
Is there a better way to achieve the same?

Like Stefan said, but for sscanf() to more properly answer the question, and with a bit more macro trickery:
#define MACRO_SIZE 5
#define FORMAT(S) "%" #S "s"
#define RESOLVE(S) FORMAT(S)
char buf[MACRO_SIZE + 1];
sscanf(input_str, RESOLVE(MACRO_SIZE), buf);
This uses C's automatic joining together of adjacent string literals, to form the required formatting string at compile-time. This only works if MACRO_SIZE is a preprocessor macro, not if it's a normal runtime variable.
The extra macro call through RESOLVE() is needed since otherwise the argument would not be resolved to its #defined value, and we'd end up with a formatting string of "%MACRO_SIZEs", which is not what we want.

if your MACRO_SIZE is const at compile time, you can try this:
#define MACRO_SIZE "5"
snprintf(fmt_str, 100, "%" MACRO_SIZE "s", buf);

The "correct" solution is what you call the trivial one. All these clever macros(I'd use m4 myself) are just going to make your code less manageable then if you just left it as a constant.
The problem you have here is strings are not a first class data structure in C. They are an array of bytes. Therefor you have to build the array you want to get the meaning you want, and you build that array with sprintf. It's not pretty, but it's correct.
If you're having performance issues and you've tracked it down to here then yes, eliminate the function calls. But unless the value for MACRO_SIZE is repeated a hundred times or spread out over multiple files I'd just change the literal. A macro is just faking having more flexibility, using sprintf actually gives you flexibility.

Related

How to use a define inside a format string?

Say I have a character array:
#define LEN 10
char arr[LEN + 1];
Lets do some scanf operation to it:
scanf("Name: %s", arr);
This could be dangerous if someone is typing a name that is longer than 10 characters. So better use this:
scanf("Name: %10s", arr);
Well now I would run into trouble if LEN is changed. I would have to go through the whole code to correct every line where I used the 10 in context of arr. So I thought about somehting like this:
scanf("Name: %LENs", arr);
But this will not work.LEN is not resolved by the preprocessor beacuse it is used inside a string.
How to use a define inside a format string?
C joins adjacent string literals and you can stringify a preprocessor parameter with #, so the following should do the trick:
#define LEN 10
// this converts to string
#define STR_(X) #X
// this makes sure the argument is expanded before converting to string
#define STR(X) STR_(X)
[...]
scanf("Name: %" STR(LEN) "s", arr);
The macros are needed because with just #LEN, you'd end up with LEN expanded to 10, and with only one macro applying # to its argument, the result would be "LEN" (the argument wouldn't be expanded).
The preprocessor / compiler will transform this in the following steps:
1. scanf("Name: %" STR_(10) "s", arr);
2. scanf("Name: %" "10" "s", arr);
3. scanf("Name: %10s", arr);
In the last step, the string literals are joined into a single one.
On a side note, your scanf() format string would require the user to literally enter
Name: xyz
to actually match. I doubt this is what you wanted. You probably want something like this:
fputs("Name: ", stdout);
fflush(stdout);
scanf("%" STR(LEN) "s", arr);
Also consider not using scanf() at all. With e.g. fgets(), this whole preprocessor magic is obsolete. For reasons why you shouldn't use scanf(), see my beginners' guide away from scanf().

C define string as char

Is it possible to define a string as char in C like this? I think C calls it multi character constant.
#define OK '_/'
I want C to treat '_/' as a char from now on, not a string, so this:
printf("%c", OK);
prints _/ and not /
While it is technically valid C to define OK as '_/', the value of a multi-character character constant is implementation defined, so this is probably not something you want to do.
There is no way you will be able to print more than one character without resorting to strings.
Multi character constants are of int type and their value is not strictly defined-- it's platform dependent stuff. So using them as normal letters is not best idea, even though you can use them in every context as normal char there is no guarantee that they will be compiled as you intend (as in your example you get only last char from ur string).
here you have explanation of the topic:
Multiple characters in a character constant

Can I use sprintf () with multiple #define token strings

I have two strings that may be changing independently. I thought to make them #defines so I don't have to change it all over my code. I'm having problems putting them together with sprintf().
So, below, if VoltageSet was 2.2, I want to put the string "sour:volt:2.2\r" into the cmd buffer.
(FYI- SOUR:VOLT:2.2 is a SCPI command to an instrument, and \r is a carriage return to terminate the message)
In my header:
#define SETVOLTAGELEVEL "SOUR:VOLT:"
#define TERMINATIONCHAR "\r"
In my c code:
int SetVoltageLevel (double VoltageSet)
{
char cmd[255]={0};
sprintf(cmd, "SETVOLTAGELEVEL%fTERMINATIONCHAR", VoltageSet);
}
Am I over thinking this? Is there an easier way?
sprintf(cmd, SETVOLTAGELEVEL "%f" TERMINATIONCHAR, VoltageSet);
or
sprintf(cmd, "%s%f%s", SETVOLTAGELEVEL, VoltageSet, TERMINATIONCHAR);
I don't quite get what you want to achieve this with this behavior (An XY problem?), but you can concatenate string literals by writing them one after another:
sprintf(cmd, SETVOLTAGELEVEL "%f" TERMINATIONCHAR, VoltageSet);
But...
Why?
Very risky. You not only fail to use snprintf() instead of sprintf(), but you may also run into format string errors in case the two macros define more or less format specifiers than you actually supply arguments to the function.
There is nothing wrong here. Preprocessor i.e. define will be replaced by its value before compilation. So you wont see any difference.
#BLUEPIXY has pointed out one way. Here is something closer to what you had in mind. Note that what you have does not work because the preprocessor does not expand tokens inside a string. But you can use string concatenation. Good to know.
int SetVoltageLevel (double VoltageSet)
{
char cmd[255]={0};
sprintf(cmd, SETVOLTAGELEVEL "%f" TERMINATIONCHAR, VoltageSet);
}

Is there an easy way to concatenate string non-literals with literals?

I would like to be able to do this:
lcd_putc("\fDeposited $" & disp_money & "\nAdd $" & temp & " more");
Unfortunately, the string literals and non-literals don't concatenate that easily. I know how to concatenate two literals, and how to concatenate two non-literals (with strcat) but that's not really what I'm looking for. Can anyone help?
sprintf() and snprintf() are good for this.
strcat is the way to do it. For more advanced concatenation, consider sprintf:
sprintf (buf, "\fDeposited $%s\nAdd $%s more", disp_money, temp);
char *buf = (char*)calloc(1,512);
sprintf(buf, "\fDeposited $%0.2d\nAdd $%0.2d more", disp_money, temp);
?
I think you might be searching for sprintf
Example:
char string[50];
int file_number = 0;
sprintf( string, "file.%d", file_number );
file_number++;
output_file = fopen( string, "w" );
Memory management in C is manual. To concatenate the strings, you will need to provide an array variable with enough characters to hold the final result. You can allocate this buffer variable on the stack, as a local variable to your function, or on the heap, using malloc().
Or you can avoid allocating the buffer by avoiding performing concatenation, if what you intend to do is display the strings. In that case, the following:
lcd_putc("\fDeposited $");
lcd_putc(disp_money);
lcd_putc("\nAdd $");
lcd_putc(temp);
lcd_putc(" more");
is a simple way to write what you could use. This method has the disadvantage of incurring the overhead (if any) due to lcd_putc(). This method has the advantage of not require concatenation of strings.
If and when you do need to concatenate strings, you will want to use snprintf() to ensure that you do not overflow your buffer (see the name of this web site), so do not use sprintf().
Just to show you the way for that future day when you need to concatenate strings:
#define STR_DEPOSITED "\fDeposited $"
#define STR_ADD "\nAdd $"
#define STR_MORE " more"
int total_length = strlen(STR_DEPOSITED) + strlen(STR_ADD) + strlen(STR_MORE) + strlen(disp_money) + strlen(temp) + 1;
char * buffer = malloc(total_length + sizeof(char));
snprintf(buffer, "%s%s%s%s%s", STR_DEPOSITED, disp_money, STR_ADD, temp, STR_MORE);
You can also accomplish the same thing using strncpy(), strncat(). As a side note, also consider using strnlen() on the variables, in order not to read beyond the end of non-terminated buffer.
Strings are not first class objects in C, so there is no language support for concatenation in the way you describe. There are, however, a lot of different functions that you can call that give you the output you need.
Probably the easiest to work with is sprintf. Be aware that you must supply an output buffer large enough to hold the resultant string, and sprintf doesn't do any bounds checking, if you have access to snprintf or sprintf_s then use that instead.
char output[SUFFICIENTLY_LARGE_VALUE];
sprintf (output, "\fDeposited $%s\nAdd $%s more", disp_money, temp);
%s in the sprintf string represents a place where a string parameter will be inserted, the first % is the first argument (disp_money) and the second % is the second argument (temp).

Help gcc to not warn about not using a string literal format string

I'm creating a function in C to convert an index value into a string, which is a verbose description of the "field" represented by the index.
So, I have a nice array with all the verbose descriptions indexed by, well the index.
To dump it into a buffer I use code like this
#define BUFFER_SIZE 40
void format_verbose(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
{
snprintf(mY_buffer, BUFFER_SIZE, "%s", MY_ARRAY[my_index].description);
}
The problem comes for some cases I need to insert some other strings into the string when formatting it. So what I want is something like this (where the description in this case contains a %s).
void format_verbose_with_data(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
{
// ...
snprintf(mY_buffer, BUFFER_SIZE, MY_ARRAY[my_index].description,
some_string);
}
Our make file is set up to make this (dangerous) use of snprintf() warn, and warnings are treated as errors. So, it won't compile. I would like to turn off the warning for just this line, where although it is somewhat dangerous, I will control the string, and I can test to ensure it works with every value it's called with.
Alternatively, I would be happy to do this some other way, but I'm really not keen to use this solution
void format_verbose_with_data(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
{
// ...
snprintf(mY_buffer, BUFFER_SIZE, "%s%s%s"
MY_ARRAY[my_index].description1, some_string,
MY_ARRAY[my_index].description2);
}
Because it makes my description array ugly, especially for the ones where I don't need to add extra values.
GCC doesn't have the ability to turn off warnings on a line by line basis, so I suspect you are out of luck. And anyway, if your coding standards say you shouldn't be doing something, you should not be looking for ways to defeat them.
Another point, when you say:
void format_verbose(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
you are really wasting your time typing - it is clearer and more idiomatic to say:
void format_verbose(uint32_t my_index,
char my_buffer[])
or:
void format_verbose(uint32_t my_index,
char * my_buffer)
If all you ever do with snprintf() is copying strings (as seems to be the case), i.e. all you ever have is one or more "%s" as format string, why are you not using strcpy(), perhaps with a strlen() first to check the source's length?
Note that strncpy(), while looking like a good idea, isn't. It always pads the target buffer with zeroes, and in case the source exceeds the buffer size, doesn't null-terminate the string.
After a night of thought, I plan on manually dividing the input string up, by looking for the %s marker myself, and then sending the strings separetley into snprintf() with their own %s. For this case, where only 1 type of format string is allowed, this less onerous, however it would suck to try to completely re-implement a printf() style parser.
void format_verbose_with_data(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
{
char pre_description[BUFFER_SIZE];
char post_description[BUFFER_SIZE];
int32_t offset = -1;
offset = find_string(MY_ARRAY[my_index].description, "%s");
ASSERT(offset >=0, "No split location!");
// Use offset to copy the pre and post descriptions
// Exercise left to the reader :-)
snprintf(mY_buffer, BUFFER_SIZE, "%s%s%s"
pre_description, some_string, post_description);
}

Resources