I am trying to do a find a replace but not just for strings but for substrings also. So the program I am working on looks for the word "bar" and append "foo" in front of any instance of "bar". So my approach is that instead of actually appending the string, I replace the whole string "bar" with "foobar". The code I have right now (not fully tested), should find and replace all occurrences of "bar" with "foobar". However, if there is a string that looks like "bar123abc", it does not replace it with "foobar123abc".
This is the code I have:
static void replaceAllString(char *buf, const char *orig, const char *replace)
{
int olen, rlen;
char *s, *d;
char *tmpbuf;
if (!buf || !*buf || !orig || !*orig || !replace)
return;
tmpbuf = malloc(strlen(buf) + 1);
if (tmpbuf == NULL)
return;
olen = strlen(orig);
rlen = strlen(replace);
s = buf;
d = tmpbuf;
while (*s) {
if (strncmp(s, orig, olen) == 0) {
strcpy(d, replace);
s += olen;
d += rlen;
}
else
*d++ = *s++;
}
*d = '\0';
strcpy(buf, tmpbuf);
free(tmpbuf);
}
Here's how I might do it:
static char *replaceAll(char *buf, int buflen, const char *orig, const char *replace) {
if (!buf || !*buf || !orig || !*orig || !replace) return buf;
int olen = strlen(orig), rlen = strlen(replace);
int max = strlen(buf) + 1;
if (olen < rlen) {
max = rlen * ((max / olen) + 1) + 1;
}
char *tmpbuf = malloc(max);
char *bp = buf, *tp = tmpbuf, *sp;
while (NULL != (sp = strstr(bp, orig))) {
int f = sp - bp;
memmove(tp, bp, f);
memmove(tp + f, replace, rlen);
tp += f + rlen;
bp += f + olen; // no recursive replacement
}
strcpy(tp, bp);
strncpy(buf, tmpbuf, buflen);
free(tmpbuf);
return buf;
}
char haystack[128] = "123bar456bar7ba8ar9bar0";
int main(int ac, char *av[]) {
printf("%s\n", replaceAll(haystack, sizeof haystack, "bar", "foobar"));
}
Note: passing buflen is NOT optional! You DO NOT write to memory buffers you don't know the length of. If I'm interviewing C programmers, this would be an instant "no hire". tmpbuf is allocated the length max, crudely calculated for the worst case (something like "barbarbar"). The heavy lifting here is done by strstr().
Related
I'm trying to split lines of the type:
GM 1 2 3 ! this is a comment
to separate out the comment section. There are several possible comment delimiters, !, ' and #. strtok is the obvious solution for this:
card->card_str = strtok(line_buf, "!'#");
producing GM 1 2 3 and this is a comment. However, for this role, I need to keep the delimiter character in the second string, so in this case ! this is a comment. Is there an easy way to do this?
strtok is rarely the right tool for parsing jobs because it has many quirks and side effects.
For your goal, you can use strcspn():
void parse_input_line(const char *line) {
size_t len = strcspn(line, "!'#");
char *p = malloc(len + 1);
if (p != NULL) {
memcpy(p, line, len);
p[len] = '\0';
card->card_str = p;
card->card_comment = p[len] ? strdup(p + len) : NULL;
}
}
Alternately, you can use strpbrk:
void parse_input_line(const char *line) {
const char *sep = strpbrk(line, "!'#");
if (sep == NULL) {
// no comment
card->card_str = strdup(line);
card->card_comment = NULL;
} else {
size_t len = sep - line;
char *p = malloc(len + 1);
if (p != NULL) {
memcpy(p, line, len);
p[len] = '\0';
card->card_str = p;
card->card_comment = strdup(sep);
}
}
}
You can use strndup to make the code more readable:
void parse_input_line(const char *line) {
size_t len = strcspn(line, "!'#");
if (p[len] == '\0') {
/* no comment */
card->card_str = strdup(line);
card->card_comment = NULL;
} else {
card->card_str = strndup(line, len);
card->card_comment = strdup(p + len);
}
}
strndup may not be available on all systems, here is a simple implementation:
size_t strnlen(const char *s, size_t n) {
size_t len;
for (len = 0; len < n; len++) {
if (s[len] == '\0')
break;
}
return len;
}
char *strndup(const char *s, size_t n) {
size_t len = strnlen(s, n);
char *p = malloc(len + 1);
if (p != NULL) {
memcpy(p, s, len);
p[len] = '\0';
}
return p;
}
I want to remove a particular substring from a string for example my main string is "ababccdabce" and I want to remove "abc" from it so it will become "abcde".
I just wanted to know if there is a predefined function in C to do that, and if not, how to do it?
There is no predefined function in C to remove a given substring from a C string, but you can write one using strstr and memmove. Note that if you remove the substring in place, you cannot use memcpy nor strcpy because these have undefined behavior if the source and destination arrays overlap.
Here is the code:
#include <string.h>
char *strremove(char *str, const char *sub) {
size_t len = strlen(sub);
if (len > 0) {
char *p = str;
while ((p = strstr(p, sub)) != NULL) {
memmove(p, p + len, strlen(p + len) + 1);
}
}
return str;
}
Note that the resulting string may contain the substring as is the case in your example.
Netherwire suggested an optimisation:
char *strremove(char *str, const char *sub) {
size_t len = strlen(sub);
if (len > 0) {
char *p = str;
size_t size = 0;
while ((p = strstr(p, sub)) != NULL) {
size = (size == 0) ? (p - str) + strlen(p + len) + 1 : size - len;
memmove(p, p + len, size - (p - str));
}
}
return str;
}
Further honing the code, I came up with an even more efficient version using the 2 finger-method: only copying the fragments between matches starting after the first match:
char *strremove(char *str, const char *sub) {
char *p, *q, *r;
if (*sub && (q = r = strstr(str, sub)) != NULL) {
size_t len = strlen(sub);
while ((r = strstr(p = r + len, sub)) != NULL) {
memmove(q, p, r - p);
q += r - p;
}
memmove(q, p, strlen(p) + 1);
}
return str;
}
Here is the same method without any calls to memmove:
char *strremove(char *str, const char *sub) {
char *p, *q, *r;
if (*sub && (q = r = strstr(str, sub)) != NULL) {
size_t len = strlen(sub);
while ((r = strstr(p = r + len, sub)) != NULL) {
while (p < r)
*q++ = *p++;
}
while ((*q++ = *p++) != '\0')
continue;
}
return str;
}
I had to write data to a CSV file in low level C code. I share the little snippet for cases external libraries like OpenCSV are not suitable.
To write to file instead of sprintf(s,...) use fprintf(f,...)
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
//replace string a with string b in str
//str must have enough space
char* _replace(char* str, char* a, char* b, int len, int lena, int lenb)
{
for (char* p = str; p = strstr(p, a);)
{
if (lena != lenb) // shift end as needed
memmove(p + lenb, p + lena, len + 1 - (p + lena - str) + 1);
memcpy(p, b, lenb);
p += lenb;
}
return str;
}
//allocate space which must be free'd
//wrap in " and replace " by "" if necessary
char* _csv_alloc(char* str)
{
int len = strlen(str);
char *_str = (char*)malloc(2 * len + 1 + 2);
bool wrap = false;
if (strchr(str, ';') != NULL || strchr(str, '\"') != NULL)
wrap = true;
if (wrap)
{
_str[0] = '\"';
memcpy(_str + 1, str, len + 1);
}
else
memcpy(_str, str, len + 1);
_replace(wrap ? (_str + 1) : _str, "\"", "\"\"", len, 1, 2);
if (wrap)
{
len = strlen(_str);
_str[len] = '\"';
_str[len + 1] = '\0';
}
return _str;
}
int main()
{
char *c1 = "Nothing to escape";
char *c2 = "Here the ; entails wrapping";
char *c3 = "Here the \" entails wrapping and escaping";
char *_c1 = _csv_alloc(c1);
char *_c2 = _csv_alloc(c2);
char *_c3 = _csv_alloc(c3);
char res[0xFF] = "";
sprintf(res, "%s;%s;%s\n", _c1, _c2, _c3);
free(_c1);
free(_c2);
free(_c3);
assert(strcmp(res, "Nothing to escape;\"Here the ; entails wrapping\";\"Here the \"\" entails wrapping and escaping\"\n") ==0);
return 0;
}
i need to make a function that returns a compressed line with the following formats,
input:
pprrrinnnttttfff
output:
p2r3i1n3t4f3
and if the new string is larger than the original, return the original, can someone tell what is wrong with my code?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *comprimir(char *s);
int main(){
FILE* input;
char *lineptr = NULL;
size_t len=0, c;
input =fopen ("input.dat", "r");
while ((c = getline(&lineptr, &len, input))!= -1){
lineptr = comprimir(lineptr);
printf("%s", lineptr );
}
fclose(input);
}
char* comprimir (char *s){
int len1 = strlen(s), len2=0;
char *str, *in, *mystr;
mystr =(char*) calloc(len1*2, sizeof(char));
strcpy(mystr, s);
for (str =mystr, in=mystr; *str; str++){
len2 += 2;
if (len2 >= len1) {
free(mystr);
return s;
}
int count =1;
in[0] = str[0]; printf("%s",in[0] ); in++;
if (len2 > len1) return s;
while (str[0] == str[1]){
count++;
str++;
}
in[0] = '0' + count;
in++; printf("%s", in[0] );
if (len2 > len1) return s;
}
strcpy(s, in);
free(mystr);
return s;
}
sample to fix
char* comprimir (char *s){
int len1 = strlen(s), len2=0;
char *out, *in, *mystr;
mystr =malloc(len1 + 2);//2*len1 not required, +2 : "r" -> "r1"(len + NUM_len + NUL
//`s` not copy to mystr, avoid overwriting
for (out = mystr, in=s; *in;){
int count = 1;
*out++ = *in++;//Pre-increment `in` to reduce code.
while (in[-1] == in[0]){
count++;
in++;
}
int num_len = sprintf(out, "%d", count);//OK even count is more than 10
len2 += 1 + num_len;
if (len2 >= len1) {
free(mystr);
return s;
}
out += num_len;
}
*out = 0;//terminate by '\0'
strcpy(s, mystr);
free(mystr);
return s;
}
There is a string
char *message = "hello#world#####.......";
How to delete all the "#" and return "helloworld" ?
In Ruby I can use gsub to deal with it
In C, you have to do it yourself. For example:
#include <string.h>
char *remove_all(const char *source, char c)
{
char *result = (char *) malloc(strlen(source) + 1);
char *r = result;
while (*source != '\0')
{
if (*source != c)
*r++ = *source;
source++;
}
*r = '\0';
return result;
}
Note that in that implementation, the caller would have to free the result string.
I believe there is a better algorithm to do this....no freeing is necessary - it's in-place.
char *remove_all(char *string, char c)
{
int idx = 0;
char *beg = string;
while(*string) {
if (*string != c)
beg[idx++] = *string;
++string;
}
beg[idx] = 0;
return beg;
}