Strings and substrings - c

I tried to work with different versions but all of them do not work unfortunetaly. That is why I am posting here. I need to return an array of two strings. The first is substring from the beginning up to but not including the comma. The second is the substring of s after the comma. String contains only one comma. Also I need to use char * strchr(const char *s, int c). Well, it did not help me. Please, help mw with this, spent many of hours......thanks
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **split_on_comma(const char * s){
//this piece of code does not work
/*int i;
char array[80];
for (i=0; i<strlen(s)-1; i++){
if(substr=(s[i],',')==NULL){
array[i]=s[i];
} else
}*/
return 0;
}

This is quite trivial: just copy both halves of the string.
char **split_on_comma(const char *str)
{
const char *p = strchr(str, ',');
if (p == NULL)
return NULL;
char **subs = malloc(2 * sizeof subs[0]);
ptrdiff_t len1 = p - str;
ptrdiff_t len2 = str + strlen(str) - (p + 1);
// copy and 0-terminate first half
subs[0] = malloc(len1 + 1);
memcpy(subs[0], str, len1);
subs[0][len1] = 0;
// copy and 0-terminate second half
subs[1] = malloc(len2 + 1);
memcpy(subs[1], p + 1, len2);
subs[1][len2] = 0;
return subs;
}
Checks against malloc() returning NULL are omitted for clarity, shall be included in production code though.

Related

Is there an easy way to remove specific chars from a char*?

