I'm building a string piece by piece in my program and am currently using a mix of strcat() when I'm adding a simple string onto the end, but when im adding a formatted string I'm using sprintf() e.g.:
int one = 1;
sprintf(instruction + strlen(instruction), " number %d", one);
is it possible to concatenate formatted string using strcat() or what is the preferred method for this?
Your solution will work. Calling strlen is a bit awkward (particularly if the string gets quite long). sprintf() will return the length you have used [strcat won't], so one thing you can do is something like this:
char str[MAX_SIZE];
char *target = str;
target += sprintf(target, "%s", str_value);
target += sprintf(target, "somestuff %d", number);
if (something)
{
target += sprintf(target, "%s", str_value2);
}
else
{
target += sprintf(target, "%08x", num2);
}
I'm not sure strcat is much more efficient than sprintf() is when used in this way.
Edit: should write smaller examples...
no it's not possible but you could use sprintf() on those simple strings and avoid calling strlen() every time:
len = 0;
len += sprintf(buf+len, "%s", str);
len += sprintf(buf+len, " number %d", one);
To answer the direct question, sure, it's possible to use strcat to append formatted strings. You just have to build the formatted string first, and then you can use strcat to append it:
#include <stdio.h>
#include <string.h>
int main(void) {
char s[100];
char s1[20];
char s2[30];
int n = 42;
double x = 22.0/7.0;
strcpy(s, "n = ");
sprintf(s1, "%d", n);
strcat(s, s1);
strcat(s, ", x = ");
sprintf(s2, "%.6f", x);
strcat(s, s2);
puts(s);
return 0;
}
Output:
n = 42, x = 3.142857
But this is not a particularly good approach.
sprintf works just as well writing to the end of an existing string. See Mats's answer and mux's answer for examples. The individual arrays used to hold individual fields are not necessary, at least not in this case.
And since this code doesn't keep track of the end of the string, the performance is likely to be poor. strcat(s1, s2) first has to scan s1 to find the terminating '\0', and then copy the contents of s2 into it. The other answers avoid this by advancing an index or a pointer to keep track of the end of the string without having to recompute it.
Also, the code makes no effort to avoid buffer overruns. strncat() can do this, but it just truncates the string; it doesn't tell you that it was truncated. snprintf() is a good choice; it returns the number of characters that it would have written if enough space were available. If this exceeds the size you specify, then the string was truncated.
/* other declarations as above */
size_t count;
count = snprintf(s, sizeof s, "n = %d, x = %.6f", n, x);
if (count > sizeof s) {
/* the string was truncated */
}
And to append multiple strings (say, if some are appended conditionally or repeatedly), you can use the methods in the other answers to keep track of the end of the target string.
So yes, it's possible to append formatted strings with strcat(). It's just not likely to be a good idea.
What the preferred method is, depends on what you are willing to use. Instead of doing all those manual (and potentially dangerous) string operations, I would use the GString data structure from GLib or GLib's g_strdup_print function. For your problem, GString provides the g_string_append_printf function.
Write your own wrapper for your need.
A call to this would look like this :-
result = universal_concatenator(4,result,"numbers are %d %f\n",5,16.045);
result = universal_concatenator(2,result,"tail_string");
You could define one function, that would take care of worrying about, if you need to use sprintf() or strcat(). This is what the function would look like :-
/* you should pass the number of arguments
* make sure the second argument is a pointer to the result always
* if non formatted concatenation:
* call function with number_of_args = 2
* else
* call function with number of args according to format
* that is, if five inputs to sprintf(), then 5.
*
* NOTE : Here you make an assumption that result has been allocated enough memory to
* hold your concatenated string. This assumption holds true for strcat() or
* sprintf() of your previous implementation
*/
char* universal_concaternator(int number_of_args,...)
{
va_list args_list;
va_start(args_list,number_of_args);
int counter = number_of_args;
char *result = va_arg(args_list, char*);
char *format;
if(counter == 2) /* it is a non-formatted concatenation */
{
result = strcat(result,va_arg(args_list,char*));
va_end(args_list);
return result;
}
/* else part - here you perform formatted concatenation using sprintf*/
format = va_arg(args_list,char*);
vsprintf(result + strlen(result),format,args_list);
va_end(args_list);
return result;
}
/* dont forget to include the header
* <stdarg.h> #FOR-ANSI
* or <varargs.h> #FOR-UNIX
*/
It should firstly, determine, which of the two it should call(strcat or sprintf), then it should make the call, and make it easy for you to concentrate on the actual logic of whatever you are working on!
Just ctrl+c code above and ctrl+v into your code base.
Note : Matt's answer is a good alternative for long strings. But for short string lengths(<250), this should do.
Related
I have problem with splitting a string. The code below works, but only if between strings are ' ' (spaces). But I need to split strings even if there is any whitespace char. Is strtok() even necessary?
char input[1024];
char *string[3];
int i=0;
fgets(input,1024,stdin)!='\0') //get input
{
string[0]=strtok(input," "); //parce first string
while(string[i]!=NULL) //parce others
{
printf("string [%d]=%s\n",i,string[i]);
i++;
string[i]=strtok(NULL," ");
}
A simple example that shows how to use multiple delimiters and potential improvements in your code. See embedded comments for explanation.
Be warned about the general shortcomings of strtok() (from manual):
These functions modify their first argument.
These functions cannot be used on constant strings.
The identity of the delimiting byte is lost.
The strtok() function uses a static buffer while parsing, so it's not thread
safe. Use strtok_r() if this matters to you.
#include <stdio.h>
#include<string.h>
int main(void)
{
char input[1024];
char *string[256]; // 1) 3 is dangerously small,256 can hold a while;-)
// You may want to dynamically allocate the pointers
// in a general, robust case.
char delimit[]=" \t\r\n\v\f"; // 2) POSIX whitespace characters
int i = 0, j = 0;
if(fgets(input, sizeof input, stdin)) // 3) fgets() returns NULL on error.
// 4) Better practice to use sizeof
// input rather hard-coding size
{
string[i]=strtok(input,delimit); // 5) Make use of i to be explicit
while(string[i]!=NULL)
{
printf("string [%d]=%s\n",i,string[i]);
i++;
string[i]=strtok(NULL,delimit);
}
for (j=0;j<i;j++)
printf("%s", string[i]);
}
return 0;
}
I am working on Mac OSX and using bash as my shell. I am working in C and I am trying to create a file that will renumber files. The important part of my code is as follows:
int i;
for (i=0; i<numberOfFiles; i++) {
strcpy(fileName,""); //Set to Null
char append[formatLength]; //String being appended
sprintf(append,"%%0%dd", formatLength); //example output: %04d
strcat(fileName,filePrefix); //Attached Prefix
strcat(fileName,append); //Attaches appended part
//Missing code: Part which equvaluates %04d as int i, such as 0023.
}
This gets me the correct string format I am looking for (say formatLength=4): filePrefix+%04d. However, now I need to evaluate the %04d in the string and evaluate it as i, so that the files look like: file0001, file0002, etc.
Would anyone have any ideas. Thanks for your help.
Use the string you created with snprintf() as the format string for the next call to snprintf().
int formatLength = /* some input */;
char filePrefix[FILEPREFIX_LEN]; // assigned by some input
const int FILENAME_LEN = strlen(filePrefix) + formatLength + 1; // +1 for terminating '\0'
char fileName[FILENAME_LEN];
int i;
for (i=0; i<numberOfFiles; i++) {
char temp[TEMPLATE_LEN]; // where TEMPLATE_LEN >= FILEPREFIX_LEN + 3 + number of characters in the decimal representation of formatLength
snprintf(temp, TEMPLATE_LEN, "%s%%0%dd", filePrefix, formatLength);
// error check snprintf here, in case the destination buffer was not large enough
snprintf(fileName, FILENAME_LEN, temp, i);
// error check snprintf here, in case the destination buffer was not large enough
// use fileName
}
So if your filePrefix = "file" then you'd get fileName = "file0001", "file0002", "file0003", and so on...
Although a lot of this work isn't actually dependant on i so you could move it outside the loop, like this:
int formatLength = /* some input */;
char filePrefix[FILEPREFIX_LEN]; // assigned by some input
const int FILENAME_LEN = strlen(filePrefix) + formatLength + 1; // +1 for terminating '\0'
char fileName[FILENAME_LEN];
char temp[TEMPLATE_LEN]; // where TEMPLATE_LEN >= FILEPREFIX_LEN + 3 + number of characters in the decimal representation of formatLength
snprintf(temp, TEMPLATE_LEN, "%s%%0%dd", filePrefix, formatLength);
// error check snprintf here, in case the destination buffer was not large enough
int i;
for (i=0; i<numberOfFiles; i++) {
snprintf(fileName, FILENAME_LEN, temp, i);
// error check snprintf here, in case the destination buffer was not large enough
// use fileName
}
In these cases, your temp (short for "template", not "temporary") is going to be "prefix%04d" (e.g., for a prefixLength of 4 and filePrefix of "prefix"). You need to take care, then, that your filePrefix does not contain any characters that have special meaning to the printf family of functions. If you know a priori that it won't, then you're good to go.
However, if it's possible it will, then you need to do one of two things. You can process the filePrefix before you use it by escaping all the special characters. Or you can change your snprintf() calls to something like these:
snprintf(temp, TEMPLATE_LEN, "%%s%%0%dd", formatLength);
// other stuff...
snprintf(fileName, FILENAME_LEN, temp, filePrefix, formatLength);
Note the extra % at the beginning of the first snprintf(). This makes the template pattern "%s%04d" (e.g., for a prefixLength of 4), and then you add the filePrefix on the second call so that it's contents are not part of the pattern string in the second call.
If I understand your question correctly, you should be able to say
char result[(sizeof filePrefix/sizeof (char)) + formatLength];
sprintf(result, fileName, i);
since fileName looks something like "filePrefix%04d". Your desired filename will be then stored in result. I would not recommend re-storing it in fileNameby saying sprintf(fileName, fileName, i) because fileName may be too small (for example, when formatLength = 9).
Note that you need (sizeof filePrefix/sizeof (char)) to find the size of filePrefix (which is likely also char*), and then you add formatLength to see how many more chars you need after that
You can build a format string then use that as a format string for another formatter call. Note they the prefix and number format specifier can be built into a single string - no need for strcat calls.
Given:
char format_specifier[256] ;
then the loop code in your example can be replaced with:
snprintf( format_specifier,
sizeof( format_specifier),
"%s%%0%dd",
filePrefix,
formatLength ) ; // Create format string "<filePrefix>%0<formatLength>",
// eg. "file%04d"
snprintf( fileName, // Where the filename will be built
sizeof(fileName), // The length of the filename buffer
format_specifier, // The previously built format string
i ) ; // The file number.
I have assumed above that fileName is an array, if it is a pointer to an array, then sizeof(fileName) will be incorrect. Of course if you choose to use sprintf rather than snprintf it is academic.
sprintf(fileNameString, fileName, i); // I think you mean this, but use snprintf()
You are almost there
// This line could be done before the loop
sprintf(append,"%%0%dd", formatLength); //example output: %04d
// Location to store number
char NumBuffer[20];
// Form textual version of number
sprintf(NumBuffer, append, i);
strcat(fileName,filePrefix); //Attached Prefix
strcat(fileName,NumBuffer); //Attaches appended part
How can I convert a char to Int?
This is what I have done so far.
Thanks
scanf("%s", str );
printf("str: %s\n", str);
int i;
if(isdigit(*str))
i = (int) str;
else {
i = 3;
}
test case
7
str: 7
i: 1606415584
Edit: I could have sworn the post was tagged C++ at the start. I'll leave this up in case the OP is interested in C++ answers and the change to C tag was an edit.
A further option, which may be advanced given the question, is to use boost::lexical_cast as so:
scanf("%s", str );
printf("str: %s\n", str);
int i = boost::lexical_cast<int>( str );
I have used boost::lexical_cast a lot to convert between types, mostly strings and primitives when reading in user-defined properties. I find it an invaluable resource.
It's worth noting that boost::lexical_cast can throw exceptions, and these should be appropriately handled when you use the call. The link I posted at the start of this answer contains all the information you should need regarding that.
If you want to parse an integer from a string:
i = atoi(str);
You're mixing the character and string concepts here. str is a string, and str[0] (which is equivalent to *str) is a character, the first character of that string.
If you want to extract an integer from the string, try this
sscanf(str,"%d",&i);
Your
i = (int) str;
forces 4 bytes that start at the same memory address str (and for completeness sake, str is a pointer) starts to be interpreted as an integer, and that's why you get a result that's totally off.
You can convert strings to int by using sscanf
sscanf(str,"%d",&i);
http://www.cplusplus.com/reference/clibrary/cstdio/sscanf/
i = (int) str;
is a wrong way to convert a string to number, because It copies an address to i variable (the address which str is pointing to it).
You could try this:
i = atoi(str);
or
sscanf(str,"%d",&i);
to convert your string into a number.
Note that you cannot make sure the entered string is numeric by just isdigit(*str), because it only check the first character of the string.
One possible way is this:
int isNumeric = 1;
for(int j=0;j<length(str);j++)
if( isdigit(str[j]) == false)
{
isNumeric = 0;
break;
}
if(isNumeric)
{
// Code when the string is number
// (e.g. convert the string to a number with atoi function)
}
else
{
// Code when the string is NOT number
// (e.g. show a error message)
}
I need to form a string, inside each iteration of the loop, which contains the loop index i:
for(i=0;i<100;i++) {
// Shown in java-like code which I need working in c!
String prefix = "pre_";
String suffix = "_suff";
// This is the string I need formed:
// e.g. "pre_3_suff"
String result = prefix + i + suffix;
}
I tried using various combinations of strcat and itoa with no luck.
Strings are hard work in C.
#include <stdio.h>
int main()
{
int i;
char buf[12];
for (i = 0; i < 100; i++) {
snprintf(buf, 12, "pre_%d_suff", i); // puts string into buffer
printf("%s\n", buf); // outputs so you can see it
}
}
The 12 is enough bytes to store the text "pre_", the text "_suff", a string of up to two characters ("99") and the NULL terminator that goes on the end of C string buffers.
This will tell you how to use snprintf, but I suggest a good C book!
Use sprintf (or snprintf if like me you can't count) with format string "pre_%d_suff".
For what it's worth, with itoa/strcat you could do:
char dst[12] = "pre_";
itoa(i, dst+4, 10);
strcat(dst, "_suff");
Look at snprintf or, if GNU extensions are OK, asprintf (which will allocate memory for you).
#include<stdio.h>
main()
{
char str[50] = "Wel %s";
char dst[50];
snprintf(dst,50,str,"Come");
//Now i want to append "*" to dst string ie "Wel Come*" using snprintf()
printf("str = %s\n",str);
printf("dst = %s\n",dst);
}
please suggest is it possible using snprintf()
Thanks
Surya
The obvious solution:
snprintf(dst,50,"%s*",dst);
is inefficient, because it makes an unnecessary copy of dst (into itself).
invokes undefined behavior as R. pointed out, because the arguments may not overlap (from man snprintf(3) on MacOSX):
"[...]or those routines that write to
a user-provided character string, that
string and the format strings should
not overlap, as the behavior is
undefined."
Posix says:
http://www.opengroup.org/onlinepubs/000095399/functions/printf.html
"If copying takes place between
objects that overlap as a result of a
call to sprintf() or snprintf(), the
results are undefined."
snprintf returns the number of characters it has written, so you can do this instead:
int k=snprintf(dst,50,str,"Come");
// make sure that we do not pass potential disastrous values to snprintf, because
// the size argument is unsigned (size_t, 50-52 is a large positive number!)
// and we want 50-k to be in the range 0-50
// k<0 means output error and k>50 means "output truncated". There is no point in
// appending anything in these cases anyway.
if (k<0 || k>50)
{
fprintf(stderr,"output error or buffer too small");
}
else k=snprintf(dst+k,50-k,"*");
// check k for truncation here.
And then there's always strcat...And just in case, you overlooked it. You can have the * attached right in the first place:
main()
{
char str[50] = "Wel %s*"; //<--!!!
[...]
This should work:
#include<stdio.h>
int main()
{
char str[50] = "Wel %s";
char dst[50];
int len;
snprintf(dst,50,str,"Come");
//get size of current string
len = strlen(dst);
//add character to the end
snprintf(dst + len, sizeof(dst) - len, "*");
printf("str = %s\n",str);
printf("dst = %s\n",dst);
return 0;
}
you can use the %s format for this:
snprintf(dst, 50, "%s*", dst);
EDIT: This seems to have some undefined behaviors. The best thing would be to ask if it is really necessary to use snprintf instead of strncat.
All the information is already available to you:
snprintf(dst + 8, sizeof(dst) - 8, "%s", "*");
You'd be better off doing:
strncat(dst, "*", sizeof(dst) - strlen(dst) - 1);