if i dupe topic i really sorry, i searched for it with no result here.
I have code
void split(char* str, char* splitstr)
{
char* p;
char splitbuf[32];
int i=0;
p = strtok(str,",");
while(p!= NULL)
{
printf("%s", p);
sprintf(&splitstr[i],"%s",p);
i++;
p = strtok (NULL, ",");
}
}
How can i use proper sprintf to put the splited words by strtok to string array?
Can i somehow avoid breaklines created by strtok?
I am programming in ANSI C. I declared array splitstr and str the same way.
char* splitstr;//in main char splitstr[32];
Thanks for help.
edit:
i would like do something like this:
INPUT (it is a string) > "aa,bbb,ccc,ddd"
I declare: char tab[33];
OUTPUT (save all items to array of strings if it is even possible) >
tab[0] is "aa"
tab[1] is "bbb"
...
tab[3] is "ddd" but not "ddd(newline)"
edit2 [18:16]
I forgot add that the data string is from reading line of file. That's why i wrote about "ddd(newline)". I found after that the new line was also shown by strtok but as another item. By the way all answers are good to think over the problem. Few seconds ago my laptop has Broken (i dont know why the screen gone black) As soon as i take control over my pc i will check codes. :-)
Give this a shot:
#include <stdlib.h>
#include <string.h>
...
void split(char *str, char **splitstr)
{
char *p;
int i=0;
p = strtok(str,",");
while(p!= NULL)
{
printf("%s", p);
splitsr[i] = malloc(strlen(p) + 1);
if (splitstr[i])
strcpy(splitstr[i], p);
i++;
p = strtok (NULL, ",");
}
}
And then, in main:
#define MAX_LEN ... // max allowed length of input string, including 0 terminator
#define MAX_STR ... // max allowed number of substrings in input string
int main(void)
{
char input[MAX_LEN];
char *strs[MAX_STR] = {NULL};
...
split(input, strs);
...
}
Some explanations.
strs is defined in main as an array of pointer to char. Each array element will point to a string extracted from the input string. In main, all that's allocated is the array of pointers, with each element initially NULL; the memory for each element will be allocated within the split function using malloc, based on the length of the substring. Somewhere after you are finished with strs you will need to deallocate those pointers using free:
for (i = 0; i < MAX_STR; i++)
free(strs[i]);
Now, why is splitstr declared as char ** instead of char *[MAX_STR]? Except when it is the operand of the sizeof or & operators, or is a string literal being used to initialize another array in a declaration, an array expression will have its type implicitly converted from N-element array of T to pointer to T, and the value of the expression will be the location of the first element in the array.
When we call split:
split(input, strs);
the array expression input is implicitly converted from type char [MAX_LENGTH] to char * (T == char), and the array expression strs is implicitly converted from type char *[MAX_STRS] to char ** (T == char *). So splitstr receives pointer values for the two parameters, as opposed to array values.
If I understand correctly, you want to save the strings obtained by strtok. In that case you'll want to declare splitstr as char[MAX_LINES][32] and use strcpy, something like this:
strcpy(splitstr[i], p);
where i is the ith string read using strtok.
Note that I'm no ansi C expert, more C++ expert, but here's working code. It uses a fixed size array, which may or may not be an issue. To do anything else would require more complicated memory management:
/* Not sure about the declaration of splitstr here, and whether there's a better way.
char** isn't sufficient. */
int split(char* str, char splitstr[256][32])
{
char* p;
int i=0;
p = strtok(str,",");
while(p) {
strcpy(splitstr[i++], p);
p = strtok (NULL, ",");
}
return i;
}
int main(int argc, char* argv[])
{
char input[256];
char result[256][32];
strcpy(input, "aa,bbb,ccc,ddd");
int count = split(input, result);
for (int i=0; i<count; i++) {
printf("%s\n", result[i]);
}
printf("the end\n");
}
Note that I supply "aa,bbb,ccc,ddd" in and I get {"aa", "bbb", "ccc", "ddd" } out. No newlines on the result.
Related
I am new to C and still trying to figure out pointer.
So here is a task I am stuck in: I would like to assign 10 fruit names to a pointer of array and print them out one by one. Below is my code;
#include <stdio.h>
#include <string.h>
int main(){
char *arr_of_ptrs[10];
char buffer[20];
int i;
for (i=0;i<10;i++){
printf("Please type in a fruit name:");
fgets(buffer,20,stdin);
arr_of_ptrs[i]= *buffer;
}
int j;
for (j=0;j<10;j++){
printf("%s",*(arr_of_ptrs+j));
}
}
However after execution this, it only shows me the last result for all 10 responses. I tried to consult similar questions others asked but no luck.
My understanding is that
1) pointer of array has been allocated memory with [10] so malloc() is not needed.
2) buffer stores the pointer to each individual answer therefore I dereference it and assign it to the arr_of_ptrs[i]
I am unsure if arr_of_ptrs[i] gets me a pointer or a value. I thought it is definitely a pointer but I deference it with * the code and assign it to *buffer, program would get stuck.
If someone could point out my problem that would be great.
Thanks in advance
Three erros, 1. You must allocate memory for elements of elements of arr_of_ptrs, now you only allocate the memory for elements of arr_of_ptrs on stack memory. 2. arr_of_ptrs[i]= *buffer; means all of the arr_of_ptrs elements are pointed to same memory address, which is the "buffer" pointer. So all the elements of arr_of_ptrs will be same to the last stdin input string. 3. subsequent fgets() call has potential problem, one of the explaination could be here
A quick fix could be,
#include <stdio.h>
#include <string.h>
int main(){
const int ARR_SIZE = 10, BUFFER_SIZE = 20;
char arr_of_ptrs[ARR_SIZE][BUFFER_SIZE];
char *pos;
int i, c;
for (i = 0; i < ARR_SIZE; ++i) {
printf ("Please type in a fruit name: ");
if (fgets (arr_of_ptrs[i], BUFFER_SIZE, stdin) == NULL) return -1;
if ((pos = strchr(arr_of_ptrs[i], '\n')))
*pos = 0;
else
while ((c = getchar()) != '\n' && c != EOF) {}
}
for (i = 0; i < ARR_SIZE; ++i)
printf("%s\n", arr_of_ptrs[i]);
return 0;
}
The misunderstanding is probably that "Dereferencing" an array of characters, unlike dereferencing a pointer to a primitive data type, does not create a copy of that array. Arrays cannot be copied using assignment operator =; There a separate functions for copying arrays (especially for 0-terminated arrays of char aka c-strings, and for allocating memory needed for the copy):
Compare a pointer to a primitive data type like int:
int x = 10;
int *ptr_x = &x;
int copy_of_x = *ptr_x; // dereferences a pointer to x, yielding the integer value 10
However:
char x[20] = "some text"; // array of characters, not a single character!
char *ptr_x = &x[0]; // pointer to the first element of x
char copy_of_first_char_of_x = *ptr_x; // copies the 's', not the entire string
Use:
char x[20] = "some text";
char *ptr_x = &x[0];
char *copy_of_x = malloc(strlen(ptr_x)+1); // allocate memory large enough to store the copy
strcpy(copy_of_x,ptr_x); // copy the string.
printf("%s",copy_of_x);
Output:
some text
The following is a snippet of code that parses tokens and stores them. The code compiles and works.
Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int storeTokens(char *src, char *delim, char **storage, size_t len) {
int idx = 0;
char str[100] = {0};
char *token = NULL;
char *saveptr = NULL;
// copy string so we don't modify the source
if (snprintf(str, sizeof(str), "%s", src) < 0) {
return 0;
}
// Loop through string and parse out tokens
token = strtok_r(str, delim, &saveptr);
while ((token != NULL) && (idx < len)) {
snprintf(storage[idx], sizeof(storage[idx]), "%s", token);
strcpy(storage[idx++], token);
token = strtok_r(NULL, delim, &saveptr);
}
// Print the tokens
int i = 0;
for (i = 0; i < idx; i++) {
printf("fun: %s\n", storage[i]);
}
return idx; // number of tokens found
}
int main() {
int i = 0;
char teststr[] = "This*is*my*string*.";
char *storageptr[72];
char storage[72][255];
// Assign storage space to the pointers
for (i = 0; i < 72; i++) {
storageptr[i] = storage[i];
}
// Parse the tokens
int numtokens = storeTokens(teststr, "*", storageptr, 72);
// Print the tokens
for (i = 0; i < numtokens; i++) {
printf("storage: %s\n", storage[i]);
}
return EXIT_SUCCESS;
}
Output
fun: This
fun: is
fun: my
fun: string
fun: .
storage: This
storage: is
storage: my
storage: string
storage: .
The function stores the string with the char **storage variable. The reason for this being a double pointer is so the function can be used on any storage regardless of the length.
My problem is passing the storage into the function. As you can see in main, I created storageptr to point to storage before passing it to the function.
This seems convoluted and unnecessary. Not to mention the time wasted looping and linking each string. However I can't figure out how to pass storage directly into the function without creating storageptr.
I've done reading such as Passing an array of strings to a C function by reference, but nothing really clicks. I can't seem to figure out how to properly pass storage into the function.
Using a C99 or C11, having optional VLA support, compatible compiler you can pass directly the 2D array specifying dynamically the size of your array:
int storeTokens(char *src, char *delim, size_t len, size_t size, char storage[len][size]);
Call from main:
int numtokens = storeTokens(teststr, "*", 72, 255, storage);
If you have a C11 compiler to be sure that it supports VLA's (that are optional for C11) check the symbol STDC_NO_VLA, if it is defined the compiler does not support VLA's.
Unless you need to keep the argument as a pointer to pointer (to char), then to be able to pass the array of arrays (of char) directly you need to change the function signature:
int storeTokens(char *src, char *delim, char (*storage)[255], size_t len)
// ^^^^^^^^^^^^^^^
Now you can pass storage from the main function:
storeTokens(teststr, "*", storage, 72)
The problem is that while arrays can decay to a pointer to its first element, it's not "recursive".
If you have an array like yours
char storage[72][255];
then when it decays to a pointer to its first element it is &storage[0], which is a pointer to an array. This secondary array will not decay, so passing storage will pass a pointer to an array (of type char (*)[255]).
If you must pass a char **, then the intermediate storageptr array is needed.
Trying to do Exercise 1-19 of K&R 2nd ed., e.g. writing a function to reverse a string. I thought I managed, but the print output looks strange :-) If I use STRINGSIZE 5 the output is
Original String: hello
Reversed String: ollehhello. If I use STRINGSIZE 6 to keep in mind the '\0' string end character and modify the while-loop to while ((outputString[STRINGSIZE - (i + 2)] = inputString[i]) != '\0'), then I get Original String: hello
Reversed String: olleh?hello, I guess the ? is some random character coming from a '\0' added to the reversed string in the while-loop at position 5; but hello is again added. Could anyone explain how come hello gets added to the end of olleh and how can I get rid of it so that I only get the proper reversed string ?
Here is the code:
#include <stdio.h>
#define STRINGSIZE 5
void reverseString (char inputString[], char outputString[]);
int main(void) {
char stringToReverse[] = "hello";
char reversedString[STRINGSIZE];
reverseString(stringToReverse, reversedString);
printf("Original String: %s\nReversed String: %s\n", stringToReverse, reversedString);
}
void reverseString (char inputString[], char outputString[]) {
int i;
i = 0;
while ((outputString[STRINGSIZE - (i + 1)] = inputString[i]) != '\0')
++i;
}
First, the character array reversedString[] does not have enough space to store the null terminator of the string "hello". One option is to use a variable length array here:
char reversedString[strlen(stringToReverse) + 1];
VLAs were introduced in C99, and made optional in C11. As I remember it, K&R does not include coverage of variable length arrays, since even the 2nd edition was published before this.
Another option that would be compatible with C89 is to use the sizeof operator:
char stringToReverse[] = "hello";
char reversedString[sizeof stringToReverse];
Here, the result from the sizeof operator is known at compile time, and can be used in the declaration of a fixed size array. This size includes space for the null terminator, in contrast to the result from strlen("hello"). Note that this would not work with char *stringToReverse = "hello";, since then the sizeof operator would give the size of a pointer. This also would not work if stringToReverse had been passed into a function first, since then the array name would have decayed to a pointer to the first element of stringToReverse.
In the reverseString() function, the length of inputString needs to be determined (since STRINGSIZE is no longer being used); this can be done with strlen(), or in a loop. Then, critically, the function must be certain to add a null terminator (\0) to outputString[] before returning. Also note that a return statement has been added to the end of main() to make this truly C89 compatible:
#include <stdio.h>
void reverseString (char inputString[], char outputString[]);
int main(void) {
char stringToReverse[] = "hello";
char reversedString[sizeof stringToReverse];
reverseString(stringToReverse, reversedString);
printf("Original String: %s\nReversed String: %s\n",
stringToReverse, reversedString);
return 0;
}
void reverseString(char inputString[], char outputString[])
{
int length = 0;
int i = 0;
/* Get inputString length; or use strlen() */
while (inputString[length] != '\0') {
++length;
}
/* Copy to outputString[] in reverse */
while (i < length) {
outputString[i] = inputString[(length - i) - 1];
++i;
}
/* Add null terminator */
outputString[i] = '\0';
}
First I'll suggest that you change this line:
char reversedString[STRINGSIZE];
to
char reversedString[strlen(stringToReverse) + 1]; // + 1 to make room for the string termination
Then I would do something like:
void reverseString (char inputString[], char outputString[]) {
int i;
int len = strlen(inputString);
for(i=0; i<len; ++i)
{
outputString[len-i-1] = inputString[i];
}
outputString[len] = '\0'; // Terminate the string
}
#include <stdio.h>
#include <string.h>
char Jones(char, char);
int main() {
char name[]="Andrew";
char surname[]="Jones";
char result[80];
result=Jones(name, surname);
puts(result);
return 0;
}
char Jones(char name, char surname)
{
char result[80];
int length;
length = strlen(surname);
for (int i=0; i<50; i++)
{
result[length+i] = name[i];
}
return result;
}
The program does not compile and i dont know why. It is supposed to read two strings and swap their places. It should display eg. "Jones Andrew".
Here's one problem:
char name[]="Andrew";
char surname[]="Jones";
char result[80];
wynik=Jones(name, surname);
This calls Jones() with character arrays (which will decay to character pointers), but the function is declared to accept single characters only.
You should change the function to take char *name, char *surname, since it really does seem to expect strings.
Further, you can't return a character array like you're doing in Jones(), you need to read up quite a bit on how to work with strings in C.
Also, wynik looks undeclared, that'll also make it fail to build.
There's a few errors:
char Jones(char, char);
This takes just a single char, not a char * which you need for a string.
result=Jones(name, surname);
Here result is an array. In C, you cannot assign to an array.
char Jones(char name, char surname)
{ char result[80];
...
return result;
}
Here you return result which is a local variable. But to return a string, you'd need to return a char*. But that char* would point to a local variable within the Jones function, which is no longer valid when the function ends. One solution is to pass in a buffer where you write the result that the caller owns.
Your algorithm for combining the surename and name is also wrong, you never do anything with the surename.
You'll need to do this:
char *Jones(char*, char* , char *);
int main() {
char name[]="Andrew";
char surname[]="Jones";
char result[80];
char *p;
p = Jones(name, surname, result);
puts(p);
return 0;
}
char *Jones(char *name, char *surname, char *result)
{
int length;
int i,k;
length = strlen(surname);
for (i=0; i<length ; i++)
{
result[i] = surname[i];
}
result[i++] = ' '; //add a space
length = strlen(name);
for (k=0; k<length ; k++, i++)
{
result[i] = name[k];
}
result[i] = 0; //add nul terminator to end the string
return result;
}
The conatenation could be simplified in many ways, e.g. to
strcpy(result, surename);
strcat(result, " ");
strcat(result, name);
or the Jones function could just do:
sprintf(result, "%s %s", surename, name);
In all cases, the function is rather fragile, as it's easy to overflow the result buffer if you pass in something else that does not fit within the result buffer.
1) The name and surname are char array and not single char so you have to change the input parameters types of your function Jones() the input parameters types should be char name[] (char array) or char *name (pointer to array of char)
2) You can not return an array defined locally and statically in the function. and if you want to return a string from the function, the string should be constant or it should be a buffer allocated dynamically (with malloc, calloc, realloc) into the function And for both cases the function type should be char *Jonas() and not char Jonas()
Or you can pass the result array via input parametr. and in this case you can fill it into the function.
void Jones(char *name, char *surname, char *result)
and in the main:
char result[80];
Jones(names, surname, result);
3) The following for loop is missing some thing
for (int i=0; i<50; i++)
{
result[length+i] = name[i];
}
The result elements from element 0 to element length are containing garbage because the result array is not initiated. so when you printf the result array you will get garbages printed. You have to initiate elements between 0 to length in the result array
#include <stdio.h>
#include <string.h>
char* Jones(char*, char*);
int main() {
char name[]="Andrew";
char surname[]="Jones";
puts(Jones(name,surname));
return 0;
}
char* Jones(char *name, char *surname)
{
strcat(surname," ");
strcat(surname,name);
return surname;
}
I'm reading in a line from a file (char by char, using fgetc()), where all fields(firstname, lastname, ...) are seperated by an ;. What I now want to do is create a char**, add all the chars to that and replace the ; by \0 so that I effectively get a list of all the fields.
Is that actually possibly? And when I create a char**, e.g. char ** buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?
EDIT
Sry, meant to replace ; by \0, bot \n.
EDIT 2
This code should demonstrate what I intend to do (may clarify things a little):
int length = 80; // initial length char *buf = malloc(length); int num_chars = 0;
// handle malloc returning NULL
// suppose we already have a FILE pointer fp for (int ch = fgetc(fp); ch != EOF && ferror(fp) == 0; ch = fgetc(fp)) {
if (length == size) { // expand array if necessary
length += 80;
buf = realloc(buf, length);
// handle realloc failure
}
if (ch == ';') {
buf[num_chars] = '\0'; // replace delimiter by null-terminator
} else {
buf[num_chars] = ch; // else put char in buf
} }
// resize the buffer to chars read buf
= realloc(buf, num_chars);
// now comes the part where I'm quite unsure if it works / is possible
char **list = (char **)buf; // and now I should be able to handle it like a list of strings?
Yes, it's possible. Yes, you can treat it as a one dimensional array. Yes, the memory is contiguous.
But you probably want:
char ** fields = malloc(sizeof(char *) * numFields);
Then you can do:
// assuming `field` is a `char *`
fields[i++] = field;
It's not exactly possible as you describe it, but something similar is possible.
More specifically, the last line of your example char **list = (char **)buf; won't do what you believe. char **list means items pointed by *list will be of the type char*, but the content of *buf are chars. Hence it won't be possible to change one into another.
You should understand that a char ** is just a pointer, it can hold nothing by itself. But the char ** can be the start address of an array of char *, each char * pointing to a field.
You will have to allocate space for the char * array using a first malloc (or you also can use a static array of char * instead of a char**. You also will have to find space for every individual field, probably performing a malloc for each field and copying it from the initial buffer. If the initial buffer is untouched after reading, it would also be possible to use it to keep the field names, but if you just replace the initial ; by a \n, you'll be missing the trailing 0 string terminator, you can't replace inplace one char by two char.
Below is a simplified example of what can be done (removed malloc part as it does not add much to the example, and makes it uselessly complex, that's another story):
#include <stdio.h>
#include <string.h>
main(){
// let's define a buffer with space enough for 100 chars
// no need to perform dynamic allocation for a small example like this
char buf[100];
const char * data = "one;two;three;four;";
// now we want an array of pointer to fields,
// here again we use a static buffer for simplicity,
// instead of a char** with a malloc
char * fields[10];
int nbfields = 0;
int i = 0;
char * start_of_field;
// let's initialize buf with some data,
// a semi-colon terminated list of strings
strcpy(buf, data);
start_of_field = buf;
// seek end of each field and
// copy addresses of field to fields (array of char*)
for (i = 0; buf[i] != 0; i++){
if (buf[i] == ';'){
buf[i] = 0;
fields[nbfields] = start_of_field;
nbfields++;
start_of_field = buf+i+1;
}
}
// Now I can address fields individually
printf("total number of fields = %d\n"
"third field is = \"%s\"\n",
nbfields, fields[2]);
}
I now want to do is create a char**, add all the chars to that
You would allocate an array of char (char*) to store the characters, not an array of char* (char**).
And when I create a char*, e.g. char * buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?
Yes, malloc always returns continguous blocks. Yes, you can treat it as single-dimensional an array of char* (with 80/sizeof char* elements). But you'll need to seperately allocate memory for each string you store in this array, or create another block for storing the string data, then use your first array to store pointers into that block.
(or something else; lots of ways to skin this cat)
It may be that Helper Method wants each of the "fields" written to a nul-terminated string. I cannot tell. You can do that with strtok(). However strtok() has problems - it destroys the original string "fed" to it.
#define SPLIT_ARRSZ 20 /* max possible number of fields */
char **
split( char *result[], char *w, const char *delim)
{
int i=0;
char *p=NULL;
for(i=0, result[0]=NULL, p=strtok(w, delim); p!=NULL; p=strtok(NULL, delim), i++ )
{
result[i]=p;
result[i+1]=NULL;
}
return result;
}
void
usage_for_split(void)
{
char *result[SPLIT_ARRSZ]={NULL};
char str[]="1;2;3;4;5;6;7;8;9;This is the last field\n";
char *w=strdup(str);
int i=0;
split(result, w, ";\n");
for(i=0; result[i]!=NULL; i++)
printf("Field #%d = '%s'\n", i, result[i]);
free(w);
}