char * deleteChars = "\"\'.“”‘’?:;-,—*($%)! \t\n\x0A\r"
I have this and i'm trying to remove any of these from a given char*. I'm not sure how I would go about comparing a char* to it.
For example if the char* is equal to "hello," how would I go about removing that comma with my deleteChars?
So far I have
void removeChar(char*p, char*delim){
char*holder = p;
while(*p){
if(!(*p==*delim++)){
*holder++=*p;
p++;
}
}
*holder = '\0';
A simple one-by-one approach:
You can use strchr to decide if the character is present in the deletion set. You then assign back into the buffer at the next unassigned position, only if not a filtered character.
It might be easier to understand this using two indices, instead of using pointer arithmetic.
#include <stdio.h>
#include <string.h>
void remove_characters(char *from, const char *set)
{
size_t i = 0, j = 0;
while (from[i]) {
if (!strchr(set, from[i]))
from[j++] = from[i];
i++;
}
from[j] = 0;
}
int main(void) {
const char *del = "\"\'.“”‘’?:;-,—*($%)! \t\n\x0A\r";
char buf[] = "hello, world!";
remove_characters(buf, del);
puts(buf);
}
stdout:
hello world
If you've several delimiters/characters to ignore, it's better to use a look-up table.
void remove_chars (char* str, const char* delims)
{
if (!str || !delims) return;
char* ans = str;
int dlt[256] = {0};
while (*delims)
dlt[(unsigned char)*delims++] = 1;
while (*str) {
if (dlt[(unsigned char)*str])
++str; // skip it
else //if (str != ans)
*ans++ = *str++;
}
*ans = '\0';
}
You could do a double loop, but depending on what you want to treat, it might not be ideal. And since you are FOR SURE shrinking the string you don't need to malloc (provided it was already malloced). I'd initialize a table like this.
#include <string.h>
...
char del[256];
memset(del, 0, 256 * sizeof(char));
for (int i = 0; deleteChars[i]; i++) del[deleteChars[i]] = 1;
Then in a function:
void delChars(char *del, char *string) {
int i, offset;
for (i = 0, offset = 0; string[i]; i++) {
string[i - offset] = string[i];
if (del[string[i]]) offset++;
}
string[i - offset] = 0;
}
This will not work on string literals (that you initialize with char* x = "") though because you'd end up writing in program memory, and probably segfault. I'm sure you can tweak it if that's your need. (Just do something like char *newString = malloc(strlen(string) + 1); newString[i - offset] = string[i])
Apply strchr(delim, p[i]) to each element in p[].
Let us take advantage that strchr(delim, 0) always returns a non-NULL pointer to eliminate the the null character test for every interrelation.
void removeChar(char *p, char *delim) {
size_t out = 0;
for (size_t in; /* empty */; in++) {
// p[in] in the delim set?
if (strchr(delim, p[in])) {
if (p[in] == '\0') {
break;
}
} else {
p[out++] = p[in];
}
}
p[out] = '\0';
}
Variation on #Oka good answer.
it is better way - return the string without needless characters
#include <string.h>
char * remove_chars(char * str, const char * delim) {
for ( char * p = strpbrk(str, delim); p; p = strpbrk(p, delim) )
memmove(p, p + 1, strlen(p));
return str;
}

finding substring and replacing it in C

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char * str = "Testing replace text...\n\n";
char* buffer = malloc(sizeof(char));
char* insertPoint = &buffer[0];
char* copy = str;
char* p = strstr(str, "epl");
char* g = "gard";
int size = 0;
size = p-copy; //p = 9, which is the number of elemts till the first element of the substring
//want to allocate this space, and then increment insertPoint, by that amt(it'll be pointing
// to nothing)
buffer = realloc(buffer, size);
printf("Size: %d\n", size);
memcpy(insertPoint, copy, size);
printf("COPY: %s\n", buffer);
copy += size;
buffer = realloc(buffer, size+strlen(g));
insertPoint += size;
printf("%c", *insertPoint);
memcpy(insertPoint, g, strlen(g)); //insert after the 9 letters, the string the size of g
size += strlen(g); //size if the size of the buffer
printf("Size2: %d\n", size);
printf("COPY2: %s\n", buffer);
return EXIT_SUCCESS;
}
Just some quick experimental code; I am just trying to replace the substring epl in str with "gard" but when I print it out there are no changes to the string buffer I am printing, meaning the first string im printing works where it gets all the letters into buffer before the substring occurs, but when I try to replace with substring it doesn't work. I've testing the individual pointers and they all seem correct...not sure what is happening, any insight? Thanks...fully runnable program.
I think the problem in your code arise because strlen does not include the terminating zero. I tried to fix your code but in the end I found it easier to re-write it anew (and using more sensible variable names).
The following simple four steps work. The continuous use of strlen may be replaced by variables, but I left them for clarity. (Also, a good compiler may very well optimize this code by leaving the calls out.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *str = "Testing replace text...\n\n";
char* buffer;
char* find_str = "epl";
char* repl_str = "gard";
char *find_str_pos = strstr (str, find_str);
/* 1. create new buffer of the correct size */
buffer = malloc (strlen(str) - strlen(find_str) + strlen(repl_str) + 1);
/* 2. copy first part */
memcpy (buffer, str, find_str_pos - str);
/* 3. add new text */
memcpy (buffer + (find_str_pos - str), repl_str, strlen(repl_str));
/* 4. append original text */
memcpy (buffer + (find_str_pos - str) + strlen(repl_str), find_str_pos + strlen(find_str), strlen(find_str_pos) - strlen(repl_str) + 1);
printf ("-> [%s]\n", buffer);
return EXIT_SUCCESS;
}
You are not appending remaining text "ace text..." after replacing "epl".
Can you try code below
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main() {
char *source_string = "Testing replace text...";
char *search_string = "epl";
char *replace_string = "gard";
int search_length = strlen(search_string);
int replace_length = strlen(replace_string);
// Find start position of search string
char *start = strstr(source_string, search_string); // start pointing to "eplace text..."
int intial_length = start - source_string; // intial_length = 9
// Get remaining text which should append after replace
char *remaining_string = (start + search_length); // remaining_string pointing to "ace text..."
int remaining_length = strlen(remaining_string); // remaining_length = 11
// Find total length of string after replacing text
int total_string_length = intial_length + replace_length + remaining_length; // 24
char *buffer = (char *)malloc(total_string_length + 1); // +1 for null pointer
char *current_index = buffer;
// Add initial text
memcpy(current_index, source_string, intial_length);
current_index += intial_length;
// Add replace text
memcpy(current_index, replace_string, replace_length);
current_index += replace_length;
// Add remaining text
memcpy(current_index, remaining_string, remaining_length);
current_index += remaining_length;
memcpy(current_index, "\0", 1); // add null pointer at last
printf("Final Output: %s", buffer); // Final Output: Testing rgardace text...
return 0;
}

How to concatenate 2 strings using malloc and not the library functions

