can you give me an example of deleting characters from an array of characters in c?
I tried too much, but i didn't reach to what i want
That is what i did:
int i;
if(c<max && c>start) // c is the current index, start == 0 as the index of the start,
//max is the size of the array
{
i = c;
if(c == start)
break;
arr[c-1] = arr[c];
}
printf("%c",arr[c]);
A character array in C does not easily permit deleting entries. All you can do is move the data (for instance using memmove). Example:
char string[20] = "strring";
/* delete the duplicate r*/
int duppos=3;
memmove(string+duppos, string+duppos+1, strlen(string)-duppos);
You have an array of characters c:
char c[] = "abcDELETEdefg";
You want a different array that contains only "abcdefg" (plus the null terminator). You can do this:
#define PUT_INTO 3
#define TAKE_FROM 9
int put, take;
for (put = START_CUT, take = END_CUT; c[take] != '\0'; put++, take++)
{
c[put] = c[take];
}
c[put] = '\0';
There are more efficient ways to do this using memcpy or memmove, and it could be made more general, but this is the essence. If you really care about speed, you should probably make a new array that doesn't contain the characters you don't want.
Here's one approach. Instead of removing characters in place and shuffling the remaining characters (which is a pain), you copy the characters you want to keep to another array:
#include <string.h>
...
void removeSubstr(const char *src, const char *substr, char *target)
{
/**
* Use the strstr() library function to find the beginning of
* the substring in src; if the substring is not present,
* strstr returns NULL.
*/
char *start = strstr(src, substr);
if (start)
{
/**
* Copy characters from src up to the location of the substring
*/
while (src != start) *target++ = *src++;
/**
* Skip over the substring
*/
src += strlen(substr);
}
/**
* Copy the remaining characters to the target, including 0 terminator
*/
while ((*target++ = *src++))
; // empty loop body;
}
int main(void)
{
char *src = "This is NOT a test";
char *sub = "NOT ";
char result[20];
removeSubstr(src, sub, result);
printf("Src: \"%s\", Substr: \"%s\", Result: \"%s\"\n", src, sub, result);
return 0;
}
string = H E L L O \0
string_length = 5 (or just use strlen inside if you don't want to cache it outside this call
remove_char_at_index = 1 if you want to delete the 'E'
copy to the 'E' position (string + 1)
from the first 'L' position (string + 1 + 1)
4 bytes (want to get the NULL), so 5 - 1 = 4
remove_character_at_location(char * string, int string_length, int remove_char_at_index) {
/* Use memmove because the locations overlap */.
memmove(string+remove_char_at_index,
string+remove_char_at_index+1,
string_length - remove_char_at_position);
}
Related
thats my first post here so I hope im following the rules.
I had a job interview few days ago and I got a problem which I coulnd solve (until now).
The idea is (was on C but I guess its not an isuue): given a char* of n length, you should use O(1) memory comp (in-place changes), and compress the patterns of the same char to "num+char" where num is the pattern length and char is the character of the pattern. you can assume the memory is long enough to contain the expected result. Expected time comp is O(n).
Example- [abbbccccdee] ---> [1a3b4c1d2e]
my main issue here was with the case of 1 long patterns, because "pushing forward" all letters to get the 1 before it, raising the complexity. Otherwise my idea was to work with a pointer that indicates the locations of the next compress pattern, and it works only as long as the pointer is behind the regular scanning of the array.
Thanks!
compressing “same letters pattern”
my main issue here was with the case of 1 long patterns, because "pushing forward" all letters to get the 1 before it, raising the complexity
Note the length needed in the first pass to keep O(n) and expand from the end.
2 passes.
Compress:
Walk the string from beginning counting character repetition:
-- If digit encountered, error out.
-- Replace any repeat of 2 or more characters with the decimal length as text followed by the character.
-- Count single character cases.
Note the length of the compressed data.
Expand:
Let source = &s[length] - 1
Let destination = &s[length + single] - 1
Walk the array from the end reading from source and then writing to destination:
-- With special detection of the the array start:
---- Detect a character lacking a preceding digit, then write to the destination as '1' + character.
---- Otherwise copy "digits + character" to destination. Copy these characters in reverse order so with "12z", copy 'z', '2', then '1'.
Insure algorithm handles "" case.
Similar to #dratenik
Let's put #chux amazing answer into real code! The following code:
#include <stdio.h>
#include <assert.h>
#include <ctype.h>
char *compress_num_to_str(char *out, size_t v) {
// the standard output&reverse
char *tmp = out;
while (v) {
*tmp++ = v % 10 + '0';
v /= 10;
}
char *ret = tmp;
while (tmp > out) {
--tmp;
char a = *tmp;
*tmp = *out;
*out = a;
out++;
}
return ret;
}
char *compress(char *string) {
if (*string == '\0') {
return string;
}
// compress
char *in = string;
char *out = string;
size_t single = 0;
for (char c; (c = *in++) != '\0'; ) {
// the letter is going to be written anyway
char *const pos = in;
// iterate over repeated letters
while (c == *in) {
++in;
}
// if there are any repeated letters
if (pos != in) {
// output the count
const size_t count = in - pos + 1;
out = compress_num_to_str(out, count);
} else {
// a single char
single++;
}
*out++ = c;
}
const size_t length = out - string;
// expand
in = &string[length];
out = &string[length + single];
*out = '\0';
// I am not particularly happy at how this loops
// it's unreadable, but hey - it's fast and it works.
char c = *--in; // current character is buffered to be read
int cont; // should _the next_ loop continue
do {
// this should be a letter always
assert(!isdigit(c));
*--out = c;
// put the letter into destination
if ((cont = in != string) && (c = *--in, isdigit(c))) {
// copy up until digits
do {
*--out = c;
} while ((cont = in != string) && (c = *--in, isdigit(c)));
} else {
// the next input is not a digit
// add a one and repeat
*--out = '1';
}
} while (cont);
assert(in == string);
assert(out == string);
return string;
}
#define TEST(str) printf("%s -> %s\n", str, compress((char[sizeof(str) * 2 - 1]){str}))
int main() {
TEST("abbcccddddeef");
TEST("aabcdee");
TEST("");
TEST("a");
TEST("abcde");
TEST("aaaaaaaaaaaaaaaaaab");
TEST("aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
TEST("abbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
}
outputs:
abbcccddddeef -> 1a2b3c4d2e1f
aabcdee -> 2a1b1c1d2e
->
a -> 1a
abcde -> 1a1b1c1d1e
aaaaaaaaaaaaaaaaaab -> 18a1b
aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -> 18a374b
abbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -> 1a30b
I just want the string without underscore. I tried below few codes all doesn't work:
string is char pointer from another function, it looks like this: " "_I_have_1_dog.dat)" "
void func1(char *string)
{
char buffer[256]="";
unsigned long count = 0;
count = sscanf_s(string, " \"%*c%255[^\"]\"", buffer, _countof(buffer));
output:
_I_have_1_dog.dat
count = sscanf_s(string, " \"%*[^_]_%255[^\"]\"", buffer, _countof(buffer));
output:
_I_have_1_dog.dat
count = sscanf_s(string, " \"[^_]_%255[^\"]\"", buffer, _countof(buffer));
output:
_I_have_1_dog.dat
count = sscanf_s(string, " \"[^_]_%255[^\"]\"", buffer, _countof(buffer));
output:
_I_have_1_dog.dat
Edit Based on Removing 1st char '_' Instead of All '_'
The easiest approach to remove a leading '_' is simply to shift all characters down by 1 in string if the first character is an '_'.
You can use the functions like memmove from string.h to do the same thing. (in a single function call) However, looping is just as easy.
A simple function using the loop method could be:
void rm_1st_underscore (char *string)
{
int i = 1; /* set index to 1 (2nd char in string) */
if (*string != '_') /* if 1st char not '_', just return */
return;
do /* loop over each char in string */
string[i-1] = string[i]; /* shift chars back by 1 in string */
while (string[i++] != 0); /* (note: causes '\0' to copy) */
}
A short example would be:
#include <stdio.h>
void rm_1st_underscore (char *string)
{
int i = 1; /* set index to 1 (2nd char in string) */
if (*string != '_') /* if 1st char not '_', just return */
return;
do /* loop over each char in string */
string[i-1] = string[i]; /* shift chars back by 1 in string */
while (string[i++] != 0); /* (note: causes '\0' to copy) */
}
int main (void) {
char str[] = "_I_have_1_dog.dat";
rm_1st_underscore (str);
puts (str);
}
Example Use/Output
$ ./bin/rmunderscore
I_have_1_dog.dat
Look things over and let me know if you still have questions. If you need help trying it with memmove let me know and I'll drop another example.
Using memmove
Since we are just removing the first '_' instead of all of them, using memmove makes it trivial. Simply include string.h and get the length of string and then call memmove copying from the second char in string back to the first, e.g.
...
#include <string.h>
void rm_1st_underscore (char *string)
{
size_t len = strlen (string);
memmove (string, string + 1, len);
}
...
(the output is the same)
I want to save part of a string into a new char array while including the period. For example, the string is:
My name is John. I have 1 dog.
I want to copy each char up to and including the first period, so the new char array will contain:
My name is John.
The code I have written below copies only "My name is John" but omits the period.
ptrBeg and ptrEnd point to the char at the beginning and end, respectively, of the portion I want to copy. My intention was to copy ptrBeg into array newBuf through a pointer to newBuf and then increment both ptrBeg and the pointer to the array until ptrBeg and ptrEnd point to the same char, which should always be a period.
At this point, the text of the string should be copied, so I increment the pointer to char array once more and copy the period to the new space using
++ptrnewBuf;
*ptrnewBuf = *ptrEnd";
Finally, I print the contents of newBuf.
Here's the total code:
int main()
{
char buf[] = "My name is John. I have 1 dog.";
char * ptrBuf;
char * ptrBeg;
char * ptrEnd;
ptrBeg = buf;
ptrBuf = ptrBeg;
while (*ptrBuf != '.'){
ptrBuf++;
}
ptrEnd = ptrBuf;
char newBuf[100];
char * ptrnewBuf = newBuf;
while(*ptrBeg != *ptrEnd){
*ptrnewBuf = *ptrBeg;
ptrnewBuf++;
ptrBeg++;
}
++ptrnewBuf;
*ptrnewBuf = *ptrEnd;
printf("%s", newBuf);
}
How would I modify this code to include a period?
You are on the right track, but you may be making things a bit more complicated than needed and overlooking a few critical checks. The key to iterating by pointers or using pointer arithmetic is to always validate and protect your array or memory bounds during each iteration or arithmetic operation.
Another tip is to always map out your pointer positions on a piece of paper before coding everything up so you have a clear picture of what your iteration limits and any adjustments need to be. (you don't have to use full long strings and many boxes, just use a representation of what needs to be done with a handful of characters) In your case where you wish to copy the substing up through the first '.', something simple like the following will do, e.g.
+---+---+---+---+---+---+
| A | . | | B | . |\0 |
+---+---+---+---+---+---+
^ ^
| pointer (when *p == '.')
buf
So to copy "A." from buf to a new buffer you can't simply iterate while (*p != '.') or you will not copy '.'. By drawing it out, you can clearly see you need to also copy the character when p == '.', e.g.
+---+---+---+---+---+---+
| A | . | | B | . |\0 |
+---+---+---+---+---+---+
^ ^
| |-->| pointer (p + 1)
buf
Now regardless of the actual length of the string before '.', you now know you need p + 1 as the final address to include the last character in the copy.
You also know how many characters your new buffer can store. Say the size of new is MAXC characters (maximum number of characters). So you can store a string of at most MAXC-1 characters (plus the nul-character). When you are filling new you need to always validate you are within MAXC-1 characters.
You also need to insure you new string is nul-terminated (or it isn't a string, it's simply an array of characters). One effective way to insure nul-termination is by initializing all characters in new to 0 when it is declared, e.g.
char new[MAXC] = "";
which initializes the 1st character to 0 (e.g. '\0' empty-string) and all remaining characters 0 by default. Now if you fill no more than MAXC-1 characters, you are guaranteed the array will be a nul-terminated string.
Putting it altogether, you could do something like the following:
#include <stdio.h>
#define MAXC 128 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[] = "My name is John. I have 1 dog.",
*p = buf, /* pointer to buf */
new[MAXC] = "", /* buffer for substring */
*n = new; /* pointer to new */
size_t ndx = 0; /* index for new */
/* loop copying each char until new full, '.' copied, or end of buf */
for (; ndx + 1 < MAXC && *p; p++, n++, ndx++) {
*n = *p; /* copy char from buf to new */
if (*n == '.') /* if char was '.' break */
break;
}
printf ("buf: %s\nnew: %s\n", buf, new);
return 0;
}
(note: ndx is incremented as part of the for loop to track the number of characters copied with the pointers)
Example Use/Output
$ ./bin/str_cpy_substr
buf: My name is John. I have 1 dog.
new: My name is John.
If you do not have the luxury of initializing the string to insure nul-termination, you can always affirmatively nul-terminate after your copy is done. For example, you could add the following after the for loop exit to insure an array of unknown initialization is properly terminated:
*++n = 0; /* nul-terminate (if not already done by initialization) and
* note ++n applied before * due to C operator precedence.
*/
Look things over and let me know if you have further questions.
Just breaking it out into a helper function that "extracts" the first sentence from a line. Just copies the characters over one at a time until either an end of string condition is hit on the source, the period is found, or a max length of the destination buffer is encountered.
void ExtractFirstSentence(const char* line, char* dst, int size)
{
int count = 0;
char c ='\0';
if ((line == NULL) || (dst == NULL) || (size <= 0))
{
return;
}
while ((*line) && ((count+1) < size) && (c != '.'))
{
c = *line++;
*dst++ = c;
count++;
}
*dst = '\0';
}
int main()
{
char buf[] = "My name is John. I have 1 dog.";
char newBuf[100];
ExtractFirstSentence(buf, newBuf, 100);
printf("%s", newBuf);
}
if you want something a bit easier without dealing with all those pointers, try :
int main()
{
char buf[] = "My name is John. I have 1 dog.";
int i = 0;
int j = 0;
while(buf[i] != '.' && buf[i] != '\0') {
i++;
}
char newbuf[i+1];
while (j <= i) {
newbuf[j] = buf[j];
j++;
}
newbuf[j] = '\0';
printf("%s\n",newbuf);
return 0;
}
though the i+1 when making newbuf and the newbuff[j] = '\0' im not 100% certain need to be that way. my thoughts are the i+1 is needed to make room for the \0 ending which is then added after the while loop copying buf to newbuf. but i could be mistaken.
You can use strtok() to split string. Just type man strtok, You will see:
Program source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
char *str1, *str2, *token, *subtoken;
char *saveptr1, *saveptr2;
int j;
if (argc != 4) {
fprintf(stderr, "Usage: %s string delim subdelim\n",
argv[0]);
exit(EXIT_FAILURE);
}
for (j = 1, str1 = argv[1]; ; j++, str1 = NULL) {
token = strtok_r(str1, argv[2], &saveptr1);
if (token == NULL)
break;
printf("%d: %s\n", j, token);
for (str2 = token; ; str2 = NULL) {
subtoken = strtok_r(str2, argv[3], &saveptr2);
if (subtoken == NULL)
break;
printf(" --> %s\n", subtoken);
}
}
exit(EXIT_SUCCESS);
}
An example of the output produced by this program is the following:
$ ./a.out 'a/bbb///cc;xxx:yyy:' ':;' '/'
1: a/bbb///cc
--> a
--> bbb
--> cc
2: xxx
--> xxx
3: yyy
--> yyy
I am trying to write a program that reads the stdin stream looking for words (consecutive alphabetic characters) and for each word rotates it left to the first vowel (e.g. "friend" rotates to "iendfr") and writes this sequence out in place of the original word. All other characters are written to stdout unchanged.
So far, I have managed to reverse the letters, but have been unable to do much more. Any suggestions?
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAX_STK_SIZE 256
char stk[MAX_STK_SIZE];
int tos = 0; // next available place to put char
void push(int c) {
if (tos >= MAX_STK_SIZE) return;
stk[tos++] = c;
}
void putStk() {
while (tos >= 0) {
putchar(stk[--tos]);
}
}
int main (int charc, char * argv[]) {
int c;
do {
c = getchar();
if (isalpha(c) && (c == 'a' || c == 'A' || c == 'e' || c == 'E' || c == 'i' || c == 'o' || c == 'O' || c == 'u' || c == 'U')) {
push(c);
} else if (isalpha(c)) {
push(c);
} else {
putStk();
putchar(c);
}
} while (c != EOF);
}
-Soul
I am not going to write the whole program for you, but this example shows how to rotate a word from the first vowel (if any). The function strcspn returns the index of the first character matching any in the set passed, or the length of the string if no matches are found.
#include <stdio.h>
#include <string.h>
void vowelword(const char *word)
{
size_t len = strlen(word);
size_t index = strcspn(word, "aeiou");
size_t i;
for(i = 0; i < len; i++) {
printf("%c", word[(index + i) % len]);
}
printf("\n");
}
int main(void)
{
vowelword("friend");
vowelword("vwxyz");
vowelword("aeiou");
return 0;
}
Program output:
iendfr
vwxyz
aeiou
There are a number of ways your can approach the problem. You can use a stack, but that just adds handling the additional stack operations. You can use a mathematical reindexing, or you can use a copy and fill solution where you copy from the first vowel to a new string and then simply add the initial characters to the end of the string.
While you can read/write a character at a time, you are probably better served by creating the rotated string in a buffer to allow use of the string within your code. Regardless which method you use, you need to validate all string operations to prevent reading/writing beyond the end of your input and/or rotated strings. An example of a copy/fill approach to rotating to the first vowel in your input could be something like the following:
/* rotate 's' from first vowel with results to 'rs'.
* if 's' contains a vowel, 'rs' contains the rotated string,
* otherwise, 'rs' contais 's'. a pointer to 'rs' is returned
* on success, NULL otherwise and 'rs' is an empty-string.
*/
char *rot2vowel (char *rs, const char *s, size_t max)
{
if (!rs || !s || !max) /* validate params */
return NULL;
char *p = strpbrk (s, "aeiou");
size_t i, idx, len = strlen (s);
if (len > max - 1) { /* validate length */
fprintf (stderr, "error: insuffieient storage (len > max - 1).\n");
return NULL;
}
if (!p) { /* if no vowel, copy s to rs, return rs */
strcpy (rs, s);
return rs;
}
idx = p - s; /* set index offset */
strcpy (rs, p); /* copy from 1st vowel */
for (i = 0; i < idx; i++) /* rotate beginning to end */
rs[i+len-idx] = s[i];
rs[len] = 0; /* nul-terminate */
return rs;
}
Above, strpbrk is used to return a pointer to the first occurrence of a vowel in string 's'. The function takes as parameters a pointer to a adequately sized string to hold the rotated string 'rs', the input string 's' and the allocated size of 'rs' in 'max'. The parameters are validated and s is checked for a vowel with strpbrk which returns a pointer to the first vowel in s (if it exists), NULL otherwise. The length is checked against max to insure adequate storage.
If no vowels are present, s is copied to rs and a pointer to rs returned, otherwise the pointer difference is used to set the offset index to the first vowel, the segment of the string from the first vowel-to-end is copied to rs and then the preceding characters are copied to the end of rs with the loop. rs is nul-terminated and a pointer is returned.
While I rarely recommend the use of scanf for input, (a fgets followed by sscanf or strtok is preferable), for purposes of a short example, it can be used to read individual strings from stdin. Note: responding to upper/lower case vowels is left to you. A short example setting the max word size to 32-chars (31-chars + the nul-terminating char) will work for all known words in the unabridged dictionary (longest word is 28-chars):
#include <stdio.h>
#include <string.h>
enum { BUFSZ = 32 };
char *rot2vowel (char *rs, const char *s, size_t max);
int main (void)
{
char str[BUFSZ] = {0};
char rstr[BUFSZ] = {0};
while (scanf ("%s", str) == 1)
printf (" %-8s => %s\n", str, rot2vowel (rstr, str, sizeof rstr));
return 0;
}
Example Use/Output
(shamelessly borrowing the example strings from WeatherVane :)
$ echo "friend vwxyz aeiou" | ./bin/str_rot2vowel
friend => iendfr
vwxyz => vwxyz
aeiou => aeiou
Look it over and let me know if you have any questions. Note: you can call the rot2vowel function prior to the printf statement and print the results with rstr, but since the function returns a pointer to the string, it can be used directly in the printf statement. How you use it is up to you.
Write a function
void inplace(char *str,
const char pattern,
const char* replacement,
size_t mlen)
Input:
str: a string ending with \0. the input indicates that we need an inplace algorithm.
pattern: a letter.
replacement: a string.
mlen: the size of the memory holds the string str starts from the beginning of the memory and that mlen should be larger than strlen(str)
The final result is still pointed by str.
Note that all occurrence of pattern should be replaced.
For example,
helelo\0...........
Here "helelo" is the string to replace with '\0' at the end. After '\0' there are still L valid bytes. We want to replace "e" by "123".
A simple approach works like this, we go through str, when a pattern is matched, we shift all the rest with the place to fill the replacement string, then replace the pattern by the replacement.
If the original string is with length n and contains only e, we need (n-1) + (n-2) + ... + 1 shifts.
Is there an algorithm that scans the string with only one pass and constant memory cost?
I think two passes is the minimum. On the first pass, count the number of characters that will be replaced. Given that count and the length of the replacement string, you can compute the length of the final string. (And you should verify that it's going to fit into the buffer.)
On the second pass, you scan the string backwards (starting at the last character), copying characters to their final location. When you encounter the search character, copy the replacement string to that location.
In your example, the increase in length would be 2. So you would
copy str[5] which is '\0' to str[7]
copy str[4] which is 'o' to str[6]
copy str[3] which is 'l' to str[5]
copy str[2] which is 'l' to str[4]
at str[1] you find the 'e' so str[3]='3' str[2]='2' str[1]='1'
At this point the output index is the same as the input index, so you can break the loop.
As #chux pointed out in the comments, the cases where the replacement string is either empty, or has exactly one character, can be handled with a single forward pass through the string. So the code should handle those cases separately.
A candidate single pass solution.
For each character in str, recurse. After the recursion, do the replacement.
It does recurse heavily.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
// return 0:success else 1:fail
static int inplace_help(char *dest, const char *src, int pattern,
const char* replacement, size_t rlen, size_t mlen) {
printf("'%p' '%s' %c\n", dest, src, pattern);
if (*src == pattern) {
if (rlen > mlen) return 1;
if (inplace_help(dest + rlen, src + 1, pattern, replacement, rlen,
mlen - rlen)) return 1;
memcpy(dest, replacement, rlen);
return 0;
}
if (mlen == 0) return 1;
int replace1 = *src;
if (*src) {
if (inplace_help(dest + 1, src + 1, pattern, replacement, rlen, mlen - 1)) {
return 1;
}
}
*dest = replace1;
return 0;
}
void inplace(char *str, const char pattern, const char* replacement,
size_t mlen) {
if (pattern == 0) return;
if (mlen == 0) return;
if (*replacement == 0) return; // Insure str does not shrink.
inplace_help(str, str, pattern, replacement, strlen(replacement), mlen - 1);
}
int main(void) {
char str[1000] = "eeeeec";
inplace(str, 'e', "1234", sizeof str);
printf("'%s'\n", str); // --> '12341234123412341234c'
return 0;
}
The following assumes that the memory allocated to the string has been initialized to something at some point in time, since standard C does not seem to allow access to uninitialized memory. In practice, it will work fine.
It does precisely two scans: the first one is over the entire allocated space, and moves the string to the right-hand edge of the space. The second scan is over the string itself, which it moves back to the left-hand edge while it does replacements.
I changed the prototype to return 0 on success; -1 on failure. I also allow the pattern to be a string. (Maybe a single character was intentional? Easy to change, anyway.) (As written, pattern must not be length zero. Should be checked.)
int inplace(char *str,
const char* pattern,
const char* replacement,
size_t mlen) {
/* We don't know how long the string is, but we know that it ends
with a NUL byte, so every time we hit a NUL byte, we reset
the output pointer.
*/
char* left = str + mlen;
char* right = left;
while (left > str) {
if (!*--left) right = str + mlen;
*--right = *left;
}
/* Naive left-to-right scan. KMP or BM would be more efficient. */
size_t patlen = strlen(pattern);
size_t replen = strlen(replacement);
for (;;) {
if (0 == strncmp(pattern, right, patlen)) {
right += patlen;
if (right - left < replen) return -1;
memcpy(left, replacement, replen);
left += replen;
} else {
if (!(*left++ = *right++)) break;
}
}
return 0;
}