For example:
input(string): foo $$ foo ## foo []
search(string): foo
output(array): $$ ,## ,[]
I tried it:
char * str = "foo $$ foo ## foo []";
char * s = "foo";
int buf_len = 0;
int len = strlen(s);
int i = 0;
char ** buffer = malloc(MAX_BUFFER_SIZE);
char * tmpbuf = malloc(MAX_BUFFER_SIZE);
char * p = str;
char ** buf = buffer;
char * tbuf = tmpbuf;
while(*p)
{
if(*p == *s)
{
while(*p == *(s + i))
{
i++;
p++;
}
if(i == len)
{
*buf ++ = tbuf;
memset(tbuf,0,buf_len);
i = buf_len = 0;
}
}
else
{
*tbuf ++= *p;
buf_len ++;
}
p++;
}
*buf ++= NULL;
int x;
for(x = 0; buffer[x]; x++)
{
printf("%s\n", buffer[x]);
}
free(buffer);
free(tmpbuf);
that show the following output:
$$ ## []
## []
[]
but the expected is:
$$
##
[]
how to fix this?
Here's a function for splitting a string into an array of strings:
#include <assert.h>
#include <string.h>
/*
* Split a string by a delimiter.
*
* This function writes the beginning of each item to #pointers_out
* (forming an array of C strings), and writes the actual string bytes
* to #bytes_out. Both buffers are assumed to be big enough for all of the
* strings.
*
* Returns the number of strings written to #pointers_out.
*/
size_t explode(const char *delim, const char *str,
char **pointers_out, char *bytes_out)
{
size_t delim_length = strlen(delim);
char **pointers_out_start = pointers_out;
assert(delim_length > 0);
for (;;) {
/* Find the next occurrence of the item delimiter. */
const char *delim_pos = strstr(str, delim);
/*
* Emit the current output buffer position, since that is where the
* next item will be written.
*/
*pointers_out++ = bytes_out;
if (delim_pos == NULL) {
/*
* No more item delimiters left. Treat the rest of the input
* string as the last item.
*/
strcpy(bytes_out, str);
return pointers_out - pointers_out_start;
} else {
/*
* Item delimiter found. The bytes leading up to it form the next
* string.
*/
while (str < delim_pos)
*bytes_out++ = *str++;
/* Don't forget the NUL terminator. */
*bytes_out++ = '\0';
/* Skip over the delimiter. */
str += delim_length;
}
}
}
Usage:
#include <stdio.h>
/* ... */
#define BIG_ENOUGH 1000
int main(void)
{
char *items[BIG_ENOUGH];
char item_bytes[BIG_ENOUGH];
size_t i;
size_t count;
count = explode("foo", "foo $$ foo ## foo []", items, item_bytes);
for (i = 0; i < count; i++)
printf("\"%s\"\n", items[i]);
return 0;
}
Output:
""
" $$ "
" ## "
" []"
This does not produce the exact output you asked for, as I'm not sure how you want to handle surrounding spaces and occurrences of the item delimiter (in your example, "foo") at the beginning of the string. Instead, I mimicked PHP's explode function.
I'd like to point out how my explode function punts on memory management. It is up to the caller to ensure the buffers are big enough. This is fine for a quick script, but might be annoying in a more serious program, where you'll have to do some math to use this function correctly. I could have written a more "robust" implementation that performs its own allocation, but:
That would clutter the implementation.
It doesn't give the caller the option of using their own memory allocator.
So implementing explode the way I did is "bad" because it is hard to use correctly, and worse, easy to use incorrectly. On the other hand, it is "good" in that it separates the concerns of functionality and memory management.
It is because you do not copy the contents of tbuf to buf when you say:
*buf ++ = tbuf;
What you do is save a reference to the current position in tbuf (or tmpbuf if you like).
tmpbuf get filled with everything but the delimiter.
It is something like, at end of loop:
01234567 <- offset
tmpbuf = "$$ ## []"
buf[0] = tmpbuf+0;
buf[1] = tmpbuf+3;
buf[2] = tmpbuf+6;
Or very simplified memory table:
memory
address value
tmpbuf -> 0x01 [ $] <- buffer[0] points here
0x02 [ $]
0x03 [ ]
0x04 [ #] <- buffer[1] points here
0x05 [ #]
0x06 [ ]
0x07 [ [] <- buffer[2] points here
0x08 [ ]]
0x09 [ ]
...
buffer -> 0x3A [0x01]
0x3B [0x04]
0x3C [0x07]
0x3D [ ]
0x3E [ ]
...
EDIT
For the phun of it; a pointer, dynamic, way, not using strstr().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int is_needle(char *hay, char *needle)
{
while (*hay && *++hay == *++needle);
return *needle == '\0';
}
char *find(char *hay, char *needle)
{
while (*hay) {
if (*hay == *needle && is_needle(hay, needle))
return hay;
++hay;
}
return hay;
}
int pushstr(char ***vs, size_t *vslen, char *val, size_t slen)
{
char **vsp = *vs + *vslen;
if ((*vsp = realloc(*(*vs + *vslen), slen + 1)) == NULL) {
perror("pushstr.1"); exit(1);
}
memcpy(*vsp, val, slen);
*(*vsp + slen) = '\0';
if ((*vs = realloc(*vs, sizeof(char*) * (++*vslen + 1))) == NULL) {
perror("pushstr.2"); exit(1);
}
*(*vs + *vslen) = NULL;
return *vslen;
}
int main(void)
{
char *hay = "foo $$ foo ## foo [] fox ## foo ??";
char *needle = "foo";
char *np;
char **vs;
size_t vslen = 0;
size_t nlen = strlen(needle);
if ((vs = malloc(sizeof(char*))) == NULL) {
perror("main");
return 1;
}
*vs = NULL;
while (*(np = find(hay, needle))) {
if (np != hay) {
pushstr(&vs, &vslen, hay, np - hay);
hay = np + nlen;
} else {
hay += nlen;
}
}
if (np != hay)
pushstr(&vs, &vslen, hay, np - hay);
while (*vs)
printf("V: '%s'\n", *vs++);
vs -= vslen;
while (*vs)
free(*vs++);
vs -= vslen;
free(vs);
return 0;
}
This is a task for strstr(). I changed your code a little bit to make use of it.
int add_to_buf(char *str, size_t len, char ***buf)
{
if (len <= 0) return 0;
**buf = malloc (len);
strncpy (**buf, str, len);
++*buf;
return 1;
}
int main()
{
char *str = "foo $$ foo ## foo []";
char *s = "foo";
char **buffer = malloc (MAX_BUFFER_SIZE*sizeof(*buffer)), **buf = buffer;
char *start, *end;
int s_len = strlen (s);
start = str;
end = strstr (str, s);
while (end) {
add_to_buf (start, end-start, &buf);
start = end + s_len;
end = strstr (start, s);
}
add_to_buf (start, strlen (str) - (start-str), &buf);
*buf = 0;
for (buf = buffer; *buf; ++buf)
printf ("%s\n", *buf);
free (buffer);
return 0;
}
You are using too many pointers for a simple program and the the way you used them makes it hard to understand. One straightforward bug I see is you are using buffer** (array of strings) but you are only allocating a single string. You are this this array of strings to store the tokens, which will do some memory violation somewhere.
Since you want to print the tokens, you don't need to store them in a separate array. This will do:
#include<stdio.h>
#include<string.h>
int main(int ac, char*argv[]) {
char str[] = "foo $$ foo ## foo []";
char * s = "foo";
char *p;
p = strtok(str, " "); // tokenize
while(p!=NULL)
{
if(strcmp(p, s)) //print non matching tokens
printf("%s\n", p);
p = strtok(NULL, " ");
}
return 0;
}
Note that here the delimiter is whitespace which makes it easier here.
The strtok function was designed for this task:
#include <string.h>
...
char *token;
char *line = "LINE TO BE SEPARATED";
char *search = " ";
/* Token will point to "LINE". */
token = strtok(line, search);
/* Token will point to "TO". */
token = strtok(NULL, search);
Related
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;
}
I am using the below function to replace a sub-string in a given string
void ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *result = NULL;
int i, cnt = 0;
int tolen = strlen(to);
int fromlen = strlen(from);
if (*inputString == NULL)
return;
// Counting the number of times old word
// occur in the string
for (i = 0; (*inputString)[i] != '\0'; i++)
{
if (strstr((&(*inputString)[i]), from) == &(*inputString)[i])
{
cnt++;
// Jumping to index after the old word.
i += fromlen - 1;
}
}
// Making new string of enough length
result = (char *)malloc(i + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return;
memset(result, 0, i + cnt * (tolen - fromlen) + 1);
i = 0;
while (&(*inputString))
{
// compare the substring with the result
if (strstr(*inputString, from) == *inputString)
{
strncpy(&result[i], to, strlen(to));
i += tolen;
*inputString += fromlen;
}
else
{
result[i++] = (*inputString)[0];
if ((*inputString)[1] == '\0')
break;
*inputString += 1;
}
}
result[i] = '\0';
*inputString = result;
return;
}
The problem with the above function is memory leak. Whatever memory is allocated for inputString will be lost after this line.
*inputString = result;
since I am using strstr and moving pointer of inputString *inputString += fromlen; inputString is pointing to NULL before the above line. So how to handle memory leak here.
Note: I dont want to return the new memory allocated inside the function. I need to alter the inputString memory based on new length.
You should use a local variable to iterate over the input string and avoid modifying *inputString before the final step where you free the previous string and replace it with the newly allocated pointer.
With the current API, ReplaceSubStr must be called with the address of a pointer to a block allocated with malloc() or similar. Passing a pointer to local storage or a string literal will have undefined behavior.
Here are a few ideas for improvement:
you could return the new string and leave it to the caller to free the previous one. In this case, you would take the input string by value instead of by address:
char *ReplaceSubStr(const char *inputString, const char *from, const char *to);
If the from string is empty, you should either insert the to string between each character of the input string or do nothing. As posted, your code has undefined behavior for this border case.
To check if the from string is present at offset i, use memcmp instead of strstr.
If cnt is 0, there is nothing to do.
You should return an error status for the caller to determine if memory could be allocated or not.
There is no need to initialize the result array.
avoid using strncpy(). This function has counter-intuitive semantics and is very often misused. Read this: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
Here is an improved version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ReplaceSubStr(char **inputString, const char *from, const char *to) {
char *input = *inputString;
char *p, *q, *result;
size_t cnt;
size_t tolen = strlen(to);
size_t fromlen = strlen(from);
if (input == NULL || fromlen == 0)
return 0;
// Counting the number of times old word occurs in the string
for (cnt = 0, p = input; (p = strstr(p, from)) != NULL; cnt++) {
p += fromlen;
}
if (cnt == 0) // no occurrence, nothing to do.
return 0;
// Making new string of enough length
result = (char *)malloc(strlen(input) + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return -1;
for (p = input, q = result;;) {
char *p0 = p;
p = strstr(p, from);
if (p == NULL) {
strcpy(q, p0);
break;
}
memcpy(q, p0, p - p0);
q += p - p0;
memcpy(q, to, tolen);
q += tolen;
p += fromlen;
}
free(*inputString);
*inputString = result;
return 0;
}
int main() {
char *p = strdup("Hello world!");
ReplaceSubStr(&p, "l", "");
printf("%s\n", p); // prints Heo word!
free(p);
return 0;
}
You cannot obviously free the input as it can be a literal, some memory you don't control. That would cripple your function even more than now.
You could return the old value of inputString so you'd be able to free it if needed.
char *ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *old_string = *inputString;
...
return old_string;
}
The caller is responsible to free the contents of old_string if needed.
If not needed (we have to workaround the char ** input by assigning a valid writable array to a pointer to be able to pass this pointer:
char input[]="hello world";
char *ptr = input;
ReplaceSubStr(&ptr, "hello", "hi");
// input is now "hi world" in a different location
free(ptr); // when replaced string isn't needed
if needed:
char *input = strdup("hello world");
char *old_input = ReplaceSubStr(&input, "hello", "hi");
free(old_input);
or just
free(ReplaceSubStr(&input, "hello", "hi"));
then always (when replaced string isn't needed):
free(input);
The only constraint is that you cannot use a constant string literal as input (const char *input = "hello world") because of the prototype & the possible return of a char * to pass to free.
Hi I am at beginner level and I am using concept of variable argument functions to concatenate strings. Same function is called for different number of strings.
I am not able to calculate the length of the concatenated string which in turn means i am not allocating memory properly. My dear peers, please help!
/* Program to do string concatenation using the concept of variable arguments */
/********************************************************************************
* REQUIRED HEADER FILES
*********************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stdarg.h>
/********************************************************************************
* REQUIRED MACROS DEFINED
*********************************************************************************/
#define ERROR_CHECKER(result)\
if(result == FAILURE)\
{\
printf("\n CONCATENATION FAILED");\
}
typedef enum {SUCCESS = 0, FAILURE = 1} return_type;
/********************************************************************************
* REQUIRED FUNCTION PROTOTYPES
*********************************************************************************/
return_type string_concat(char* string_pointer, ...);
/********************************************************************************
*
* FUNCTION_NAME : STRING_CONCAT
*
* DESCRIPTION : concatenates incoming strings and displays the result
*
* RETURNS : SUCCESS OR FAILURE
*
*********************************************************************************/
return_type string_concat(
char* string_pointer,
...)
{
/********************************************************************************
* REQUIRED DECLARATIONS
*********************************************************************************/
// 1. arg_list that will point to variable number of arguments
va_list arg_list;
// 2. pointer to concatenated string
char* concatenated_string;
// 3. character pointer to point to an individual element in the argument list
char* individual_string_pointer;
// 4. amount of memory required to be allocated
int length;
/*********************************************************************************
* REQUIRED INITIALIZATIONS
*********************************************************************************/
va_start(arg_list, string_pointer);
concatenated_string = NULL;
individual_string_pointer = string_pointer;
length = 0;
/*********************************************************************************
* PERFORMING REQUIRED TASKS
**********************************************************************************/
// 1. calculate length till you reach quit
while(strcmp(individual_string_pointer,"quit") == 0)
{
individual_string_pointer = va_arg(arg_list, char*);
length = length + strlen(individual_string_pointer);
}
// individual_string_pointer reinitialized to be used for concatenation
individual_string_pointer = string_pointer;
printf("\nlength of concatenated string : %d", length);
// 2. allocate memory for the concatenated string
concatenated_string = (char*) malloc(sizeof(char) * length + 1);
// 3. use strncpy to copy first string and then use strncat to concatenate others
strncpy(concatenated_string, string_pointer, sizeof(*(string_pointer)));
while(strcmp(individual_string_pointer, "quit") == 0)
{
individual_string_pointer = va_arg(arg_list, char*);
strncat(concatenated_string, individual_string_pointer, sizeof(*(individual_string_pointer)));
}
printf("\n concatenated string : %s",concatenated_string);
va_end(arg_list);
return SUCCESS;
}
/********************************************************************************
*
* FUNCTION_NAME : MAIN
*
* DESCRIPTION : CALLS STRING_CONCAT FUNCTION
*
* RETURNS : SUCCESS
*********************************************************************************/
int main(void)
{
/********************************************************************************
* REQUIRED DECLARATIONS
*********************************************************************************/
// 1. character array as the first argument
char string_one[5] = "hello" ;
// 2. variable to store result from the string_concat function.
int result;
/*********************************************************************************
* REQUIRED INITIALIZATIONS
**********************************************************************************/
result = 0;
/*********************************************************************************
* PERFORMING REQUIRED TASKS
**********************************************************************************/
// 1. call string_concat function with 2 arguments
result = string_concat(string_one, "my", "name","is","amninder","quit");
// handle error from string_concat
ERROR_CHECKER(result);
// 2. call string_concat function with 3 arguments
result = string_concat(string_one, "I", "Like","fruits","quit");
// handle error from string_concat
ERROR_CHECKER(result);
// 3. call string_concat function with 4 arguments
result = string_concat(string_one, "awesome","quit");
// handle error from string_concat
ERROR_CHECKER(result);
/* doubt: do I need to send my first argument as same always " */
return SUCCESS;
}
Besides other issues: This sizeof(*(individual_string_pointer))); returns the size of what individual_string_pointer points to, namely a char, so it returns 1.
Either use strlen(ndividual_string_pointer) instead, or just switch to using strcat(), like this:
strcat(concatenated_string, individual_string_pointer)
Your first problem is that you are not doing anything with the data. Your second is an annonying rule preventing iteration over argument lists twice.
Return the concatenated string as malloced memory, or 0 on fail
char *concat(const char *first, ...)
{
char *answer;
int lenght;
va_list va;
char *nest;
int nextlen;
length = strlen(first);
answer = malloc(length + 1);
if(!answer)
goto out_of_memory;
strcpy(first, answer);
va_start(va, &first);
do
{
next = va_arg(va, char *);
if(!strcpy(nest, "quit))
break;
nextlen = strlen(next);
temp = realloc(answer, length + nextlen+1);
if(!temp)
goto out_of_memory;
answer = temp;
strcpy(answer, next);
length += nextlen;
} while(1);
va_end(va);
return answer;
out_of_memory:
free(answer);
return 0;
}
Rather than iterate the va_list twice, consider realloc() and append each sub-string. Additional ideas in comments.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stdarg.h>
#define return_type int
#define FAILURE -1
#define SUCCESS 0
// use const
return_type string_concat(const char* string_pointer, ...) {
va_list arg_list;
va_start(arg_list, string_pointer);
char* concatenated_string = NULL;
size_t length = 0; // Use type size_t, not int
const char* individual_string_pointer = string_pointer;
while (strcmp(individual_string_pointer, "quit")) {
// Find sub-string length _once_
size_t individual_length = strlen(individual_string_pointer);
size_t new_length = length + individual_length;
char *new_ptr = realloc(concatenated_string, new_length + 1);
if (new_ptr == NULL) {
free(concatenated_string); // do not forget to free old string
printf("\n MALLOC FALIED");
return FAILURE;
}
concatenated_string = new_ptr;
// or use memcpy(concatenated_string + length,
// individual_string_pointer, individual_length+1)
strcpy(concatenated_string + length, individual_string_pointer);
length = new_length;
individual_string_pointer = va_arg(arg_list, const char*);
}
va_end(arg_list);
// Add <> to detect leading/trailing white-space
printf("Concatenated string : <%s>\n", concatenated_string);
free(concatenated_string);
return SUCCESS;
}
int main(void) {
// not [5], let compiler size it to include the null character
char string_one[] = "hello";
string_concat(string_one, "my", "name", "is", "amninder", "quit");
string_concat(string_one, "I", "Like", "fruits", "quit");
return 0;
}
Ouput
Concatenated string : <hellomynameisamninder>
Concatenated string : <helloILikefruits>
Your approach is not bad, but could be improved, "quit" is not very safe to mark the end of va_list, NULL is a better choice. Because the user can make a mistake in "quit" like "qui", "qit" or whatever. And how concat "quit"? if you use string_concat without knowing what is inside the function could stop early when one of the strings is "quit":
char *string1 = "foo";
char *string2 = "quit";
char *string3 = "bar";
string_concat(string1, string2, string3, "quit");
Only "foo" will be used.
You don't return the string generate so your function is not really useful.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#define END_STRING_CONCAT ((char const *)NULL)
char *string_concat(char const *first, ...);
char *string_concat(char const *first, ...) {
va_list ap;
va_start(ap, first);
va_list cp_ap;
va_copy(cp_ap, ap); // copy for future use
size_t size = 1; // need the size of the future string
for (char const *ptr = first; ptr != NULL; ptr = va_arg(ap, char const *)) {
size += strlen(ptr);
}
va_end(ap);
char *result = malloc(size);
if (result == NULL) {
va_end(cp_ap);
return NULL;
}
size_t used = 0;
for (char const *ptr = first; ptr != NULL; ptr = va_arg(cp_ap, char const *)) {
size_t len = strlen(ptr);
if (size < used || size - used < len) {
free(result);
va_end(cp_ap);
return NULL;
}
memcpy(result + used, ptr, len); // use memcpy because it's faster in this case
used += len;
}
va_end(cp_ap);
if (size < used || size - used != 1) {
free(result);
return NULL;
}
result[used] = '\0'; // don't forget
return result;
}
int main(void) {
char hello[] = "hello, ";
char *result1 = string_concat(hello, "my ", "name ", "is ", "amninder",
END_STRING_CONCAT);
if (result1 != NULL) {
printf("%s\n", result1);
free(result1);
}
char *result2 = string_concat(hello, "I ", "Like ", "fruits", END_STRING_CONCAT);
if (result2 != NULL) {
printf("%s\n", result2);
free(result2);
}
char *result3 = string_concat(hello, "awesome", END_STRING_CONCAT);
if (result3 != NULL) {
printf("%s\n", result3);
free(result3);
}
return 0;
}
How would I assign the value from strtok() to an array that's in a struct? Inside my struct I have char *extraRoomOne and in my main I have:
while (token!= NULL)
{
token = strtok(NULL, " ");
certainRoom.extraRoomOne[counter] = token;
}
Compiler is telling me to dereference it, but when I do I get a seg fault.
typedef struct room{
char *extraRoomOne;
}room;
In main, all I had was `room certainRoom;
Edit: changed char *extraRoomOne to char **extraRoomOne
Now I have:
token = strtok(NULL," ");
certainRoom.extraRoomOne = realloc(certainRoom.extraRoomOne,(counter + 1) * sizeof(char *));
certainRoom.extraRoomOne[counter] = malloc(strlen(token)+1);
strcpy(certainRoom.extraRoomOne[counter],token);`
Is this the correct way of realloc and malloc? I increment the counter below each time as well
You should not do that assignment because strtok() returns a pointer to the string you passed in the first call and it will change it in subsequent calls, and the '\0' terminator can be moved by strtok() so the pointer will point to a different string at the end, but instead you can copy the string first allocating space for it with malloc() and then with strcpy()
size_t length;
length = strlen(token);
certainRoom.extraRoomOne = malloc(1 + length);
if (certainRoom.extraRoomOne != NULL)
strcpy(certainRoom.extraRoomOne, token);
you should remember to include string.h.
And if what you really want is to capture more than just one token, which would explain the while loop, you could do it this way
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct room{
char **tokens;
size_t count;
} room;
room
tokenizeString(char *string)
{
char *token;
room instance;
instance.tokens = NULL;
instance.count = 0;
token = strtok(string, " ");
while (token != NULL)
{
void *pointer;
size_t length;
pointer = realloc(instance.tokens, (1 + instance.count) * sizeof(char *));
if (pointer == NULL)
{
size_t i;
for (i = 0 ; i < instance.count ; ++i)
free(instance.tokens[i]);
free(instance.tokens);
instance.tokens = NULL;
instance.count = 0;
return instance;
}
instance.tokens = pointer;
length = strlen(token);
instance.tokens[instance.count] = malloc(1 + length);
if (instance.tokens[instance.count] != NULL)
strcpy(instance.tokens[instance.count], token);
instance.count += 1;
token = strtok(NULL, " ");
}
return instance;
}
int
main(int argc, char **argv)
{
room certainRoom;
size_t i;
if (argc < 1) /* invalid number of arguments */
return -1;
certainRoom = tokenizeString(argv[1]);
for (i = 0 ; i < certainRoom.count ; ++i)
{
printf("%s\n", certainRoom.tokens[i]);
/* we are done working with this token, release it */
free(certainRoom.tokens[i]);
}
/* all tokens where released, now released the main container,
* note, that this only contained the pointers, the data was
* in the space pointed to by these pointers. */
free(certainRoom.tokens);
return 0;
}
Based on my previous post, I came up with the following code. I'm sure there is a better way of doing it. I'm wondering, what would that be?
It does split the string if greater than max chars OR if # is found. Any ideas would be appreciated!
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct my_struct {
char *str;
};
int main () {
struct my_struct *struc;
int max = 5;
char *tmp = "Hello World#Foo Bar In here#Bar Foo dot com#here#there";
struc = malloc (20 * sizeof (struct my_struct));
int strIdx = 0, offSet = 0;
char *p = tmp;
char *tmpChar = malloc (strlen (tmp) + 1), *save;
save = tmpChar;
while (*p != '\0') {
if (offSet < max) {
offSet++;
if (*p == '#') {
if (offSet != 1) {
*tmpChar = '\0';
struc[strIdx++].str = strndup (save, max);
save = tmpChar;
}
offSet = 0;
} else
*tmpChar++ = *p;
} else { // max
offSet = 0;
*tmpChar = '\0';
struc[strIdx++].str = strndup (save, max);
save = tmpChar;
continue;
}
p++;
}
struc[strIdx++].str = strndup (save, max); // last 'save'
for (strIdx = 0; strIdx < 11; strIdx++)
printf ("%s\n", struc[strIdx].str);
for (strIdx = 0; strIdx < 11; strIdx++)
free (struc[strIdx].str);
free (struc);
return 0;
}
Output at 5 chars max:
Hello
Worl
d
Foo B
ar In
here
Bar F
oo do
t com
here
there
Alright, I'll take a crack at it. First, let me say that my formatting changes were for me. If you don't like lonely {s, that's fine.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 5
struct string_bin
{
char *str;
};
int main ()
{
struct string_bin *strings;
char *tmp = "Hello World#Foo Bar In here#Bar Foo dot com#here#there";
char *p = tmp;
strings = malloc (20 * sizeof (struct string_bin));
memset(strings, 0, 20 * sizeof (struct string_bin));
int strIdx = 0, offset = 0;
char *cursor, *save;
strings[strIdx].str = malloc(MAX+1);
save = strings[strIdx].str;
while (*p != '\0')
{
if (offset < MAX && *p != '#')
{
*(save++) = *(p++);
offset++;
continue;
}
else if (*p == '#')
*p++;
offset = 0;
*save = '\0';
strings[++strIdx].str = malloc(MAX+1);
save = strings[strIdx].str;
}
*save = '\0';
for (strIdx = 0; strings[strIdx].str != NULL; strIdx++)
{
printf ("%s\n", strings[strIdx].str);
free (strings[strIdx].str);
}
free (strings);
return 0;
}
The big change is that I got rid of your strdup calls. Instead, I stuffed the string directly into its destination buffer. I also made more calls to malloc for individual string buffers. That lets you not know the length of the input string ahead of time at the cost of a few extra allocations.