I need to create a function to concatenate 2 strings, in my case they are already given. I will need to concatenate the strings 'hello' and 'world!' to make it into 'helloworld!'. However, I can't use library functions besides strlen(). I also need to use malloc. I understand malloc would create n amounts of bytes for memory, however, how would I make it so that it can return a string array if thats possible.
Here is what I have so far,
#include <stdio.h>
#include <string.h>
int *my_strcat(const char* const str1, const char *const str2)
{
int s1, s2, s3, i = 0;
char *a;
s1 = strlen(str1);
s2 = strlen(str2);
s3 = s1 + s2 + 1;
a = char *malloc(size_t s3);
for(i = 0; i < s1; i++)
a[i] = str1[i];
for(i = 0; i < s2; i++)
a[i+s1] = str2[i];
a[i]='\0';
return a;
}
int main(void)
{
printf("%s\n",my_strcat("Hello","world!"));
return 0;
}
Thanks to anyone who can help me out.
This problem is imo a bit simpler with pointers:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *mystrcat(char *a, char *b) {
char *p, *q, *rtn;
rtn = q = malloc(strlen(a) + strlen(b) + 1);
for (p = a; (*q = *p) != '\0'; ++p, ++q) {}
for (p = b; (*q = *p) != '\0'; ++p, ++q) {}
return rtn;
}
int main(void) {
char *rtn = mystrcat("Hello ", "world!");
printf("Returned: %s\n", rtn);
free(rtn);
return 0;
}
But you can do the same thing with indices:
char *mystrcat(char *a, char *b) {
char *rtn = malloc(strlen(a) + strlen(b) + 1);
int p, q = 0;
for (p = 0; (rtn[q] = a[p]) != '\0'; ++p, ++q) {}
for (p = 0; (rtn[q] = b[p]) != '\0'; ++p, ++q) {}
return rtn;
}
Here is an alternate fix. First, you forgot #include <stdlib.h> for malloc(). You return a pointer to char from the function my_strcat(), so you need to change the function prototype to reflect this. I also changed the const declarations so that the pointers are not const, only the values that they point to:
char * my_strcat(const char *str1, const char *str2);
Your call to malloc() is incorrectly cast, and there is no reason to do so anyway in C. It also looks like you were trying to cast the argument in malloc() to size_t. You can do so, but you have to surround the type identifier with parentheses:
a = malloc((size_t) s3);
Instead, I have changed the type declaration for s1, s2, s3, i to size_t since all of these variables are used in the context of string lengths and array indices.
The loops were the most significant change, and the reason that I changed the consts in the function prototype. Your loops looked fine, but you can also use pointers for this. You step through the strings by incrementing a pointer, incrementing a counter i, and store the value stored there in the ith location of a. At the end, the index i has been incremented to indicate the location one past the last character, and you store a '\0' there. Note that in your original code, the counter i was not incremented to indicate the location of the null terminator of the concatenated string, because you reset it when you looped through str2. #jpw shows one way of solving this problem.
I changed main() just a little. I declared a pointer to char to receive the return value from the function call. That way you can free() the allocated memory when you are through with it.
Here is the modified code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * my_strcat(const char *str1, const char *str2)
{
size_t s1, s2, s3, i = 0;
char *a;
s1 = strlen(str1);
s2 = strlen(str2);
s3 = s1+s2+1;
a = malloc(s3);
while(*str1 != '\0') {
a[i] = *str1;
str1++;
i++;
}
while(*str2 != '\0') {
a[i] = *str2;
str2++;
i++;
}
a[i] = '\0'; // Here i = s1 + s2
return a;
}
int main(void)
{
char *str = my_strcat("Hello", "world!");
printf("%s\n", str);
/* Always free allocated memory! */
free(str);
return 0;
}
There are a few issues:
In the return from malloc you don't need to do any cast (you had the syntax for the cast wrong anyway) (see this for more information).
You need to include the header stdlib.h for the malloc function.
And most importantly, a[i]='\0'; in this i is not what you need it to be; you want to add the null char at the end which should be a[s3]='\0'; (the length of s1+s2).
This version should be correct (unless I missed something):
#include <stdio.h>
#include <stdlib.h> //for malloc
#include <string.h>
char *my_strcat(const char* const str1, const char *const str2)
{
int s1,s2,s3,i=0;
char *a;
s1 = strlen(str1);
s2 = strlen(str2);
s3 = s1+s2+1;
a = malloc(s3);
for(i = 0; i < s1; i++) {
a[i] = str1[i];
}
for(i = 0; i < s2; i++) {
a[i+s1] = str2[i];
}
a[s3-1] = '\0'; // you need the size of s1 + s2 + 1 here, but - 1 as it is 0-indexed
return a;
}
int main(void)
{
printf("%s\n",my_strcat("Hello","world!"));
return 0;
}
Testing with Ideone renders this output: Helloworld!

