I'm quite new to C and am trying to write a function, which will split a string into an array of strings at a specific delimiter. But strangely I can only write at the first index of my char** array of strings, which will be my result. For example if I want to split the following string "Hello;;world;;!" at ;; I get [ "Hello" ] instead of [ "Hello", "world", "!" ]. I can't find my mistake.
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "strings.h"
int split(char **dest, const char *src, const char *splitStr) {
char buffer[16384];
int counter = 0;
int len = strlen(splitStr);
int flag = 0;
int start = 0;
for (int i = 0; i < strlen(src); i++) {
flag = 0;
if (src[i] == splitStr[0]) {
for (int j = 1; j < len; j++) {
//check if all elements in delimiter are in string
if (src[i + j] == splitStr[j] && j != (len - 1)) {
continue;
}
else if(src[i + j] == splitStr[j] && j == (len - 1)) {
buffer[i] = '\0';
dest[counter] = malloc(sizeof(char) * (i - start + 1));
strncpy(dest[counter], buffer + start, (i - start));
start = i + (len-1)
flag = 1;
i += (len - 1);
counter++;
}
//if not break
else {
break;
}
}
}
if (i == (strlen(src) - 1)) {
buffer[i] = src[i];
buffer[i + 1] = '\0';
counter++;
break;
}
if (flag == 0) {
buffer[i] = src[i];
}
}
return counter;
}
A proper function call would look like this:
auto src = "Hello;;world;;!";
auto buffer = (char **)malloc(32);
int count = split(buffer, src, ";;");
The buffer should contain, all the splitted strings, more or less like this: [ "Hello", "world", "!" ].
Currently my result buffer looks like this in the debugger. It appears as only the first element is written into it.
There are multiple problems in your code:
you compute string lengths repeatedly, which may be very inefficient. Instead of testing i < strlen(src) you should write src[i] != '\0'.
your test for check a matching delimiter is too complicated. You should use strstr to locate the delimiter string in the remaining portion of the string.
strncpy does not do what you think: strncpy(dest[counter], buffer + start, (i - start)); should be replaced with memcpy(dest[counter], buffer + start, i - start); and you must set the null terminator explicitly: dest[counter][i - start] = '\0'; You should read why you should never use strncpy().
it is unclear why you use buffer at all.
Here is a modified version:
#include <stdlib.h>
#include <string.h>
/* if POSIX function strndup() is not defined on your system, use this */
char *strndup(const char *str, size_t n) {
size_t len;
for (len = 0; len < n && str[len] != '\0'; len++)
continue;
char *s = malloc(len + 1);
if (s != NULL) {
memcpy(s, str, len);
s[len] = '\0';
}
return s;
}
int split(char **dest, const char *src, const char *splitStr) {
const char *p = str;
const char *end;
int counter = 0;
size_t len = strlen(splitStr);
if (len == 0) {
/* special case */
while (*p != '\0') {
dest[counter++] = strndup(p++, 1);
}
} else {
while ((end = strstr(p, splitStr)) != NULL) {
dest[counter++] = strndup(p, end - p);
p = end + len;
}
dest[counter++] = strdup(p);
}
return counter;
}
First of all you are not updating the start variable after you have copied the first string.
For simple debugging I would recommend adding some printf statements to see what is going on.
Proper formatting is not to be underestimated to make the code easy to read and easier to debug.
Also it is not clear what the buffer is for, and I think you can do without it.
The tips in the comments are also good. Split the function into smaller pieces and structure your code so it is simple to read.
A suggestion is to write a function to find the index of the next split string and the end of the string. Then you can use that to get the index and length you need to copy.
Related
EDIT: I've edited my code, and this is the result:
#include <stdlib.h>
#include <ctype.h>
char *trim(const char *s) {
if (s == NULL) {
return NULL;
}
size_t count_1 = 0;
for (size_t i = 0; s[i] != '\0'; i++) {
count_1++;
}
if (count_1 < 1) {
return NULL;
}
size_t count_2 = 0;
if (isspace(s[0])) {
count_2++;
}
if (isspace(s[count_1 - 1])) {
count_2++;
}
size_t max_length = (count_1 - count_2) + 1u;
if (max_length >= count_1) {
return NULL;
}
char *str = malloc(max_length);
if (!str) {
return NULL;
}
for (size_t i = 0; s[i] != '\0'; i++) {
if (isspace(s[i]) == 0) { // if isspace is false.
str[i] = s[i];
}
}
str[count_1 - count_2] = 0;
return str;
}
int main(void) {
char s[] = " a b ";
char *str;
str = trim(s);
free(str);
return 0;
}
now, the problem is here
for (size_t i = 0; s[i] != '\0'; i++) {
if (isspace(s[i]) == 0) { // if isspace is false.
str[i] = s[i];
}
I have a buffer overrun, even if I've checked the length. In fact, if count_1 is equal to zero, I have a buffer overrun error, but I've excluded this case, but the problem persists. By debugging line-by-line, I've noticed I have an undefined behavior.
I wanted to try to simplify the suggested solution for this exercise, therefore I've written another code for the same exercise.
this is the original answer: trim function halve the memory size to remove the whitespaces?
this is the minimal reproducible code:
#include <stdlib.h>
#include <ctype.h>
char *trim(const char *s) {
size_t count_1 = 0;
for (size_t i = 0; s[i] != '\0'; i++) {
count_1++;
}
size_t count_2 = 0;
if (isspace(s[0])) {
count_2++;
}
if (isspace(s[count_1])) {
count_2++;
}
size_t max_length = (count_1 - count_2) + 1u;
if (max_length >= count_1) {
return NULL;
}
char *str = malloc(max_length);
if (!str) {
return NULL;
}
for (size_t i = 0; s[i] != '\0'; i++) {
if (isalpha(s[i]) == 0) { // if isalpha is false.
str[i] = s[i];
}
str[count_1 - count_2] = 0;
}
return str;
}
int main(void) {
char s[] = " a b ";
char *str;
str = trim(s);
free(str);
return 0;
}
here's the detailed explanation about what I've done so far:
I've counted characters of the string s, and the length is stored in count_1.
I've counted how many whitespaces I have at the beginning of the string, and at the end of the string; and the amount is stored in count_2.
note: I've chosen to use isspace function (in <ctype.h>), because I tried to type ' ' (i.e a whitespace), but the result is not correct, and these if-checks are not evaluated whatsoever. (I used the debugger line-by-line to state this thing).
before malloc the memory I've used a check condition to avoid buffer overrun (it's similar to the question I asked yesterday), meaning I've allocated enough memory if and only if max_length is less than count_1. doing this way, I have no buffer overrun warning.
I think I can avoid to explain the final steps, because they are self explanatory and I also think they doesn't cause errors. If I'm wrong, I'll edit this point.
issue I have no clue how to fix it:
by debugging line-by-line, I've noticed that when the flow of execution goes to the 2nd if-check, the if body is not executed whatsoever. And this is strange, because the first one works fine.
There are multiple problems in your code:
count_1 is the length of the string, you should name it more explicitly as len
you return NULL if no trimming is needed. This is questionable. You should probably return a copy of the string in all cases and only return NULL in case of allocation failure.
you only test for 1 space char at the start of the string.
you only test for 1 space char at the end of the string.
furthermore this space might be counted twice if the string is " ".
max_length is a misnomer: it is not the length of the new string, but the allocation size, new_size seems more appropriate.
in the final loop, you use the same index i into the original and the new string: this is incorrect. You should use a separate index so characters from the original string can be copied after skipping the initial space.
str[count_1 - count_2] = 0; is redundant inside the loop: you should move this statement after the end of the loop.
argument values of type char should be cast as (unsigned char) when passed to the functions and macros defined in <ctype.h> to avoid undefined behavior on negative values on platforms where the char type is signed. These functions are only defined for the values of type unsigned char (between 0 and UCHAR_MAX) and the special negative value EOF. These values are the ones returned by getchar() and getc().
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
char *trim(const char *s) {
if (s == NULL) {
return NULL;
}
size_t start, end;
for (start = 0; isspace((unsigned char)s[start]); start++) {
continue;
}
for (end = start; s[end] != '\0'; end++) {
continue;
}
while (end > start && isspace((unsigned char)s[end - 1])) {
end--;
}
// if you are allowed to use strndup, you can return the new string this way:
//return strndup(str + start, end - start);
char *new_str = malloc(end - start + 1);
if (new_str) {
size_t j = 0; // index into the new string
for (size_t i = start; i < end; i++) {
new_str[j++] = str[i];
}
new_str[j] = '\0';
}
return new_str;
}
int main(void) {
char s[] = " a b ";
char *str = trim(s);
printf("trim(\"%s\") -> \"%s\"\n", s, str);
free(str);
return 0;
}
My problem now is that I have taken space for different words,but I'm having problems storing this as an array. Even though there are some similar posts like this, nothing seems to work for me and I'm completely stuck here. I want to keep this format(i don't want to change the definition of the function). Grateful for all help and comments!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int i, len = 0, counter = 0;
char ** p = 0;
for(i = 0; s[i] != '\0'; i++){
len++;
if(s[i] == ' ' || s[i+1] == '\0'){
counter ++;
for(i = 0; i < len; i++){
p[i] = s[i];
}
}
printf("%d\n", len);
printf("%d\n", counter);
return p;
}
int main() {
char *s = "This is a string";
int n;
int i;
for(i = 0; i < n*; i++){
//also not sure how to print this
}
}
I edited your code and it's now working correctly:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char** split(const char* s, int *n);
char** split(const char* s, int *n) {
int i, len = 0, counter = 0;
char ** p = 0;
for(int i = 0; ; ++i) {
if(s[i] == '\0') {
break;
}
if(s[i] == ' ') {
counter += 1;
}
}
++counter;
p = (char **) malloc(counter * sizeof(char*));
for(int i = 0, c = 0; ; ++i, ++c) {
if(s[i] == '\0') {
break;
}
len = 0;
while(s[len + i + 1] != ' ' && s[len + i + 1] != '\0') {
++len;
}
p[c] = (char *) malloc(len * sizeof(char) + 1);
int k = 0;
for(int j = i; j < i + len + 1; ++j) {
p[c][k++] = s[j];
}
p[c][k] = '\0';
i += len + 1;
}
*n = counter;
return p;
}
int main() {
char *s = "This is a string";
int n;
int i;
char** split_s = split(s, &n);
for(i = 0; i < n; i++) {
printf("%s\n", split_s[i]);
}
}
But I suggest you do a little bit clean-up.
Here is a solution using sscanf. scanf and sscanf considers space as an end of input. I have taken benefit of that to make it work for you.
char *str = (char*) "This is a string";
char buffer[50];
char ** p = (char**)malloc(1 * sizeof(*p));
for (int i = 0; str[0] != NULL; i++)
{
if (i > 0)
{
p = (char**)realloc(p, i * sizeof(p));
}
sscanf(str, "%s", buffer);
int read = strlen(buffer);
str += read + 1;
p[i] = (char*)malloc(sizeof(char)*read + 1);
strcpy(p[i], buffer);
printf("%s\n", p[i]);
}
Since this pointer is growing in both the dimensions, every time a new string is found we need to resize the p itself and then the new address that it contains should be resized too .
My problem now is that I have taken space for different words using malloc, but I'm having problems storing this as an array.
When addressable memory for a collection of strings is needed, then a collection of pointers, as well as memory for each pointer needed.
In your code:
p = (char**)malloc(counter*sizeof(char*));
You have created the collection of pointers, but you have not yet created memory at those locations to accommodate the strings. (By the way, the cast is not necessary)
Here are the essential steps to both create a collection of pointers, and memory for each:
//for illustration, pick sizes for count of strings needed,
//and length of longest string needed.
#define NUM_STRINGS 5
#define STR_LEN 80
char **stringArray = NULL;
stringArray = malloc(NUM_STRINGS*sizeof(char *));// create collection of pointers
if(stringArray)
{
for(int i=0;i<NUM_STRINGS;i++)
{
stringArray[i] = malloc(STR_LEN + 1);//create memory for each string
if(!stringArray[i]) //+1 room for nul terminator
{
//handle error
}
}
}
As a function it could look like this: (replacing malloc with calloc for initialized space)
char ** Create2DStr(size_t numStrings, size_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
using this in your split() function:
char** split(const char* s, int *n){
int i, len = 0, counter = 0, lenLongest = 0
char ** p = 0;
//code to count words and longest word
p = Create2DStr(counter, longest + 1); //+1 for nul termination
if(p)
{
//your searching code
//...
// when finished, free memory
Let's start at the logic.
How does a string like A quick brown fox. get processed? I would suggest:
Count the number of words, and the amount of memory needed to store the words. (In C, each string ends with a terminating nul byte, \0.)
Allocate enough memory for the pointers and the words.
Copy each word from the source string.
We have a string as an input, and we want an array of strings as output. The simplest option is
char **split_words(const char *source);
where the return value is NULL if an error occurs, or an array of pointers terminated by a NULL pointer otherwise. All of it is dynamically allocated at once, so calling free() on the return value will free both the pointers and their contents.
Let's start implementing the logic according to the bullet points above.
#include <stdlib.h>
char **split_words(const char *source)
{
size_t num_chars = 0;
size_t num_words = 0;
size_t w = 0;
const char *src;
char **word, *data;
/* Sanity check. */
if (!source)
return NULL; /* split_words(NULL) will return NULL. */
/* Count the number of words in source (num_words),
and the number of chars needed to store
a copy of each word (num_chars). */
src = source;
while (1) {
/* Skip any leading whitespace (not just spaces). */
while (*src == '\t' || *src == '\n' || *src == '\v' ||
*src == '\f' || *src == '\r' || *src == ' ')
src++;
/* No more words? */
if (*src == '\0')
break;
/* We have one more word. Account for the pointer itself,
and the string-terminating nul char. */
num_words++;
num_chars++;
/* Count and skip the characters in this word. */
while (*src != '\0' && *src != '\t' && *src != '\n' &&
*src != '\v' && *src != '\f' && *src != '\r' &&
*src != ' ') {
src++;
num_chars++;
}
}
/* If the string has no words in it, return NULL. */
if (num_chars < 1)
return NULL;
/* Allocate memory for both the pointers and the data.
One extra pointer is needed for the array-terminating
NULL pointer. */
word = malloc((num_words + 1) * sizeof (char *) + num_chars);
if (!word)
return NULL; /* Not enough memory. */
/* Since 'word' is the return value, and we use
num_words + 1 pointers in it, the rest of the memory
we allocated we use for the string contents. */
data = (char *)(word + num_words + 1);
/* Now we must repeat the first loop, exactly,
but also copy the data as we do so. */
src = source;
while (1) {
/* Skip any leading whitespace (not just spaces). */
while (*src == '\t' || *src == '\n' || *src == '\v' ||
*src == '\f' || *src == '\r' || *src == ' ')
src++;
/* No more words? */
if (*src == '\0')
break;
/* We have one more word. Assign the pointer. */
word[w] = data;
w++;
/* Count and skip the characters in this word. */
while (*src != '\0' && *src != '\t' && *src != '\n' &&
*src != '\v' && *src != '\f' && *src != '\r' &&
*src != ' ') {
*(data++) = *(src++);
}
/* Terminate this word. */
*(data++) = '\0';
}
/* Terminate the word array. */
word[w] = NULL;
/* All done! */
return word;
}
We can test the above with a small test main():
#include <stdio.h>
int main(int argc, char *argv[])
{
char **all;
size_t i;
all = split_words(" foo Bar. BAZ!\tWoohoo\n More");
if (!all) {
fprintf(stderr, "split_words() failed.\n");
exit(EXIT_FAILURE);
}
for (i = 0; all[i] != NULL; i++)
printf("all[%zu] = \"%s\"\n", i, all[i]);
free(all);
return EXIT_SUCCESS;
}
If we compile and run the above, we get
all[0] = "foo"
all[1] = "Bar."
all[2] = "BAZ!"
all[3] = "Woohoo"
all[4] = "More"
The downside of this approach (of using one malloc() call to allocate memory for both the pointers and the data), is that we cannot easily grow the array; we can really just treat it as one big clump.
A better approach, especially if we intend to add new words dynamically, is to use a structure:
typedef struct {
size_t max_words; /* Number of pointers allocated */
size_t num_words; /* Number of words in array */
char **word; /* Array of pointers */
} wordarray;
Unfortunately, this time we need to allocate each word separately. However, if we use a structure to describe each word in a common allocation buffer, say
typedef struct {
size_t offset;
size_t length;
} wordref;
typedef struct {
size_t max_words;
size_t num_words;
wordref *word;
size_t max_data;
size_t num_data;
char *data;
} wordarray;
#define WORDARRAY_INIT { 0, 0, NULL, 0, 0, NULL }
static inline const char *wordarray_word_ptr(wordarray *wa, size_t i)
{
if (wa && i < wa->num_words)
return wa->data + wa->word[i].offset;
else
return "";
}
static inline size_t wordarray_word_len(wordarray *wa, size_t i)
{
if (wa && i < wa->num_words)
return wa->word[i].length;
else
return 0;
}
The idea is that if you declare
wordarray words = WORDARRAY_INIT;
you can use wordarray_word_ptr(&words, i) to get a pointer to the ith word, or a pointer to an empty string if ith word does not exist yet, and wordarray_word_len(&words, i) to get the length of that word (much faster than calling strlen(wordarray_word_ptr(&words, i))).
The underlying reason why we cannot use char * here, is that realloc()ing the data area (where the word pointers would point to) may change its address. If that were to happen, we'd have to adjust every pointer in our array. It is much easier to use offsets to the data area instead.
The only downside to this approach is that deleting words does not mean a corresponding shrinkage in the data area. However, it is possible to write a simple "compactor" function, that repacks the data to a new area, so that holes left by deleted words are "moved" to the end of the data area. Usually, this is not necessary, but you might wish to add a member to the wordarray structure, say the number of lost characters from word deletions, so that the compaction can be done heuristically the next time the data area would be otherwise resized.
I have a char array, and I want to insert hyphens after every five characters. I have tried the following code but am looking for a better way to accomplish this task, what string function should I use, and how? Thanks. :D
#include <stdio.h>
#include <string.h>
int main()
{
char str[]="356a192b7913b04c54574d18c28d46e6395428ab";
char buf[50];
sprintf(buf, "%.5s-%.5s-%.5s-%.5s-%.5s-%.5s-%.5s-%.5s",str, str+5,str+10,str+15,str+20,str+25,str+30,str+35,str+40);
printf("%s",buf);
return 0;
}
You know you want (length - 1) / 5 hyphens. So for buf, you need storage for buf[len + (len -1) / 5 + 1]; characters. Then simply loop over the characters in str and (skipping the case where i = 0) if i % 5 == 0 write a hyphen to buf in addition to copying the character from str.
Putting it together, you can do something similar to:
#include <stdio.h>
#include <string.h>
#define HYPHPOS 5
int main(void) {
char str[] = "356a192b7913b04c54574d18c28d46e6395428ab";
size_t len = strlen (str);
char buf[len + (len -1)/ HYPHPOS + 1], *p = buf;
for (size_t i = 0; i < len; i++) {
if (i && i % HYPHPOS == 0)
*p++ = '-';
*p++ = str[i];
}
*p = 0;
printf ("str : %s\nbuf : %s\n", str, buf);
return 0;
}
You can also simply use str[i] as the loop test clause.
Example Use/Output
$ ./bin/hyphenate
str : 356a192b7913b04c54574d18c28d46e6395428ab
buf : 356a1-92b79-13b04-c5457-4d18c-28d46-e6395-428ab
Look things over and let me know if you have further questions.
Here's a helper function I whipped up for you. It takes a string as a parameter along with the desired hyphens spacing and returns a new string with hyphens within it. Notice it makes sure that there's no trailing hyphens at the end of the string.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* hyphenize(const char* str, int spacing)
{
size_t len, newlen;
char *ptr, *buf;
if (spacing < 1)
{
return NULL; // error!
}
len = strlen(str);
newlen = len + (len / spacing) + 1;
buf = (char*)malloc(newlen);
ptr = buf;
for (size_t x = 0; x < len; x++)
{
*ptr++ = str[x];
if ((x != 0) && (x != (len - 1)) && ((x + 1) % spacing == 0))
{
*ptr++ = '-';
}
}
*ptr = '\0';
return buf;
}
int main()
{
char str[] = "356a192b7913b04c54574d18c28d46e6395428ab";
char* p = hyphenize(str, 5);
printf("%s\n", p);
free(p);
return 0;
}
I tried to make a function which replace every word in a text with the word shifted to right by 'k' times.
the code look like this:
void operation_3(char *string, int k){
int len = 0, i;
int string_len = strlen(string);
char *word;
char s[12] = " .,?!\"'";
char *dup;
dup = strdup(string);
word = strtok(dup, s);
while (word != NULL) {
len = strlen(word);
char *new_word = (char *)malloc(len * sizeof(char));
for (i = 0; i < k; i++) {
new_word = shift_to_right(word);
}
string = replace_word(string, word, new_word);
word = strtok(NULL, s);
}
}
shift_to_right is:
char *shift_to_right(char *string){
char temp;
int len = strlen(string) - 1;
int i;
for (i = len - 1; i >= 0; i--) {
temp = string[i+1];
string[i+1] = string[i];
string[i] = temp;
}
return string;
}
replace_word is:
char *replace_word(char *string, char *word, char *new_word) {
int len = strlen(string) + 1;
char *temp = malloc(len * sizeof(char));
int temp_len = 0;
char *found;
while (found = strstr(string, word)) {
if (strlen(found) != strlen(word) || isDelimitator(*(found - 1)) == 1) {
break;
}
memcpy(temp + temp_len, string, found - string);
temp_len = temp_len + found - string;
string = found + strlen(word)
len = len - strlen(word) + strlen(new_word);
temp = realloc(temp, len * sizeof(char));
memcpy(temp + temp_len, new_word, strlen(new_word));
temp_len = temp_len + strlen(new_word);
}
strcpy(temp + temp_len, string);
return temp;
}
and isDelimitator is:
int isDelimitator(char c) {
if(c == ' ' || c == '.' || c == ',' || c == '?' || c == '!' ||
c == '"' || c == '\0' || c == '\'') {
return 0;
}
else return 1;
}
I tested shift_to_right, replace_word and isDelimitator and work fine. But the final function, operation_3 doesn't work as expected. For example, for input: "Hi I am John" and for k = 1 the output is : "Hi I am John". Basically operation_3 doesn't modify the string. Any advice, corrections please?
There are a few things which I see are possibly the reason for error.
1) In operation_3 you do this : new_word = shift_to_right(word); And, in the definition of char *shift_to_right(char *string) you modify the string itself and return a pointer to it. So, if you called shift_to_right(word) and word = "Hi" then after the execution of shift_to_right both word and new_word are now pointing to the same string "iH", so in replace_word when you pass both the words and check for the substring word you will always get NULL, because, there is no substring "iH".
A possible solution, in shift_to_right add a statement,
char *new_string = strdup(string);
and instead of swapping the characters in string, swap the characters now in new_string and return the new_string from the function.
Your code shall look like this ::
char *shift_to_right(char *string){
char temp;
int len = strlen(string) - 1;
char *new_string = strdup(string);
int i;
for (i = len - 1; i >= 0; i--) {
temp = new_string[i+1];
new_string[i+1] = new_string[i];
new_string[i] = temp;
}
return new_string;
}
2) In the function replace_word, for a moment let us consider that the above mentioned error does not occur and replace_word get called with the parameters :: replace_word(string, "Hi", "iH");.
So, when you perform found = strstr(string, word), it gives you a pointer to the first letter where Hi started. So, in this case, if your string was "Hi I am John", then you get a pointer to the first H, and when you perform strlen(found) you will get 12(length of string left starting from the pointer) as the output, and strlen(word) will always be less (unless found points to the last word in the string), so in most cases your if condition becomes true and you break from the loop, without any swapping.
Moreover, as you yourself pointed out in the comments that strstr will return Johns as well if you want a substring John the only solution for this would be to run a loop and check that in string after John if there is delimiter character or not, if there is no delimiter character, then this is not the substring that you needed.
replace_word shall look something like this ::
void replace_word(char *string, char *word, char *new_word) {
char *found = strstr(string, word);
int len = strlen(word);
while(found) {
char temp = *(found + len);
if(isDelimeter(temp) == 0) {
break;
} else {
found = strstr(found + len + 1);
}
}
if(found != NULL) {
for(int i = 0; i < len; i++) {
*(found + i) = new_word[i]; // *(found + i) is accessing the i^th, character in string from the pointer found
}
}
}
I think this replace_word shall work, you can directly modify the string, and there is no need to actually make a temp string and return it. This reduces the need of allocating new memory and saving that pointer.
I hope this could help!
EDIT :: Since we have been using strdup in the code, which dynamically allocates memory of the size of the string with an extra block for the \0 character, we shall take care of freeing it explicitly, so it will be a good idea according to me free the allocated memory in replace_word just before we exit the function since the new_word is useless after it.
Moreover, I saw a statement in your code::
1) char *new_word = (char *)malloc(len * sizeof(char));
Just before you start the shifting the words, I hope you understand that you do not need to do it. new_word is just a pointer, and since we now allocated memory to it in strdup we do not need to do it. Even before, considering the code that you had written there was no reason to allocate memory to new_word since you were returning the address of the array, which was already in the stack, and would stay in the stack till the end of the execution of the program.
This code is simpler than what you have, and it prints all the word delimiters that were in the input string. And rather than looking for specific punctuation characters, it checks alphanumeric instead.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
char instr[] = "Hi! I am 'John' ;)";
int lennin = strlen(instr);
int shifts, i, len, index, start, next;
printf("Working with %s\n", instr);
for(shifts=0; shifts<5; shifts++) { // various examples
printf("Shifts = %d ", shifts);
start = 0;
while(start < lennin) {
while (start < lennin && !isalnum(instr[start])) { // find next alphanum
printf("%c", instr[start]); // output non-alphanum
start++;
}
next = start + 1;
while (isalnum(instr[next])) // find next non-alphanum
next++;
len = next - start;
for(i=0; i<len; i++) { // shift the substring
index = i - shifts;
while(index < 0) index += len; // get index in range
printf("%c", instr[start + (index % len)]); // ditto
}
start = next; // next substring
}
printf("\n");
}
return 0;
}
Program output:
Working with Hi! I am 'John' ;)
Shifts = 0 Hi! I am 'John' ;)
Shifts = 1 iH! I ma 'nJoh' ;)
Shifts = 2 Hi! I am 'hnJo' ;)
Shifts = 3 iH! I ma 'ohnJ' ;)
Shifts = 4 Hi! I am 'John' ;)
I'm trying to build a string from f, being split at whitespace and read it into a struct.
f is the char array I'm iterating over.
I then copy the contents from tmp into ra1.callsign, and essentially empty the tmp char array.
What I want to do is have the the tmp variable start building from index 0 again, so that when I try to strcpy the second time round all the characters in tmp start from index 0.
The way I have it now, when it tries the line: strcpy(ra1.location, tmp) it doesn't copy anything, I think this is because at that point the first character in tmp doesn't appear until some time down the array.
char c;
char tmp[1000];
for (i = 0; i < len; ++i) {
c = f[i];
if (c != ' ') {
tmp[i] = c; //build string to be added
}
//add string to data structure
if (c == ' ') {
if (addTo == CALLSIGN) {
strncpy(ra1.callsign, tmp, strlen(tmp));
memset(tmp, '\0', strlen(tmp));
}
if (addTo == LOCATION) {
strcpy(ra1.location, tmp);
}
++addTo;
}
}
Hope this is clear enough, thanks.
You left out quite a few details in your code and I have made a number of assumptions.
So, using the assumptions that I have made (which you can see in the code below), I believe that this will do what you are trying to accomplish. There are much easier and cleaner ways to do this, but I am hoping that you can get a clear understanding of how it would work with your code.
I have basically added a terminating null character where it is required so the strlen() function will work correctly and utilized an extra variable called cur_size which can be used as an offset based on the current index i.
#include <string.h>
#include <stdio.h>
#define CALLSIGN 3U
#define LOCATION 5U
#define ARRAY_SIZE 50U
typedef struct
{
char callsign[ARRAY_SIZE];
char location[ARRAY_SIZE];
} MyStruct;
MyStruct ra1 = { .callsign = {0}, .location = {0} };
char f[] = "This is my character array. Let's see what happens.";
int main (void)
{
char c;
char tmp[ARRAY_SIZE];
unsigned char addTo = 0;
unsigned char i;
unsigned char cur_size = 0;
for(i = 0; i < sizeof(f); ++i)
{
c = f[i];
if(c != ' ')
{
tmp[i - cur_size] = c; //build string to be added
}
//add string to data structure
if(c == ' ')
{
tmp[i - cur_size] = '\0'; /* YOU NEED THIS FOR strlen(tmp) to work */
cur_size = i + 1;
if(addTo == CALLSIGN)
{
strncpy(ra1.callsign, tmp, strlen(tmp));
//memset(tmp, '\0', strlen(tmp));
}
else if (addTo == LOCATION)
{
strncpy(ra1.location, tmp, strlen(tmp));
}
++addTo;
}
}
for (i = 0; i < ARRAY_SIZE; i++)
{
printf("%c", ra1.callsign[i]);
}
printf("\r\n");
for (i = 0; i < ARRAY_SIZE; i++)
{
printf("%c", ra1.location[i]);
}
printf("\r\n");
return 0;
}