C code to handle separator and " in content in csv - c

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;
}

Related

Remove a substring from a string in C

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;
}

Substring replace

I want to implement a c code such that it replaces only the exact matching not part of another string.
check out my code.
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="This is a simpled simple string";
char * pch;
char str1[]= "simple";
pch = strstr (str,str1);
strncpy (pch,"sample",6);
puts (str);
return 0;
}
the above code gives the output : This is sampled simple string
I want the output to be : This is simpled sample string
please help
Thanks.
The best way to deal with these types of question is consider each and every word one-by-one. And then check whether, the pattern (which we are looking for?) is present in the given string or not, if yes then replace it with replacing word.
Below is my code. (I know it may seem bit odd one out, but trust me it will work for any pattern-matching and replacement problem). It will reduce and expand the final output according to the given pattern word and its corresponding replacement word.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main() {
/* This program will replace the "demo" with "program" */
char input[] = " isdemo Hello this is demo. replace demo with demoes something else demo";
char pattern[] = "demo";
char replace[] = "program";
char output[105];
int index = 0;
/*Read the the input line word-by-word,
if the word == pattern[], then replace it else do nothing */
for(int i=0; i<strlen(input);) {
while(i<strlen(input) && !isalpha(input[i])) {
output[index++] = input[i++];
}
char temp[105]; int j = 0;
while(i<strlen(input) && isalpha(input[i])) {
temp[j++] = input[i++];
}
temp[j] = 0;
if(strcmp(temp, pattern) == 0) {
strncpy(output+index, replace, strlen(replace));
index += strlen(replace);
} else {
strncpy(output+index, temp, strlen(temp));
index += strlen(temp);
}
}
output[index] = 0;
puts(output);
return 0;
}
If i'm still missing any test case. I will be pleased to know about it.
First, you need search the entire string continuously until no substring is found, second, you need check the char before and after the substring returned by strstr, to make sure that the found substring is a complete word. When check for word boundary, take special care when the word is at the beginning or end of the longer string. For example:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] ="simple simples is a simpled simple string simple";
char *s = str;
char *pch = str;
char str1[]= "simple";
int len = strlen(str1);
int pos;
while (1) {
pch = strstr(s, str1);
if (!pch) // no more occurrences of str1, quit
break;
pos = pch - str;
if (pos == 0) { // if it's the beginning
if (!isalpha(pch[len])) {
strncpy(pch, "sample", 6);
}
} else { // check two ends
if (!isalpha(*(pch-1)) && !isalpha(*(pch+len))) {
strncpy(pch, "sample", 6);
}
}
s = pch + len;
}
puts(str);
return 0;
}
I updated my code.This deals with the replacement that you want.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void replace(char *buf, size_t bufSize, const char *word_to_replace, const char *replacement_word);
int main(void)
{
char str[100] = "simple Asimple simpleB This is a simpled simple string and simple is good sometimes!, simple";
replace(str, sizeof(str), "simple", "sample");
printf("%s\n", str);
return 0;
}
void replace(char *buf, size_t bufSize, const char *word_to_replace, const char *replacement_word)
{
size_t buf_len = strlen(buf), word_len = strlen(word_to_replace);
char *ptr = strstr(buf, word_to_replace);
if (ptr == NULL) {
fprintf(stderr, "Could not find matches.\n");
return;
}
bool _G = 0;
char *tmp = (char *)malloc(bufSize);
// Deal with begining of line
if (ptr == buf) {
if (ptr[word_len] == ' ' || ptr[word_len] == '\0') {
_G = 1;
}
if (_G) {
strcpy_s(tmp, bufSize, ptr + word_len);
*ptr = 0;
strcat_s(buf, bufSize, replacement_word);
strcat_s(buf, bufSize, tmp);
_G = 0;
}
}
else {
if (*(ptr - 1) == ' ' && (ptr[word_len] == ' ' || ptr[word_len] == '\0')) {
_G = 1;
}
if (_G) {
strcpy_s(tmp, bufSize, ptr + word_len);
*ptr = 0;
strcat_s(buf, bufSize, replacement_word);
strcat_s(buf, bufSize, tmp);
_G = 0;
}
}
// deal with the rest
while (ptr = strstr(ptr + 1, word_to_replace))
{
if (*(ptr - 1) == ' ' && (ptr[word_len] == ' ' || ptr[word_len] == '\0')) {
_G = 1;
}
if (_G) {
strcpy_s(tmp, bufSize, ptr + word_len);
*ptr = 0;
strcat_s(buf, bufSize, replacement_word);
strcat_s(buf, bufSize, tmp);
_G = 0;
}
}
free(tmp);
}
A word can start with space or may lie at the start of string and can end with a space, a full stop, a comma or with the end of string. using these conditions you can easily identify any word within a string. Following code describes it according to your example.
Using this code you can replace a word with another word of any size.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main()
{
char str[] = "simple This is a simpled simple simple. simple, string simple";
char * pch;
char * result = str;
char * temp;
char str1[] = "simple"; //string to be replaced
char str2[] = "sample"; //string to be replaced with
pch = strstr(result, str1);
while(pch)
{
temp = result;
if ((pch == str || *(pch - 1) == ' ') && (strlen(pch) == strlen(str1) || !isalpha(*(pch + strlen(str1)))))
{
result = (char*)malloc(strlen(temp)+(strlen(str2) - strlen(str1))+1); //allocate new memory, +1 for trailing null character
strncpy(result, temp, pch - temp); // copy previous string till found word to new allocated memory
strncpy(result + (pch - temp), str2, strlen(str2)); // replace previous word with new word
strncpy(result + (pch - temp) + strlen(str2), pch + strlen(str1), strlen(pch + strlen(str1))); // place previous string after replaced word
strncpy(result + strlen(temp) + (strlen(str2) - strlen(str1)), "\0", 1); // place null character at the end of string
if (temp != str)
free(temp); // free extra memory
}
pch = strstr(result + (pch - temp) + 1, str1); // search for another word in new string after the last word was replaced
}
puts(result);
if (result != str)
free(result);
return 0;
}