strend function in C using pointers?

I have created a function for strend, which basically returns 1 if string t is present at the end of string s, however it never returns 1:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int strend(char *s, char *t) {
int p;
for (p = 0; p < strlen(s) - strlen(t); p++) {
*s++;
}
printf("%s\n%s\n", s, t);
if (s == t)
return 1;
return 0;
}
int main(void) {
int bool = strend("Hello", "ello");
printf("%i\n", bool);
return 0;
}
This gives me an output of:
ello
ello
0
So technically I should get 1. I assume the comparison using pointers is not used in this way?
You need to review your basic knowledge of C strings. There are lots of standard string functions in string.h that can help you with this test.
The basic problem is that the test s == t is valid, but you are comparing memory addresses here. You can see that is valid if you change the strings to test to
char test[] = "Hello";
int bool = strend_(test, test+1);
where test obviously is the same as your "Hello", and similarly, test+1 is the same as "ello" (try it by printing them). This correctly returns 1 with your routine.
In addition, I get two warnings:
on *s++; "warning: expression result unused [-Wunused-value]": you increment s but also ask what character is at that position through *s; and you don't use that information.
Fix by removing the * there.
on p < strlen(s) ..; "warning: comparison of integers of different signs: 'int' and 'unsigned long'", because strlen does not return a signed integer but an unsigned one (apparently, my header uses unsigned long).
Fix by declaring p as unsigned long, or even better, size_t.
Your entire routine can be condensed to a simple
int strend (char *s, char *t)
{
if (strlen(s) >= strlen(t) && !strcmp (s+strlen(s)-strlen(t),t))
return 1;
return 0;
}
It's not worth the trouble to cache the result of those four strlen calls into 2 temporary variables; a good compiler will work it out and do that for you. (A quick glance to the assembly output of the compiler I'm using – clang – shows it does, even with the default optimization settings.)
A slightly modified test, based on #M.M.'s comment:
int strend (char *s, char *t)
{
if (strlen(s) < strlen(t)) return 0;
return !strcmp (s+strlen(s)-strlen(t),t);
}
but attempting to optimize it this way is not as easy parsed as the routine above, and its assembly is ever so slightly "wordy" as well. Personally, I'd go for the more humanly readable version.
Use strcmp(3)
if (strcmp(s, t) == 0) return 1;
This actually compares the contents of the memory pointed to by s and t rather than their addresses.
Your code is broken in multiple ways:
The initial loop is a very cumbersome way to advance p by the difference of lengths if positive.
Once you have pointers at the same distance from the end of both strings, You should compare the characters with strcmp() (or memcmp() if you can first exclude the case of strlen(s) < strlen(t).
Comparing the pointers obtained after the loop will only work if t points inside the string pointed to by s, a special case that may or may not be produced by the compiler for the specific call in main: strend("Hello", "ello");.
Here is a modified version:
#include <string.h>
int strend(const char *str1, const char *str2) {
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
return len1 >= len2 && !memcmp(str1 + len1 - len2, str2, len2);
}
I corrected/modified your code, here is the code,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#pragma warning(disable:4996)
int strend(char *s, char *t)
{
int p,flag=0,count=0;//count will be the starting index for *t
p = strlen(s) - strlen(t);//this will be the starting index for *s
while(count<strlen(t))
{
if (*(s+p) == *(t+count))
{
flag = 1;
count++;
p++;
continue;
}
else
{
flag = 0;
break;
}
}
return flag;
}
int main(void)
{
int flag = strend("Hello", "ello");
printf("%i\n", flag);
return 0;
}
This code works too.
#include <stdio.h>
#include <string.h>
int strend (char *s1, char *s2);
void main ()
{
char str1[20] = "somethings";
char str2[20] = "things";
int f;
f = strend (str1,str2);
if (f==1)
printf ("1");
else
printf ("0");
}
int strend (char *str1, char *str2)
{
int l = strlen(str1) - strlen(str2);
str1 = str1 + l;
int d = strcmp(str1,str2);
if (d == 0)
return 1;
else
return 0;
}
this code works well.
int strend(char *s, char *t){
while(*t & *s){
if(*t == *s){
t++;
}
s++;
}
return *t==*s;
}

String appending function doesn't work

I tried to create a function that easily appends two strings. The source string has to be previously allocated dynamically. I used my knowledge but this resulted in ub and any sort of leaks.
void strapp (char *source_offset, int position, char *appendage)
{
size_t appendage_size = strlen(appendage);
source_offset = realloc(source_offset, strlen(source_offset) + appendage_size);
sprintf( &source_offset[position], "%s%s", appendage, source_offset + (position + appendage_size) );
}
What am I doing wrong?
source_offset + (position + appendage_size) is somehow strange. It seems that you tried to catenate the second string with a substring of the first copying the result in the first string.
source_offset + (position + appendage_size) is the suffix of the source string starting at offset position+appendage_size which is a non-sense as it is past the end of the source string...
May be you wanted something like this?
If you want to catenate the two string then the following is correct:
size_t appendage_size = strlen(appendage);
source_offset = realloc(source_offset, position + appendage_size + 1);
sprintf( &source_offset[position], "%s", appendage );
Which appends appendage to source_offset starting at position.
Now if you want to insert appendage in the middle this can be a little more tricky:
size_t appendage_size = strlen(appendage);
char *result = malloc(strlen(source_offset) + appendage_size + 1);
char cut = source_offset[position];
source_offset[position] = '\0';
sprintf( result, "%s%s%c%s", source_offset,appendage,cut,source_offset+position+1);
// do hat you want with result
Beware that realloc may change the base address of the initial memory, so you can't do things like this as the parameter value source_offset will be changed only locally.
size_t appendage_size = strlen(appendage) + 1; /* To hold a \0 character */
The new array allocated should be able to hold both the null characters in each string
strlen(string) + 1
actually your coes does not append string B to string A, it does append string B at position n of string A along with string A, with some out of memory offset of String A
char *
strapp(char *dest, const char *src)
{
size_t i,j;
for (i = 0; dest[i] != '\0'; i++)
;
for (j = 0; src[j] != '\0'; j++)
dest[i+j] = src[j];
dest[i+j] = '\0';
return dest;
}
the above code appends string B to String A and returns pointer to string a A;
char* strapp (char *source_offset, const char *appendage, int position)
{
char* temp = NULL:
size_t appendage_size = strlen(appendage);
if (( temp = realloc(source_offset, strlen(source_offset) + appendage_size + 1)) == NULL )
return NULL;
else
source_offset = temp;
sprintf( &source_offset[position], "%s", appendage);
return source_offset;
}
If you want to fix your function try this:
void strapp (char **source_offset, int position, char *appendage)
{
size_t appendage_size = strlen(appendage);
*source_offset = (char*)realloc(*source_offset, strlen(*source_offset) + appendage_size);
sprintf( *source_offset + position, "%s%s", appendage, *source_offset + (position + appendage_size) );
}
And call this like strapp(&str, 10, "INSERTION"); // &str - pointer to pointer.
The problem was in the following: when you sent pointer to function you are able to change data pointed by this pointer, but cannot change the pointer, so memory allocation made inside function do mot change address of initial string.
Since I was asked to provide an example of function usage and respectively the output. I converted this code to something compilable to mingw and it works like expected.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void strapp (char *source_offset, int position, char *appendage)
{
size_t appendage_size = strlen(appendage) + 1;
source_offset = realloc(source_offset, strlen(source_offset) + appendage_size);
sprintf( &source_offset[position], "%s%s", appendage, source_offset + (position + appendage_size) );
}
int main(void)
{
char *str1 = malloc(11 + 1);
sprintf(str1, "Hello World");
strapp(str1, 4, " Horrible ");
printf(str1);
free(str1);
return 0;
}
Output: Hell Horrible
Partial fix of the problem I found while investigating.
If I add a substring to a location of the source string like that:
sprintf(&source[4], "new");
the source string will be terminated by the "new" and the rest of the string will not be shown.
But if I declare the substring "new" and use
memcpy(&str1[4], appendage, 3); it does the job.
There, this is the working function:
void strapp (char *source_offset, int position, char *appendage)
{
size_t appendage_size = strlen(appendage) + 1;
char copy[strlen(source_offset) + 1];
strcpy(copy, source_offset);
source_offset = realloc(source_offset, strlen(source_offset) + appendage_size);
memcpy(&source_offset[position], appendage, strlen(appendage));
sprintf(&source_offset[position + strlen(appendage)], &copy[position]);
}

Resources