I want to create a big string 'des' from 2 substrings copied from string 'scr' this way :
I want to copy the substring, lets call it - 'string1' ( from ptr x to the end of the 'scr' string), then to place it in the 'des' and after that to concatenate it with another substring, lets call it - 'string 2' copied from the 'scr' string from the head of the string to x ptr. How can I do it not using a temp string ?
for example : scr = "ThisIs", string1 = "Is", string2 = "This" des = "IsThis"
I don't want to use a temp string to hold string 1 or string2.
Can you help me?
You don't need a temp string, you only need a pointer to hold the boundary of substrings. Try following:
char src[] = "ThisIs";
char dst[7] = {'\0'};
int len = strlen(src);
int str1len = 4;
strncpy(dst, src + str1len, len - str1len);
strncpy(dst + len - str1len, src, str1len);
printf("src=%s, dst=%s\n", src, dst);
If you know the position of the second string, you can just print the two substrings to the destination string in reverse order:
char *src = "ThisIs"; // source string
char dst[7]; // char buffer for destination string
int pos = 4; // position of second substring
snprintf(dst, sizeof(dst), "%s%.*s", src + pos, pos, src);
puts(dst);
Explanation:
snprintf writes formatted data to a string, just as printf writes formatted data to the screen. It takes the buffer length as second argument and uses it to ensure that the buffer will not overflow. It also guarantees that the resulting string is null terminated.
If the output would be a string with more characters than the buffer can hold, the output is truncated. snprintf returns the length that the string would have if the buffer were ininitely large. You can use that return value to check whether the output was truncated.
The second substring is null-terminated, because it ends where the whole string src ends. You can print it with printf("%s", str + pos), where pos is the start of the substring.
The first substring isn't null terminated. You can print substrings of any length by providing a "precision" to the %s format after a period: printf("%.4s", str).
You can make that precision variable by using an asterisk in the format and then providing an additional int argument before the actual argument: printf("%.*s", 4, str)
This answer is at heart the same answer as fluter's, but it guards against buffer overfloows and involves fewer length calculations.
Related
I tried to think how to make the function strncpy and I met this problem.
char src[] = "123456789A";
char dest[10];
int n = 10;
printf("strncpy:%s\n", strncpy(dest, src, n));
Output
strncpy:123456789A123456789A
What is happening ?
The quick answer: strncpy is not your friend!
strncpy is not a safer version of strcpy, it will copy up to n characters from src and if src is shorter, will pad the destination with null bytes up a total of n characters.
If the source string has n or more characters, the destination array will not be null terminated and passing to printf("%s", will have undefined behavior: printf will keep reading and printing bytes from memory after the end of dest, until it finds a null byte or until this undefined behavior causes other unpredictable side effects...
The semantics of strncpy are counter-intuitive and error-prone, avoid usng this function. See this article for a long answer: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
As others have said strncpy won't include a terminating null if the destination size is the same as the string length. To give you a practical answer I normally just subtract one from the size of the destination using sizeof to get the destination size including space for the terminator:
char src[] = "123456789A";
char dest[10];
printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1));
Which gives an output of "strncpy:123456789" which is a character short of what you want but at least is defined behaviour and lets you know the destination buffer isn't large enough to contain the null terminator. So the final code that gives you the result you're after would be:
char src[] = "123456789A";
char dest[11];
printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1));
The array dest does not contain a string because there is no enough space to accommodate the terminating zero '\0' of the copied source string,
So to output the array use the following statement
printf("strncpy: %*.*s\n", n, n, strncpy(dest, src, n));
Otherwise you have to write something like the following
strncpy( dest, src, n )[sizeof( dest ) - 1] = '\0';
printf("strncpy: %s\n", dest );
In this case the destination array will not have the last character of the source string that will be overwritten by the zero character.
If you want to copy less characters than the size of the destination array then what to do after copying depends on the intention. If you want just to overwrite part of the string that is already stored in the destination array then nothing else you need to do. Otherwise set the character at position n to zero character.
Here is a demonstrative program.
#include <stdio.h>
#include <string.h>
int main(void)
{
char src[] = "123456789A";
char dest[10] = "543216789";
size_t n = 5;
strncpy( dest, src, n );
printf("strncpy: %s\n", dest );
strncpy( dest, "Hello", n )[n] = '\0';
printf("strncpy: %s\n", dest );
return 0;
}
Its output is
strncpy: 123456789
strncpy: Hello
I'm trying to use sprintf() to put a string "inside itself", so I can change it to have an integer prefix. I was testing this on a character array of length 12 with "Hello World" inside it already.
The basic premise is that I want a prefix that denotes the amount of words within a string. So I copy 11 characters into a character array of length 12.
Then I try to put the integer followed by the string itself by using "%i%s" in the function. To get past the integer (I don't just use myStr as the argument for %s), I make sure to use myStr + snprintf(NULL, 0, "%i", wordCount), which should be myStr + characters taken up by the integer.
The problem is that I'm having is that it eats the 'H' when I do this and prints "2ello World" instead of having the '2' right beside the "Hello World"
So far I've tried different options for getting "past the integer" in the string when I try to copy it inside itself, but nothing really seems to be the right case, as it either comes out as an empty string or just the integer prefix itself '222222222222' copied throughout the entire array.
int main() {
char myStr[12];
strcpy(myStr, "Hello World");//11 Characters in length
int wordCount = 2;
//Put the integer wordCount followed by the string myStr (past whatever amount of characters the integer would take up) inside of myStr
sprintf(myStr, "%i%s", wordCount, myStr + snprintf(NULL, 0, "%i", wordCount));
printf("\nChanged myStr '%s'\n", myStr);//Prints '2ello World'
return 0;
}
First, to insert a one-digit prefix into a string “Hello World”, you need a buffer of 13 characters—one for the prefix, eleven for the characters in “Hello World”, and one for the terminating null character.
Second, you should not pass a buffer to snprintf as both the output buffer and an input string. Its behavior is not defined by the C standard when objects passed to it overlap.
Below is a program that shows you how to insert a prefix by moving the string with memmove. This is largely tutorial, as it is not generally a good way to manipulate strings. For short strings, where space is not an issue, most programmers would simply print the desired string into a temporary buffer, avoiding overlap issues.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Insert a decimal numeral for Prefix into the beginning of String.
Length specifies the total number of bytes available at String.
*/
static void InsertPrefix(char *String, size_t Length, int Prefix)
{
// Find out how many characters the numeral needs.
int CharactersNeeded = snprintf(NULL, 0, "%i", Prefix);
// Find the current string length.
size_t Current = strlen(String);
/* Test whether there is enough space for the prefix, the current string,
and the terminating null character.
*/
if (Length < CharactersNeeded + Current + 1)
{
fprintf(stderr,
"Error, not enough space in string to insert prefix.\n");
exit(EXIT_FAILURE);
}
// Move the string to make room for the prefix.
memmove(String + CharactersNeeded, String, Current + 1);
/* Remember the first character, because snprintf will overwrite it with a
null character.
*/
char Temporary = String[0];
// Write the prefix, including a terminating null character.
snprintf(String, CharactersNeeded + 1, "%i", Prefix);
// Restore the first character of the original string.
String[CharactersNeeded] = Temporary;
}
int main(void)
{
char MyString[13] = "Hello World";
InsertPrefix(MyString, sizeof MyString, 2);
printf("Result = \"%s\".\n", MyString);
}
The best way to deal with this is to create another buffer to output to, and then if you really need to copy back to the source string then copy it back once the new copy is created.
There are other ways to "optimise" this if you really needed to, like putting your source string into the middle of the buffer so you can append and change the string pointer for the source (not recommended, unless you are running on an embedded target with limited RAM and the buffer is huge). Remember code is for people to read so best to keep it clean and easy to read.
#define MAX_BUFFER_SIZE 128
int main() {
char srcString[MAX_BUFFER_SIZE];
char destString[MAX_BUFFER_SIZE];
strncpy(srcString, "Hello World", MAX_BUFFER_SIZE);
int wordCount = 2;
snprintf(destString, MAX_BUFFER_SIZE, "%i%s", wordCount, srcString);
printf("Changed string '%s'\n", destString);
// Or if you really want the string put back into srcString then:
strncpy(srcString, destString, MAX_BUFFER_SIZE);
printf("Changed string in source '%s'\n", srcString);
return 0;
}
Notes:
To be safer protecting overflows in memory you should use strncpy and snprintf.
I have a function that will parse some data coming in. My problem is that after using strncpy I get some garbage when I try to print it. I try using malloc to make the char array the exact size.
Code:
void parse_data(char *unparsed_data)
{
char *temp_str;
char *pos;
char *pos2;
char *key;
char *data;
const char newline = '\n';
int timestamp = 0;
temp_str = (char*)malloc(strlen(unparsed_data));
g_print("\nThe original string is: \n%s\n",unparsed_data);
//Ignore the first two lines
pos = strchr(unparsed_data, newline);
strcpy(temp_str, pos+1);
pos = strchr(temp_str, newline);
strcpy(temp_str, pos+1);
//Split the line in two; The key name and the value
pos = strchr(temp_str, ':'); // ':' divides the name from the value
pos2 = strchr(temp_str, '\n'); //end of the line
key = (char*)malloc((size_t)(pos-temp_str)-1); //allocate enough memory
data = (char*)malloc((size_t)(pos2-pos)-1);
strncpy(key, temp_str, (size_t)(pos-temp_str));
strncpy(data, pos + 2, (size_t)(pos2-pos));
timestamp = atoi(data);
g_print("size of the variable \"key\" = %d or %d\n", (size_t)(pos-temp_str), strlen(key));
g_print("size of the variable \"data\" = %d or %d\n", (size_t)(pos2-pos), strlen(data));
g_print("The key name is %s\n",key);
g_print("The value is %s\n",data);
g_print("End of Parser\n");
}
Output:
The original string is:
NEW_DATAa_PACKET
Local Data Set 16-byte Universal Key
Time Stamp (microsec): 1319639501097446
Frame Number: 0
Version: 3
Angle (deg): 10.228428
size of the variable "key" = 21 or 22
size of the variable "data" = 18 or 21
The key name is Time Stamp (microsec)
The value is 1319639501097446
F32
End of Parser
Run it again:
The original string is:
NEW_DATAa_PACKET
Local Data Set 16-byte Universal Key
Time Stamp (microsec): 1319639501097446
Frame Number: 0
Version: 3
Angle (deg): 10.228428
size of the variable "key" = 21 or 25
size of the variable "data" = 18 or 18
The key name is Time Stamp (microsec)ipe
The value is 1319639501097446
F
End of Parser
Your results are because strncpy does not put a null character at the end of the string.
Your strncpy(data, pos + 2, (size_t)(pos2-pos)); doesn't add a terminating \0 character at the end of the string. Therefore when you try to print it later, printf() prints your whole data string and whatever is in the memory right after it, until it reaches zero - that's the garbate you're getting. You need to explicitly append zero at the end of your data. It's also needed for atoi().
Edit:
You need to allocate one more byte for your data, and write a terminating character there. data[len_of_data] = '\0'. Only after that it becomes a valid C string and you can use it for atoi() and printf().
You need to malloc() +1 byte for a string, so it can append the zero when you do strcpy(), but the strncpy will not append zero as well you need a extra byte for it.
One problem: What if there's no newline?
Undefined behaviour:
pos = strchr(temp_str, newline);
strcpy(temp_str, pos+1);
The source and destination of strcpy must not overlap.
You have to remember in allocating space for a string to add one byte for the terminating '\0' character. You have to be careful with strncpy, especially if you are used to using strcpy, strcat, or sprintf. Those three functions terminate the string with '\0'. strncpy copies a number of bytes that you specify, and makes no assumption of terminating the string.
You assume that responsibility by making sure you place a '\0' at the end of the character buffer to which you copied. That means you have to know the starting the position and the length of the copy and put a '\0' one byte past the sum of the starting position and length.
I chose to solve a sample problem slightly differently, but it still involves knowing the length of what I copied.
In this case, I use strncpy to take the first 9 characters from pcszTestStr1 and
copy them to szTestBuf. Then, I use strcpy -- which terminates the string with a zero --
to append the new part of the sentence.
#include <stdio.h>
#include <string.h>
int n;
int argv_2;
char szTestBuf[100] = {0};
char * pcszTestStr1 =
"This is a very long, long string to be used in a C example, OK?";
int main(int argc, char *argv[])
{
int rc = 0;
printf("The following sentence is too long.\n%s\n", pcszTestStr1);
strncpy(szTestBuf, pcszTestStr1, 9);
strcpy(szTestBuf + 9, " much shorter sentence.");
printf("%s\n", szTestBuf);
return rc;
}
Here's the output of running test.c compiled gcc -o test test.c.
cnorton#hiawatha:~/scratch$ ./test
The following sentence is too long.
This is a very long, long string to be used in a C example, OK?
This is a much shorter sentence.
cnorton#hiawatha:~/scratch$
As simple as that. I'm on C++ btw. I've read the cplusplus.com's cstdlib library functions, but I can't find a simple function for this.
I know the length of the char, I only need to erase last three characters from it. I can use C++ string, but this is for handling files, which uses char*, and I don't want to do conversions from string to C char.
If you don't need to copy the string somewhere else and can change it
/* make sure strlen(name) >= 3 */
namelen = strlen(name); /* possibly you've saved the length previously */
name[namelen - 3] = 0;
If you need to copy it (because it's a string literal or you want to keep the original around)
/* make sure strlen(name) >= 3 */
namelen = strlen(name); /* possibly you've saved the length previously */
strncpy(copy, name, namelen - 3);
/* add a final null terminator */
copy[namelen - 3] = 0;
I think some of your post was lost in translation.
To truncate a string in C, you can simply insert a terminating null character in the desired position. All of the standard functions will then treat the string as having the new length.
#include <stdio.h>
#include <string.h>
int main(void)
{
char string[] = "one one two three five eight thirteen twenty-one";
printf("%s\n", string);
string[strlen(string) - 3] = '\0';
printf("%s\n", string);
return 0;
}
If you know the length of the string you can use pointer arithmetic to get a string with the last three characters:
const char* mystring = "abc123";
const int len = 6;
const char* substring = mystring + len - 3;
Please note that substring points to the same memory as mystring and is only valid as long as mystring is valid and left unchanged. The reason that this works is that a c string doesn't have any special markers at the beginning, only the NULL termination at the end.
I interpreted your question as wanting the last three characters, getting rid of the start, as opposed to how David Heffernan read it, one of us is obviously wrong.
bool TakeOutLastThreeChars(char* src, int len) {
if (len < 3) return false;
memset(src + len - 3, 0, 3);
return true;
}
I assume mutating the string memory is safe since you did say erase the last three characters. I'm just overwriting the last three characters with "NULL" or 0.
It might help to understand how C char* "strings" work:
You start reading them from the char that the char* points to until you hit a \0 char (or simply 0).
So if I have
char* str = "theFile.nam";
then str+3 represents the string File.nam.
But you want to remove the last three characters, so you want something like:
char str2[9];
strncpy (str2,str,8); // now str2 contains "theFile.#" where # is some character you don't know about
str2[8]='\0'; // now str2 contains "theFile.\0" and is a proper char* string.
Let's say I have a char* str = "0123456789" and I want to cut the first and the last three letters and print just the middle, what is the simplest, and safest, way of doing it?
Now the trick: The portion to cut and the portion to print are of variable size, so I could have a very long char*, or a very small one.
You can use printf(), and a special format string:
char *str = "0123456789";
printf("%.6s\n", str + 1);
The precision in the %s conversion specifier specifies the maximum number of characters to print. You can use a variable to specify the precision at runtime as well:
int length = 6;
char *str = "0123456789";
printf("%.*s\n", length, str + 1);
In this example, the * is used to indicate that the next argument (length) will contain the precision for the %s conversion, the corresponding argument must be an int.
Pointer arithmetic can be used to specify the starting position as I did above.
[EDIT]
One more point, if your string is shorter than your precision specifier, less characters will be printed, for example:
int length = 10;
char *str = "0123456789";
printf("%.*s\n", length, str + 5);
Will print "56789". If you always want to print a certain number of characters, specify both a minimum field width and a precision:
printf("%10.10s\n", str + 5);
or
printf("%*.*s\n", length, length, str + 5);
which will print:
" 56789"
You can use the minus sign to left-justify the output in the field:
printf("%-10.10s\n", str + 5);
Finally, the minimum field width and the precision can be different, i.e.
printf("%8.5s\n", str);
will print at most 5 characters right-justified in an 8 character field.
Robert Gamble and Steve separately have most of the pieces.
Assembled into a whole:
void print_substring(const char *str, int skip, int tail)
{
int len = strlen(str);
assert(skip >= 0);
assert(tail >= 0 && tail < len);
assert(len > skip + tail);
printf("%.*s", len - skip - tail, str + skip);
}
Invocation for the example:
print_substring("0123456789", 1, 3);
If you don't mind modifying the data, you could just do some pointer arithmetic. This is assuming that str is a char pointer and not an array:
char string[] = "0123456789";
char *str = string;
str += 3; // "removes" the first 3 items
str[4] = '\0'; // sets the 5th item to NULL, effectively truncating the string
printf(str); // prints "3456"
Here is a clean and simple substring function I dug up from my personal library that may be useful:
char *
substr(const char *src, size_t start, size_t len)
{
char *dest = malloc(len+1);
if (dest) {
memcpy(dest, src+start, len);
dest[len] = '\0';
}
return dest;
}
It's probably self-explanatory but it takes a string, a starting position (starting at zero), and a length and returns a substring of the original string or a null pointer if malloc fails. The pointer returned can be free'd by the caller when the memory is no longer needed. In the spirit of C, the function doesn't validate the starting position and length provided.
I believe there is some magic you can do with printf that will only print a certain number of characters, but it's not commonly understood or used. We tried to do it at a previous job and couldn't get it to work consistently.
What I would do is save off a character, null that character in the string, print it, then save it back.