Find a replace a substring C

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().

compressing the lines of a file using dynamic memory

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;
}

How to add a character in a string by checking the delimiter?

I have a string in my program where in which it need to be altered with another string value before a "/".
Source String : qos-tree/output_rate
Target String : qos-tree-2/output_rate
#include <stdio.h>
#include <string.h>
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
int main(void)
{
char str[256] = "qos-tree/output_rate";
char c = "a";
append(str, c);
printf("%s\n", str);
return 0;
}
This is what i have done so far,I think the logic is wrong here.Can anyone guide me to correct it?
Once the execution is completed the source string should have a "-2" before the "/"
void insert_before_ch(char *s, const char *ins, char c){
char *p = strchr(s, c);
if(p){
size_t len = strlen(ins);
memmove(p + len, p, strlen(p)+1);
memcpy(p, ins, len);
}
}
int main(void){
char str[256] = "qos-tree/output_rate";
insert_before_ch(str, "-2", '/');
printf("%s\n", str);
return 0;
}
In your attempt, you don't look for a slash and I do not see any "-2" anywhere.
Try this instead:
#include <stdio.h>
#include <string.h>
void append(char* s, char del, char* substring) {
char origin[256];
strcpy(origin, s);
int i = 0, j = 0, z = 0;
for(; origin[i]; ++i) {
if(origin[i] != del) {
s[j++] = origin[i];
} else {
for(; substring[z]; ++z) {
s[j++] = substring[z];
}
s[j++] = origin[i];
}
}
s[j] = '\0';
}
int main(void) {
char str[256] = "qos-tree/output_rate";
char del = '/';
char* substring = "-2";
append(str, del, substring);
printf("%s\n", str);
return 0;
}
The logic is that inside the function we use origin array to remember the actual contents of the array and then we copy from origin to s (which is the actual array of main()). If we find our delimiter del, then we copy the substring in that position and continuing with copying.
Note that the length of the array should be enough to store the resulted string. In this case, it is.
I think you should make your function work with dynamic allocation, because inserting characters into the string will make the resulting string larger, so this is my suggestion
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void insert(char **str, char chr, unsigned int position)
{
int length;
char *ptr;
if (str == NULL)
return;
length = strlen(*str);
if (position >= length)
return;
ptr = realloc(*str, 2 + length);
if (ptr == NULL)
return;
*str = ptr;
memmove(ptr + position + 1, ptr + position, length - position + 1);
ptr[position] = chr;
}
int main(void)
{
const char *source = "qos-tree/output_rate";
size_t length = strlen(source);
char *str = malloc(1 + length);
if (str == NULL)
return -1;
strcpy(str, source);
insert(&str, '-', 8);
insert(&str, '2', 9);
printf("%s\n", str);
free(str);
return 0;
}
first of all thist char c = "a" should be replace with this char c = 'a'; because c is a character not a string
as for your problem I didn't realy see the relation between what your code is doing with what you said you wanted to do , but here a piece of code to achieve what , I think , you want to do :
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void append(char* str , char c)
{
char firststr[60];
char therest[30];
strcpy(firststr , strtok(str , "/"));
strcpy(therest , strtok(NULL , "/"));
strcat(firststr , &c);
strcat(firststr , "/");
strcat(firststr , therest);
strcpy(str , firststr);
}
int main(void)
{
char str[60] = "qos-tree/output_rate";
char c = '2';
append(str , c);
printf("%s\n" , str);
}
there you go I think this is what you wanted to do you can modify the array sizes to fit your needs

Resources