I am dynamically allocating the 2D array like this:
char ** inputs;
inputs = (char **) malloc(4 * sizeof(char));
After doing this I started having the problem with the string. I printed the string before and after allocating the 2D-array:
printf("%s\n", str);
char ** inputs;
inputs = (char **) malloc(4 * sizeof(char));
printf("%s\n", str);
But I get strange output:
before: input aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa with len 34
after: input aaaaaaaaaaaaaaaaaaaaaaaaaaaa with len 29
Why the length is changed? I've searched through stackoverflow and other websites but couldn't find reasonable answer for that.
Here is my all function call:
int main(int argc, char const *argv[])
{
/* code */
mainProcess();
printf("\nEnd of the program\n");
return 0;
}
// Reading the input from the user
char * getInput(){
printf("Inside of the getInput\n");
char * result;
char * st;
char c;
result = malloc(4 * sizeof(char));
st = malloc(4 * sizeof(char));
// code goes here
printf("$ ");
while(3){
c = fgetc(stdin);
if(c == 10){
break;
}
printf("%c", c);
result[length] = c;
length++;
}
result[length] = '\0';
return result;
}
void mainProcess(){
char * input;
printf("Inside of Main process\n");
input = getInput();
printf("\nthis is input %s with len %d\n", input, strlen(input));
splitInput(input);
printf("\nthis is input %s with len %d\n", input, strlen(input));
}
char ** splitInput(const char * str){
char ** inputs;
inputs = NULL;
printf("inside split\n");
printf("%s\n", str);
inputs = (char **) malloc( sizeof(char));
// free(inputs);
printf("------\n"); // for testing
printf("%s\n", str);
if(!inputs){
printf("Error in initializing the 2D array!\n");
exit(EXIT_FAILURE);
}
return NULL;
}
It is not entirely clear what you are trying to accomplish, but it appears you are attempting to read a line of text with getInput and then you intend to separate the input into individual words in splitInput, but are not clear on how to go about doing it. The process of separating a line of text into words is called tokenizing a string. The standard library provide strtok (aptly named) and strsep (primarily useful if you have the possibility of an empty delimited field).
I have explained the difference between a 2D array and your use of a pointer-to-pointer-to-char in the comments above.
To begin, look at getInput. One issue that will give you no end of grief is c must be type int or you cannot detect EOF. In addition, you can simply pass a pointer (type size_t) as a parameter and keep count of the characters in result and avoid the need for strlen to get the length of the returned string. You MUST use a counter anyway to insure you do not write beyond the end of result to begin with, so you may as well make the count available back in the calling function e.g.
char *getInput (size_t *n)
{
printf ("Inside of the getInput\n");
char *result = NULL;
int c = 0; /* c must be type 'int' or you cannot detect EOF */
/* validate ALL allocations */
if ((result = malloc (MAXC * sizeof *result)) == NULL) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return result;
}
printf ("$ ");
fflush (stdout); /* output is buffered, flush buffer to show prompt */
while (*n + 1 < MAXC && (c = fgetc (stdin)) != '\n' && c != EOF) {
printf ("%c", c);
result[(*n)++] = c;
}
putchar ('\n'); /* tidy up with newline */
result[*n] = 0;
return result;
}
Next, as indicated above, it appears you want to take the line of text in result and use splitInput to fill a pointer-to-pointer-to-char with the individual words (which you are confusing to be a 2D array). To do that, you must keep in mind that strtok will modify the string it operates on so you must make a copy of str which you pass as const char * to avoid attempting to modify a constant string (and the segfault).
You are confused in how to allocate the pointer-to-pointer-to-char object. First you must allocate space for a sufficient number of pointers, e.g. (with #define MAXW 32) you would need something like:
/* allocate MAXW pointers */
if ((inputs = malloc (MAXW * sizeof *inputs)) == NULL) {
fprintf (stderr, "error: memory exhausted - inputs.\n");
return inputs;
}
Then as you tokenize the input string, you must allocate for each individual word (each themselves an individual string), e.g.
if ((inputs[*n] = malloc ((len + 1) * sizeof *inputs[*n])) == NULL) {
fprintf (stderr, "error: memory exhausted - word %zu.\n", *n);
break;
}
strcpy (inputs[*n], p);
(*n)++;
note: 'n' is a pointer to size_t to make the word count available back in the caller.
To tokenize the input string you can wrap the allocation above in:
for (char *p = strtok (cpy, delim); p; p = strtok (NULL, delim))
{
size_t len = strlen (p);
...
if (*n == MAXW) /* check if limit reached */
break;
}
Throughout your code you should also validate all memory allocations and provide effective returns for each function that allocates to allow the caller to validate whether the called function succeeded or failed.
Putting all the pieces together, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 256 /* constant for maximum characters of user input */
#define MAXW 32 /* constant for maximum words in line */
void mainProcess();
int main (void)
{
mainProcess();
printf ("End of the program\n");
return 0;
}
char *getInput (size_t *n)
{
printf ("Inside of the getInput\n");
char *result = NULL;
int c = 0; /* c must be type 'int' or you cannot detect EOF */
/* validate ALL allocations */
if ((result = malloc (MAXC * sizeof *result)) == NULL) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return result;
}
printf ("$ ");
fflush (stdout); /* output is buffered, flush buffer to show prompt */
while (*n + 1 < MAXC && (c = fgetc (stdin)) != '\n' && c != EOF) {
printf ("%c", c);
result[(*n)++] = c;
}
putchar ('\n'); /* tidy up with newline */
result[*n] = 0;
return result;
}
/* split str into tokens, return pointer to array of char *
* update pointer 'n' to contain number of words
*/
char **splitInput (const char *str, size_t *n)
{
char **inputs = NULL,
*delim = " \t\n", /* split on 'space', 'tab' or 'newline' */
*cpy = strdup (str);
printf ("inside split\n");
printf ("%s\n", str);
/* allocate MAXW pointers */
if ((inputs = malloc (MAXW * sizeof *inputs)) == NULL) {
fprintf (stderr, "error: memory exhausted - inputs.\n");
return inputs;
}
/* split cpy into tokens (words) max of MAXW words allowed */
for (char *p = strtok (cpy, delim); p; p = strtok (NULL, delim))
{
size_t len = strlen (p);
if ((inputs[*n] = malloc ((len + 1) * sizeof *inputs[*n])) == NULL) {
fprintf (stderr, "error: memory exhausted - word %zu.\n", *n);
break;
}
strcpy (inputs[*n], p);
(*n)++;
if (*n == MAXW) /* check if limit reached */
break;
}
free (cpy); /* free copy */
return inputs;
}
void mainProcess()
{
char *input = NULL,
**words = NULL;
size_t len = 0, nwords = 0;
printf ("Inside of Main process\n\n");
input = getInput (&len);
if (!input || !*input) {
fprintf (stderr, "error: input is empty or NULL.\n");
return;
}
printf ("this is input '%s' with len: %zu (before split)\n", input, len);
words = splitInput (input, &nwords);
printf ("this is input '%s' with len: %zu (after split)\n", input, len);
free (input); /* done with input, free it! */
printf ("the words in input are:\n");
for (size_t i = 0; i < nwords; i++) {
printf (" word[%2zu]: '%s'\n", i, words[i]);
free (words[i]); /* free each word */
}
free (words); /* free pointers */
putchar ('\n'); /* tidy up with newline */
}
Example Use/Output
$ ./bin/mainprocess
Inside of Main process
Inside of the getInput
$ my dog has fleas
my dog has fleas
this is input 'my dog has fleas' with len: 16 (before split)
inside split
my dog has fleas
this is input 'my dog has fleas' with len: 16 (after split)
the words in input are:
word[ 0]: 'my'
word[ 1]: 'dog'
word[ 2]: 'has'
word[ 3]: 'fleas'
End of the program
Memory Error Check
In any code you write that dynamically allocates memory, you need to run your code though a memory/error checking program. On Linux, valgrind is the normal choice. Simply run your code through it, e.g.
$ valgrind ./bin/mainprocess
==15900== Memcheck, a memory error detector
==15900== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==15900== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==15900== Command: ./bin/mainprocess
==15900==
Inside of Main process
Inside of the getInput
$ my dog has fleas
my dog has fleas
this is input 'my dog has fleas' with len: 16 (before split)
inside split
my dog has fleas
this is input 'my dog has fleas' with len: 16 (after split)
the words in input are:
word[ 0]: 'my'
word[ 1]: 'dog'
word[ 2]: 'has'
word[ 3]: 'fleas'
End of the program
==15900==
==15900== HEAP SUMMARY:
==15900== in use at exit: 0 bytes in 0 blocks
==15900== total heap usage: 7 allocs, 7 frees, 546 bytes allocated
==15900==
==15900== All heap blocks were freed -- no leaks are possible
==15900==
==15900== For counts of detected and suppressed errors, rerun with: -v
==15900== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always verify you have freed any memory you allocate, and that there are no memory errors.
Look things over and let me know if you have any questions. If I guess wrong about what you intended, well that's where an MCVE helps :)
This code compiles (gcc -Wall) without warnings and does not change the size.
It also tries to stress the need for allocating enough space and/or not to write beyond allocated memory.
Note for example the
malloc((MaxInputLength+1) * sizeof(char))
while(length<MaxInputLength)
inputs[i]=malloc((MaxLengthOfSplitString+1) * sizeof(char));
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// the length which was used in your MCVE, probably accidentally
#define MaxInputLength 3 // you will probably want to increase this
#define MaxLengthOfSplitString 1 // and this
#define MaxNumberOfSplitStrings 3 // and this
// Reading the input from the user
char * getInput(){
printf("Inside of the getInput\n");
char * result;
char c;
int length=0;
result = malloc((MaxInputLength+1) * sizeof(char));
// code goes here
printf("$ ");
while(length<MaxInputLength){
c = fgetc(stdin);
if(c == 10){
break;
}
printf("%c", c);
result[length] = c;
length++;
}
result[length] = '\0';
return result;
}
char ** splitInput(const char * str){
char ** inputs;
inputs = NULL;
printf("inside split\n");
printf("%s\n", str);
inputs = (char **) malloc(MaxNumberOfSplitStrings * sizeof(char*));
{
int i;
for (i=0; i< MaxNumberOfSplitStrings; i++)
{
inputs[i]=malloc((MaxLengthOfSplitString+1) * sizeof(char));
}
// Now you have an array of MaxNumberOfSplitStrings char*.
// Each of them points to a buffer which can hold a ero- terminated string
// with at most MaxLengthOfSplitString chars, ot counting the '\0'.
}
// free(inputs);
printf("------\n"); // for testing
printf("%s\n", str);
if(!inputs){
printf("Error in initializing the 2D array!\n");
exit(EXIT_FAILURE);
}
return NULL;
}
void mainProcess(){
char * input;
printf("Inside of Main process\n");
input = getInput();
printf("\nthis is input %s with len %d\n", input, strlen(input));
splitInput(input);
printf("\nthis is input %s with len %d\n", input, strlen(input));
}
int main(int argc, char const *argv[])
{
/* code */
mainProcess();
printf("\nEnd of the program\n");
return 0;
}
Related
I'm trying to break a string str containing the bar symbol | into an array of strings(output), with the delimiter being |, which will not be included in the array of strings. There will be 20 elements in output. This code belongs to a function that will return output pointer, which is why I need dynamic allocation.
I'm trying to do this without using the sscanf() function.
Example: if str is "abc|def||jk" then this is what output should look like at the end (less than 20 elements for demonstration purpose):
output[0]=="abc"
output[1]=="def"
output[2]==""
output[3]=="jk"
However, I always get an error exit code, something like:
Process finished with exit code -1073740940 (0xC0000374)
When debugging I found out that the first string element is parsed correctly into output, but the second element is produced correctly sometimes, and other times I ran into trouble.
Code below:
char **output = (char**) calloc(20, 20*sizeof(char));
int begin = 0;
int end = 1;
// index that counts output element
int arrayIndex = 0;
int i;
for(i = 0; i < strlen(str); i++) {
end = i;
bool endOfString = false;
// there is no '|' symbol at the end of the string
if (*(str+i) == '|' || (endOfString = (i+1)==strlen(str))) {
end = endOfString ? (end+1):end;
// problem here. Assembly code poped up when debugging (see image below)
char *target = (char*) calloc(end-begin+1, sizeof(char));
// give proper value to target
strcpy(target, str+begin);
*(target+end-begin) = '\0';
*(output+arrayIndex) = target;
// increase proper indexes
begin = i + 1;
arrayIndex++;
}
}
The worst of all is that I cannot debug it because a window with assembly code pops up the instance I step over the calloc function when debugging.
I used gdb too, but it didn't work:
56 char target = (char) calloc(length, sizeof(char));
(gdb) n
warning: Critical error detected c0000374
Thread 1 received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffded8191f3 in ?? ()
(gdb) bt
#0 0x00007ffded8191f3 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) continue
Continuing.
gdb: unknown target exception 0xc0000374 at 0x7ffded819269
(edit) indexing was OK. Couple of notes: sizeof(char) is always 1 -- just use 1. Further, there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?. Additionally, how many times do you call strlen(str)? (hopefully optimization limits the loop condition to a single call, but you could potentially be calling strlen(str) for every iteration). Same with endOfString = (i+1)==strlen(str). Save the length of the string before entering the loop and then just use the saved value to compare against.
While you can count indexes as you have and loop character-by-character looking for delimiters, it is far more efficient to let strchr (pointer, '|') advance to the next delimiter (or return NULL indicating no more delimiters remain). Then rather than worrying about indexes, simply keep a pointer p and end-pointer ep to advance down the string, e.g.
#define NFIELDS 20
...
char **splitstr (const char *s, const char delim, size_t *n)
{
const char *p = s, /* pointer for parsing */
*ep = s; /* end pointer for parsing */
*n = 0; /* zero string counter */
while (*n < NFIELDS && *p) { /* loop while less than NFIELDS */
size_t len;
if ((ep = strchr (p, delim))) /* get pointer to delim */
len = ep - p;
else
len = strlen (p); /* or get length of final string */
if (!(output[*n] = malloc (len + 1))) { /* allocated for string */
...
memcpy (output[*n], p, len); /* copy chars to output[n] */
output[(*n)++][len] = 0; /* nul-terminate to make string */
if (!ep) /* if delim not found, last */
break;
p = ++ep; /* update p to 1-past delim */
}
...
Adding appropriate error checking and returning the pointer to the allocated strings, you could do:
char **splitstr (const char *s, const char delim, size_t *n)
{
const char *p = s, /* pointer for parsing */
*ep = s; /* end pointer for parsing */
char **output = calloc (NFIELDS, sizeof *output); /* pointer to output */
if (!output) {
perror ("calloc-output");
return NULL;
}
*n = 0; /* zero string counter */
while (*n < NFIELDS && *p) { /* loop while less than NFIELDS */
size_t len;
if ((ep = strchr (p, delim))) /* get pointer to delim */
len = ep - p;
else
len = strlen (p); /* or get length of final string */
if (!(output[*n] = malloc (len + 1))) { /* allocated for string */
perror ("malloc-output[n]");
while ((*n)--) /* free prior allocations on failure */
free (output[*n]);
free(output);
return NULL;
}
memcpy (output[*n], p, len); /* copy chars to output[n] */
output[(*n)++][len] = 0; /* nul-terminate to make string */
if (!ep) /* if delim not found, last */
break;
p = ++ep; /* update p to 1-past delim */
}
return output; /* return pointer to allocated strings */
}
In your case a complete short example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NFIELDS 20
#define MAXC 1024
char **splitstr (const char *s, const char delim, size_t *n)
{
const char *p = s, /* pointer for parsing */
*ep = s; /* end pointer for parsing */
char **output = calloc (NFIELDS, sizeof *output); /* pointer to output */
if (!output) {
perror ("calloc-output");
return NULL;
}
*n = 0; /* zero string counter */
while (*n < NFIELDS && *p) { /* loop while less than NFIELDS */
size_t len;
if ((ep = strchr (p, delim))) /* get pointer to delim */
len = ep - p;
else
len = strlen (p); /* or get length of final string */
if (!(output[*n] = malloc (len + 1))) { /* allocated for string */
perror ("malloc-output[n]");
while ((*n)--) /* free prior allocations on failure */
free (output[*n]);
free(output);
return NULL;
}
memcpy (output[*n], p, len); /* copy chars to output[n] */
output[(*n)++][len] = 0; /* nul-terminate to make string */
if (!ep) /* if delim not found, last */
break;
p = ++ep; /* update p to 1-past delim */
}
return output; /* return pointer to allocated strings */
}
int main (void) {
char buf[MAXC], /* buffer for input */
**output = NULL; /* pointer to split/allocated strings */
size_t n = 0; /* number of strings filled */
if (!fgets (buf, MAXC, stdin)) { /* validate input */
fputs ("error: invalid input.\n", stderr);
return 1;
}
buf[strcspn (buf, "\n")] = 0; /* trim newline from buf */
/* split buf into separate strings on '|' */
if (!(output = splitstr (buf, '|', &n))) {
fputs ("error: splitstr() failed.\n", stderr);
return 1;
}
for (size_t i = 0; i < n; i++) { /* loop outputting each & free */
printf ("output[%2zu]: %s\n", i, output[i]);
free (output[i]); /* free strings */
}
free (output); /* free pointers */
}
Example Use/Output
$ echo "abc|def||jk" | ./bin/splitstr
output[ 0]: abc
output[ 1]: def
output[ 2]:
output[ 3]: jk
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ echo "abc|def||jk" | valgrind ./bin/splitstr
==32024== Memcheck, a memory error detector
==32024== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==32024== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==32024== Command: ./bin/splitstr
==32024==
output[ 0]: abc
output[ 1]: def
output[ 2]:
output[ 3]: jk
==32024==
==32024== HEAP SUMMARY:
==32024== in use at exit: 0 bytes in 0 blocks
==32024== total heap usage: 5 allocs, 5 frees, 172 bytes allocated
==32024==
==32024== All heap blocks were freed -- no leaks are possible
==32024==
==32024== For counts of detected and suppressed errors, rerun with: -v
==32024== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
The question is to convert a text file into a CSV file using C programming. The input text file is formatted as the following:
JACK Maria Stephan Nora
20 34 45 28
London NewYork Toronto Berlin
The output CSV file should look like:
Jack,20,London
Maria,34,NewYork
Stephan,45,Toronto
Nora,28,Berlin
The following code is what I tried so far:
void load_and_convert(const char* filename){
FILE *fp1, *fp2;
char ch;
fp1=fopen(filename,"r");
fp2=fopen("output.csv","w");
for(int i=0;i<1000;i++){
ch=fgetc(fp1);
fprintf(fp2,"%c",ch);
if(ch==' '|| ch=='\n')
fprintf(fp2,"%c,\n",ch);
}
fclose(fp1);
fclose(fp2);
}
The output from my code looks like:
Jack,
Maria,
Stephan,
Nora,
20,
34,
45,
28,
London,
NewYork,
Toronto,
Berlin,
How should I modify my code to make it work correctly?
What's the idea to treat this question?
Since I have some times, here is a working solution for you (tried my best to make the solution as elegant as I can):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_LENGTH 50
#define MAX_NUMBER_OF_PEOPLE 50
typedef struct
{
char name[MAX_STRING_LENGTH];
int age;
char city[MAX_STRING_LENGTH];
} Person;
void getName(char *src, char *delim, Person *people) {
char *ptr = strtok(src, delim);
int i = 0;
while(ptr != NULL)
{
strncpy(people[i].name, ptr, MAX_STRING_LENGTH);
ptr = strtok(NULL, delim);
i++;
}
}
void getAge(char *src, char *delim, Person *people) {
char *ptr = strtok(src, delim);
int i = 0;
while(ptr != NULL)
{
people[i].age = atoi(ptr);
i++;
ptr = strtok(NULL, delim);
}
}
void getCity(char *src, char *delim, Person *people) {
char *ptr = strtok(src, delim);
int i = 0;
while(ptr != NULL)
{
strncpy(people[i].city, ptr, MAX_STRING_LENGTH);
i++;
ptr = strtok(NULL, delim);
}
}
int main(void)
{
Person somebody[MAX_NUMBER_OF_PEOPLE];
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
int ln = 0;
fp = fopen("./test.txt", "r");
if (fp == NULL)
return -1;
// Read every line, support first line is name, second line is age...
while ((read = getline(&line, &len, fp)) != -1) {
// remote trailing newline character
line = strtok(line, "\n");
if (ln == 0) {
getName(line, " ", somebody);
} else if (ln == 1) {
getAge(line, " ", somebody);
} else {
getCity(line, " ", somebody);
}
ln++;
}
for (int j = 0; j < MAX_NUMBER_OF_PEOPLE; j++) {
if (somebody[j].age == 0)
break;
printf("%s, %d, %s\n", somebody[j].name, somebody[j].age, somebody[j].city);
}
fclose(fp);
if (line)
free(line);
return 0;
}
What you are needing to do is non-trivial if you want to approach the problem holding all values in memory as you transform the 3-rows with 4-fields in each row, to a format of 4-rows with 3-fields per-row. So when you have your datafile containing:
Example Input File
$ cat dat/col2csv3x4.txt
JACK Maria Stephan Nora
20 34 45 28
London NewYork Toronto Berlin
You want to read each of the three lines and then transpose the columns into rows for .csv output. Meaning you will then end up with 4-rows of 3-csv fields each, e.g.
Expected Program Output
$ ./bin/transpose2csv < dat/col2csv3x4.txt
JACK,20,London
Maria,34,NewYork
Stephan,45,Toronto
Nora,28,Berlin
There is nothing difficult in doing it, but it takes meticulous attention to handling the memory storage of object and allocating/reallocating to handle the transformation between 3-rows with 4-pieces of data to 4-rows with 3-pieces of data.
One approach is to read all original lines into a typical pointer-to-pointer to char setup. Then transform/transpose the columns into rows. Since conceivably there could be 100-rows with 500-fields next time, you will want to approach the transformation using indexes and counters to track your allocation and reallocation requirement to make your finished code able to handle transposing a generic number of lines and fields into fields-number of lines with as many vales per row as you had original lines.
You can design your code to provide the transformation in two basic functions. The first to read and store the lines (saygetlines`) and the second to then transpose those lines into a new pointer-to-pointer to char so it can be output as comma separated values
One way to approach these two functions would be similar to the following that takes the filename to read as the first-arguments (or will read from stdin by default if no argument is given). The code isn't trivial, but it isn't difficult either. Just keep track of all your allocations, preserving a pointer to the beginning of each, so the memory may be freed when no longer needed, e.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NPTR 2
#define NWRD 128
#define MAXC 1024
/** getlines allocates all storage required to read all lines from file.
* the pointers are doubled each time reallocation is needed and then
* realloc'ed a final time to exactly size to the number of lines. all
* lines are stored with the exact memory required.
*/
char **getlines (size_t *n, FILE *fp)
{
size_t nptr = NPTR; /* tracks number of allocated pointers */
char buf[MAXC]; /* tmp buffer sufficient to hold each line */
char **lines = calloc (nptr, sizeof *lines);
if (!lines) { /* validate EVERY allocaiton */
perror ("calloc-lines");
return NULL;
}
*n = 0; /* pointer tracks no. of lines read */
rewind (fp); /* clears stream error state if set */
while (fgets (buf, MAXC, fp)) { /* read each line o finput */
size_t len;
if (*n == nptr) { /* check/realloc ptrs if required */
void *tmp = realloc (lines, 2 * nptr * sizeof *lines);
if (!tmp) { /* validate reallocation */
perror ("realloc-tmp");
break;
}
lines = tmp; /* assign new block, (opt, zero new mem below) */
memset (lines + nptr, 0, nptr * sizeof *lines);
nptr *= 2; /* increment allocated pointer count */
}
buf[(len = strcspn(buf, "\r\n"))] = 0; /* get line, remove '\n' */
lines[*n] = malloc (len + 1); /* allocate for line */
if (!lines[*n]) { /* validate */
perror ("malloc-lines[*n]");
break;
}
memcpy (lines[(*n)++], buf, len + 1); /* copy to line[*n] */
}
if (!*n) { /* if no lines read */
free (lines); /* free pointers */
return NULL;
}
/* optional final realloc to free unused pointers */
void *tmp = realloc (lines, *n * sizeof *lines);
if (!tmp) {
perror ("final-realloc");
return lines;
}
return (lines = tmp); /* return ptr to exact no. of required ptrs */
}
/** free all pointers and n alocated arrays */
void freep2p (void *p2p, size_t n)
{
for (size_t i = 0; i < n; i++)
free (((char **)p2p)[i]);
free (p2p);
}
/** transpose a file of n rows and a varying number of fields to an
* allocated pointer-to-pointer t0 char structure with a fields number
* of rows and n csv values per row.
*/
char **transpose2csv (size_t *n, FILE *fp)
{
char **l = NULL, **t = NULL;
size_t csvl = 0, /* csv line count */
ncsv = 0, /* number of csv lines allocated */
nchr = MAXC, /* initial chars alloc for csv line */
*offset, /* array tracking read offsets in lines */
*used; /* array tracking write offset to csv lines */
if (!(l = getlines (n, fp))) { /* read all lines to l */
fputs ("error: getlines failed.\n", stderr);
return NULL;
}
ncsv = *n;
#ifdef DEBUG
for (size_t i = 0; i < *n; i++)
puts (l[i]);
#endif
if (!(t = malloc (ncsv * sizeof *t))) { /* alloc ncsv ptrs for csv */
perror ("malloc-t");
freep2p (l, *n); /* free everything else on failure */
return NULL;
}
for (size_t i = 0; i < ncsv; i++) /* alloc MAXC chars to csv ptrs */
if (!(t[i] = malloc (nchr * sizeof *t[i]))) {
perror ("malloc-t[i]");
while (i--) /* free everything else on failure */
free (t[i]);
free (t);
freep2p (l, *n);
return NULL;
}
if (!(offset = calloc (*n, sizeof *offset))) { /* alloc offsets array */
perror ("calloc-offsets");
free (t);
freep2p (l, *n);
return NULL;
}
if (!(used = calloc (ncsv, sizeof *used))) { /* alloc used array */
perror ("calloc-used");
free (t);
free (offset);
freep2p (l, *n);
return NULL;
}
for (;;) { /* loop continually transposing cols to csv rows */
for (size_t i = 0; i < *n; i++) { /* read next word from each line */
char word[NWRD]; /* tmp buffer for word */
int off; /* number of characters consumed in read */
if (sscanf (l[i] + offset[i], "%s%n", word, &off) != 1)
goto readdone; /* break nested loops on read failure */
size_t len = strlen (word); /* get word length */
offset[i] += off; /* increment read offset */
if (csvl == ncsv) { /* check/realloc new csv row as required */
size_t newsz = ncsv + 1; /* allocate +1 row over *n */
void *tmp = realloc (t, newsz * sizeof *t); /* realloc ptrs */
if (!tmp) {
perror ("realloc-t");
freep2p (t, ncsv);
goto readdone;
}
t = tmp;
t[ncsv] = NULL; /* set new pointer NULL */
/* allocate nchr chars to new pointer */
if (!(t[ncsv] = malloc (nchr * sizeof *t[ncsv]))) {
perror ("malloc-t[i]");
while (ncsv--) /* free everything else on failure */
free (t[ncsv]);
goto readdone;
}
tmp = realloc (used, newsz * sizeof *used); /* realloc used */
if (!tmp) {
perror ("realloc-used");
freep2p (t, ncsv);
goto readdone;
}
used = tmp;
used[ncsv] = 0;
ncsv++;
}
if (nchr - used[csvl] - 2 < len) { /* check word fits in line */
/* realloc t[i] if required (left for you) */
fputs ("realloc t[i] required.\n", stderr);
}
/* write word to csv line at end */
sprintf (t[csvl] + used[csvl], used[csvl] ? ",%s" : "%s", word);
t[csvl][used[csvl] ? used[csvl] + len + 1 : len] = 0;
used[csvl] += used[csvl] ? len + 1 : len;
}
csvl++;
}
readdone:;
freep2p (l, *n);
free (offset);
free (used);
*n = csvl;
return t;
}
int main (int argc, char **argv) {
char **t;
size_t n = 0;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!(t = transpose2csv (&n, fp))) {
fputs ("error: transpose2csv failed.\n", stderr);
return 1;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < n; i++)
if (t[i])
puts (t[i]);
freep2p (t, n);
return 0;
}
Example Use/Output
$ ./bin/transpose2csv < dat/col2csv3x4.txt
JACK,20,London
Maria,34,NewYork
Stephan,45,Toronto
Nora,28,Berlin
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/transpose2csv < dat/col2csv3x4.txt
==18604== Memcheck, a memory error detector
==18604== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==18604== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==18604== Command: ./bin/transpose2csv
==18604==
JACK,20,London
Maria,34,NewYork
Stephan,45,Toronto
Nora,28,Berlin
==18604==
==18604== HEAP SUMMARY:
==18604== in use at exit: 0 bytes in 0 blocks
==18604== total heap usage: 15 allocs, 15 frees, 4,371 bytes allocated
==18604==
==18604== All heap blocks were freed -- no leaks are possible
==18604==
==18604== For counts of detected and suppressed errors, rerun with: -v
==18604== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I am trying to write a program in c that compare strings. The strings are given in pairs and in the top of the file there is the number of the pairs.
The file has a form like the following:
2
a: 01010100000101011111
01001010100000001111
00000000000011110000
b: 00000111110000010001
10101010100111110001
a: 00000011111111111100
00111111111111000
b: 00000001111001010101
My problem is to read the strings properly in order to execute comparisons etc
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#define NCHAR 32
int main (int argc, char **argv) {
char *word1 = NULL;
FILE *fp = NULL;
for (int i = 0; i<pairs; i++){
if (i == 0)
{
word1 = readWord(fp, &word1);//read a:
while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
}
word1 = readWord(fp, &word1);//read string
while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
aline = malloc(amaxsize);
strncpy(aline, word1, amaxsize);
word1 = readWord(fp, &word1);
while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
while (strcmp(word1, "b:")!=0){
aline = concat(aline, word1);
word1 = readWord(fp, &word1);
while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
}
fprintf(fpw, "a: %s\n", aline); //write to the file..
free (word1);
word1 = NULL;
word1 = readWord(fp, &word1); //read string after b:
while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
bline = malloc(bmaxsize);
strncpy(bline, word1, bmaxsize);
word1 = readWord(fp, &word1);
while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
if (i == (pairs-1))
{
while (strcmp(word1, "")!=0){
bline = concat(bline, word1);
word1 = readWord(fp, &word1);
}
}
else
{
while (strcmp(word1, "a:")!=0){
bline = concat(bline, word1);
word1 = readWord(fp, &word1);
while(strcmp(word1, "") == 0) word1 = readWord(fp, &word1);
}
}
fprintf(fpw, "b: %s\n", bline); //write to the file..
free (word1);
word1 = NULL;
fprintf(fpw,"\n");
}
char *readWord(FILE *fp, char **buffer)
{
int ch, nchar = NCHAR;
int buflen = 0;
*buffer = malloc (nchar);
if(*buffer){
while ((ch = fgetc(fp)) != '\n' && ch != EOF && ch != '\t' && ch != ' ')
{
if (ch!='\t' && ch!= ' ' && ch != '\n') (*buffer)[buflen++] = ch;
if (buflen + 1 >= nchar) { /* realloc */
char *tmp = realloc (*buffer, nchar * 2);
if (!tmp) {
(*buffer)[buflen] = 0;
return *buffer;
}
*buffer = tmp;
nchar *= 2;
}
}
(*buffer)[buflen] = 0; /* nul-terminate */
if (buflen == 0 && ch == EOF) { /* return NULL if nothing read */
free (*buffer);
*buffer = NULL;
}
return *buffer;
}
else {
fprintf (stderr, "Error...\n");
return NULL;
}
}
readWord function reads a word per time. What I am trying to do is reading the file in words and concatenate them to get the full string a and save it in aline so I can work on it. Same with b. The problem is that the file is not read properly, for example instead of getting the whole a of the first pair, I'm getting only the first part of it. Is there any idea?
The read you are attempting from the file is non-trivial but can be handled fairly simply by setting a flag telling you whether you are already seen an 'a' or 'b', skipping all whitespace and ':' characters, storing all other characters in your buffer, reallocating as needed, and then when the second 'a' or 'b'is found, putting that character back in the FILE* stream with ungetc, nul-terminating and returning your buffer.
Sounds easy enough -- right? Well, that's pretty much it. Let's look at what would be needed in your readword() function.
First, since you are allocating for buffer in readword(), there is no need to pass char **buffer as a parameter. You have already declared readword as char *readword(...) so just pass the FILE* pointer as a parameter and return a pointer to your allocated, filled and nul-terminated buffer.
You can handle the reallocation scheme any way you like, You can either start with some reasonable number of characters allocated and then double (or add some multiple to) the current size, or just add a fixed amount each time you run out. The example below simply starts with a 32-char buffer and then adds another 32-chars each time reallocation is needed. (if the data size was truly unknown, I would probably start with 32-chars and then double each time I ran out -- completely up to you).
Using the isspace() function found in ctype.h ensures all whitespace is handled correctly.
The last few issues are simply ensuring you return a nul-terminated string in buffer and making sure you re-initialize your pointer to the end of your buffer in each new block of memory when realloc is called.
Putting it altogether, you could do something similar to the following. A simple example program is added after the readword() function to read your example file and output the combined strings read from the file,
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define NCHR 32
char *readword (FILE *fp)
{
int c, /* current character */
firstline = 0; /* flag for 'a' or 'b' found at 1st char */
size_t n = 0, nchr = NCHR; /* chars read, number of chars allocated */
char *buffer = NULL, *p; /* buffer to fill, pointer to buffer */
buffer = malloc (nchr); /* allocate initial NCHR */
if (!buffer) { /* validate */
perror ("malloc-buffer");
return NULL;
}
p = buffer; /* set pointer to buffer */
while ((c = fgetc (fp)) != EOF) { /* read each char */
if (isspace (c) || c == ':') /* skip all whitespace and ':' */
continue;
if (c == 'a' || c == 'b') { /* begins with 'a' or 'b' */
if (firstline) { /* already had a/b line */
ungetc (c, fp); /* put the char back */
*p = 0; /* nul-terminate */
return buffer; /* return filled buffer */
}
firstline = 1; /* set firstline flag */
continue;
}
else {
if (n == nchr - 2) { /* check if realloc needed */
void *tmp = realloc (buffer, nchr + NCHR);
if (!tmp) /* validate */
exit (EXIT_FAILURE);
buffer = tmp; /* assign new block to buffer */
p = buffer + n; /* set p at buffer end */
nchr += NCHR; /* update no. chars allocated */
}
*p++ = c; /* assign the current char and advance p */
n++; /* increment your character count */
}
}
*p = 0; /* nul-terminate */
return buffer;
}
int main (int argc, char **argv) {
char buf[NCHR], *word;
int nwords, toggle = 0;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!fgets (buf, NCHR, fp)) {
fputs ("error: read of line 1 failed.\n", stderr);
return 1;
}
if (sscanf (buf, "%d", &nwords) != 1) {
fputs ("error: invalid file format.\n", stderr);
return 1;
}
nwords *= 2; /* actual number of words is twice the number of pairs */
while (nwords-- && (word = readword (fp))) {
printf ("%c: %s\n", toggle ? 'b' : 'a', word);
free (word);
if (toggle) {
putchar ('\n');
toggle = 0;
}
else
toggle = 1;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(note: above the toggle is simply a 1 or 0 flag used to either output "a:" or "b:" at the beginning of the appropriate line and add a '\n' between the pairs of lines read.)
Example Use/Output
$ ./bin/read_multiline_pairs dat/pairsbinline.txt
a: 010101000001010111110100101010000000111100000000000011110000
b: 0000011111000001000110101010100111110001
a: 0000001111111111110000111111111111000
b: 00000001111001010101
Memory Use/Error Check
Always verify your memory use when you dynamically allocate storage and ensure you have freed all the memory you allocate.
$ valgrind ./bin/read_multiline_pairs dat/pairsbinline.txt
==14257== Memcheck, a memory error detector
==14257== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14257== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==14257== Command: ./bin/read_multiline_pairs dat/pairsbinline.txt
==14257==
a: 010101000001010111110100101010000000111100000000000011110000
b: 0000011111000001000110101010100111110001
a: 0000001111111111110000111111111111000
b: 00000001111001010101
==14257==
==14257== HEAP SUMMARY:
==14257== in use at exit: 0 bytes in 0 blocks
==14257== total heap usage: 8 allocs, 8 frees, 872 bytes allocated
==14257==
==14257== All heap blocks were freed -- no leaks are possible
==14257==
==14257== For counts of detected and suppressed errors, rerun with: -v
==14257== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Look things over and let me know if you have questions. The largest part of the problem was handling the read and concatenation of all the lines for each pair. The rest of the coding is left to you.
I want split_str to be able to take, for example, "bob is great" and return ["bob", "is", "great"].
More precisely: foo = split_str("bob is great", " ") allocates["bob", "is", "great"] in foo (thus becoming an array of 3 strings which were all separated by a space, as specified... but I would like this to be generalized to not only generating arrays of 3 strings, but of any amount of strings if possible).
char* split_str(char*, char[]);
char* split_str(char* str, char delim[]) {
char copied_input[strlen(str)];
strncpy (copied_input, str, strlen(str)+1);
char* result[strlen(str)+1]; // add 1 for the "NULL" char
int tmp = 0; // preparing iterator
result[tmp] = strtok (copied_input, delim); // obtaining first word
while (result[tmp] != NULL) { // to populate the whole array with each words separately
result[++tmp] = strtok (NULL, delim);
}
return result;
}
This represents more or less the kind of execution I'm trying to achieve:
int main (void)
{
int MAX_AMNT = 50; // maximum amount of args to parse
char *bar[MAX_AMNT];
bar = split_str("bob is great", " ");
tmp = 0;
while (bar[tmp] != NULL) {
fprintf (stdout, "Repeating, from array index %d: %s\n", tmp, bar[tmp++]);
}
}
I'm very new to C so I might be wrong in the way I've phrased my question (pointers and arrays, and pointers of arrays, and etc. is a bit of a headache still for me).
I know my return signature is wrong for my function, and also that it's probably wrong to return a local variable (result), but I'm lost as of how to proceed from here. I tried changing it to a void function and adding a third argument as a variable that would be populated (as result is), but I keep getting errors.
A solution is :
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char ** split(const char * str, const char * delim)
{
/* count words */
char * s = strdup(str);
if (strtok(s, delim) == 0)
/* no word */
return NULL;
int nw = 1;
while (strtok(NULL, delim) != 0)
nw += 1;
strcpy(s, str); /* restore initial string modified by strtok */
/* split */
char ** v = malloc((nw + 1) * sizeof(char *));
int i;
v[0] = strdup(strtok(s, delim));
for (i = 1; i != nw; ++i)
v[i] = strdup(strtok(NULL, delim));
v[i] = NULL; /* end mark */
free(s);
return v;
}
int main()
{
char ** v = split("bob is great", " ");
for (int i = 0; v[i] != NULL; ++i) {
puts(v[i]);
free(v[i]);
}
free(v);
return 0;
}
As you see I add a null pointer at the end of the vector as a mark, but it can be changed easily to return the number of words etc
Execution :
bob
is
great
A second solution taking into account the remarks of alk :
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char ** split(const char * str, const char * delim)
{
/* count words */
char * s = strdup(str);
if ((s == NULL) /* out of memory */
|| (strtok(s, delim) == 0)) /* no word */
return NULL;
size_t nw = 1;
while (strtok(NULL, delim) != 0)
nw += 1;
strcpy(s, str); /* restore initial string modified by strtok */
/* split */
char ** v = malloc((nw + 1) * sizeof(char *));
if (v == NULL)
/* out of memory */
return NULL;
if ((v[0] = strdup(strtok(s, delim))) == 0) {
/* out of memory */
free(v);
return NULL;
}
size_t i;
for (i = 1; i != nw; ++i) {
if ((v[i] = strdup(strtok(NULL, delim))) == NULL) {
/* out of memory, free previous allocs */
while (i-- != 0)
free(v[i]);
free(v);
return NULL;
}
}
v[i] = NULL; /* end mark */
free(s);
return v;
}
int main()
{
const char * s = "bob is still great";
char ** v = split(s, " ");
if (v == NULL)
puts("no words of not enough memory");
else {
for (int i = 0; v[i] != NULL; ++i) {
puts(v[i]);
free(v[i]);
}
free(v);
}
return 0;
}
When out of memory the return value is NULL ( in a previous version it was the string to split), of course there are other ways to signal that easily
Execution under valgrind :
==5078== Memcheck, a memory error detector
==5078== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5078== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5078== Command: ./a.out
==5078==
bob
is
still
great
==5078==
==5078== HEAP SUMMARY:
==5078== in use at exit: 0 bytes in 0 blocks
==5078== total heap usage: 7 allocs, 7 frees, 1,082 bytes allocated
==5078==
==5078== All heap blocks were freed -- no leaks are possible
==5078==
==5078== For counts of detected and suppressed errors, rerun with: -v
==5078== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)
An approach to split a string of unknown number of words and make them available in return from a function would require a function that returns a pointer-to-pointer-to-char. This allows a true dynamic approach where you allocate some initial number of pointers (say 2, 4, 8, etc..) make a single pass through your string using strtok keeping track of the number of pointers used, allocating storage fro each token (word) as you go and when the number of pointers used equals the number allocated, you simply realloc storage for additional pointers and keep going.
A short example implementing the function splitstring() that does that could look similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NPTR 8 /* initial number of pointers to allocate */
#define MAXD 32 /* maximum no chars for delimiter */
#define MAXC 1024 /* maximum no chars for user input */
char **splitstring (const char *str, const char *delim, size_t *nwords)
{
size_t nptr = NPTR, /* initial pointers */
slen = strlen (str); /* length of str */
char **strings = malloc (nptr * sizeof *strings), /* alloc pointers */
*cpy = malloc (slen + 1), /* alloc for copy of str */
*p = cpy; /* pointer to cpy */
*nwords = 0; /* zero nwords */
if (!strings) { /* validate allocation of strings */
perror ("malloc-strings");
free (cpy);
return NULL;
}
if (!cpy) { /* validate allocation of cpy */
perror ("malloc-cpy");
free (strings);
return NULL;
}
memcpy (cpy, str, slen + 1); /* copy str to cpy */
/* split cpy into tokens */
for (p = strtok (p, delim); p; p = strtok (NULL, delim)) {
size_t len; /* length of token */
if (*nwords == nptr) { /* all pointers used/realloc needed? */
void *tmp = realloc (strings, 2 * nptr * sizeof *strings);
if (!tmp) { /* validate reallocation */
perror ("realloc-strings");
if (*nwords) /* if words stored, return strings */
return strings;
else { /* no words, free pointers, return NULL */
free (strings);
return NULL;
}
}
strings = tmp; /* assign new block to strings */
nptr *= 2; /* update number of allocate pointers */
}
len = strlen (p); /* get token length */
strings[*nwords] = malloc (len + 1); /* allocate storage */
if (!strings[*nwords]) { /* validate allocation */
perror ("malloc-strings[*nwords]");
break;
}
memcpy (strings[(*nwords)++], p, len + 1); /* copy to strings */
}
free (cpy); /* free storage of cpy of str */
if (*nwords) /* if words found */
return strings;
free (strings); /* no strings found, free pointers */
return NULL;
}
int main (void) {
char **strings = NULL,
string[MAXC],
delim[MAXD];
size_t nwords = 0;
fputs ("enter string : ", stdout);
if (!fgets (string, MAXC, stdin)) {
fputs ("(user canceled input)\n", stderr);
return 1;
}
fputs ("enter delimiters: ", stdout);
if (!fgets (delim, MAXD, stdin)) {
fputs ("(user canceled input)\n", stderr);
return 1;
}
if ((strings = splitstring (string, delim, &nwords))) {
for (size_t i = 0; i < nwords; i++) {
printf (" word[%2zu]: %s\n", i, strings[i]);
free (strings[i]);
}
free (strings);
}
else
fputs ("error: no delimiter found\n", stderr);
}
(note: the word count nwords is passed as a pointer to the splitstring() function to allow the number of words to be updated within the function and made available back in the calling function, while returning a pointer-to-pointer-to-char from the function itself)
Example Use/Output
$ ./bin/stringsplitdelim
enter string : my dog has fleas and my cat has none and snakes don't have fleas
enter delimiters:
word[ 0]: my
word[ 1]: dog
word[ 2]: has
word[ 3]: fleas
word[ 4]: and
word[ 5]: my
word[ 6]: cat
word[ 7]: has
word[ 8]: none
word[ 9]: and
word[10]: snakes
word[11]: don't
word[12]: have
word[13]: fleas
(note: a ' ' (space) was entered as the delimiter above resulting in delim containing " \n" (exactly what you want) by virtue of having used the line-oriented input function fgets for user input)
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/stringsplitdelim
==12635== Memcheck, a memory error detector
==12635== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==12635== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==12635== Command: ./bin/stringsplitdelim
==12635==
enter string : my dog has fleas and my cat has none and snakes don't have fleas
enter delimiters:
word[ 0]: my
word[ 1]: dog
word[ 2]: has
word[ 3]: fleas
word[ 4]: and
word[ 5]: my
word[ 6]: cat
word[ 7]: has
word[ 8]: none
word[ 9]: and
word[10]: snakes
word[11]: don't
word[12]: have
word[13]: fleas
==12635==
==12635== HEAP SUMMARY:
==12635== in use at exit: 0 bytes in 0 blocks
==12635== total heap usage: 17 allocs, 17 frees, 323 bytes allocated
==12635==
==12635== All heap blocks were freed -- no leaks are possible
==12635==
==12635== For counts of detected and suppressed errors, rerun with: -v
==12635== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I wanted to know if there was a way to use scanf so I can take in an unknown number of string arguments and put them into a char* array. I have seen it being done with int values, but can't find a way for it to be done with char arrays. Also the arguments are entered on the same line separated by spaces.
Example:
user enters hello goodbye yes, hello gets stored in array[0], goodbye in array[1] and yes in array[2]. Or the user could just enter hello and then the only thing in the array would be hello.
I do not really have any code to post, as I have no real idea how to do this.
You can do something like, read until the "\n" :
scanf("%[^\n]",buffer);
you need to allocate before hand a big enough buffer.
Now go through the buffer count the number of words, and allocate the necessary space char **array = ....(dynamic string allocation), go to the buffer and copy string by string into the array.
An example:
int words = 1;
char buffer[128];
int result = scanf("%127[^\n]",buffer);
if(result > 0)
{
char **array;
for(int i = 0; buffer[i]!='\0'; i++)
{
if(buffer[i]==' ' || buffer[i]=='\n' || buffer[i]=='\t')
{
words++;
}
}
array = malloc(words * sizeof(char*));
// Using RoadRunner suggestion
array[0] = strtok (buffer," ");
for(int w = 1; w < words; w++)
{
array[w] = strtok (NULL," ");
}
}
As mention in the comments you should use (if you can) fgets instead fgets(buffer,128,stdin);.
More about strtok
If you have an upper bound to the number of strings you may receive from the user, and to the number of characters in each string, and all strings are entered on a single line, you can do this with the following steps:
read the full line with fgets(),
parse the line with sscanf() with a format string with the maximum number of %s conversion specifiers.
Here is an example for up to 10 strings, each up to 32 characters:
char buf[400];
char s[10][32 + 1];
int n = 0;
if (fgets(buf, sizeof buf, sdtin)) {
n = sscanf("%32s%32s%32s%32s%32s%32s%32s%32s%32s%32s",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9]));
}
// `n` contains the number of strings
// s[0], s[1]... contain the strings
If the maximum number is not known of if the maximum length of a single string is not fixed, or if the strings can be input on successive lines, you will need to iterate with a simple loop:
char buf[200];
char **s = NULL;
int n;
while (scanf("%199s", buf) == 1) {
char **s1 = realloc(s, (n + 1) * sizeof(*s));
if (s1 == NULL || (s1[n] = strdup(buf)) == NULL) {
printf("allocation error");
exit(1);
}
s = s1;
n++;
}
// `n` contains the number of strings
// s[0], s[1]... contain pointers to the strings
Aside from the error handling, this loop is comparable to the hard-coded example above but it still has a maximum length for each string. Unless you can use a scanf() extension to allocate the strings automatically (%as on GNU systems), the code will be more complicated to handle any number of strings with any possible length.
You can use:
fgets to read input from user. You have an easier time using this instead of scanf.
malloc to allocate memory for pointers on the heap. You can use a starting size, like in this example:
size_t currsize = 10
char **strings = malloc(currsize * sizeof(*strings)); /* always check
return value */
and when space is exceeded, then realloc more space as needed:
currsize *= 2;
strings = realloc(strings, currsize * sizeof(*strings)); /* always check
return value */
When finished using the requested memory from malloc() and realloc(), it's always to good to free the pointers at the end.
strtok to parse the input at every space. When copying over the char * pointer from strtok(), you must also allocate space for strings[i], using malloc() or strdup.
Here is an example I wrote a while ago which does something very similar to what you want:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITSIZE 10
#define BUFFSIZE 100
int
main(void) {
char **strings;
size_t currsize = INITSIZE, str_count = 0, slen;
char buffer[BUFFSIZE];
char *word;
const char *delim = " ";
int i;
/* Allocate initial space for array */
strings = malloc(currsize * sizeof(*strings));
if(!strings) {
printf("Issue allocating memory for array of strings.\n");
exit(EXIT_FAILURE);
}
printf("Enter some words(Press enter again to end): ");
while (fgets(buffer, BUFFSIZE, stdin) != NULL && strlen(buffer) > 1) {
/* grow array as needed */
if (currsize == str_count) {
currsize *= 2;
strings = realloc(strings, currsize * sizeof(*strings));
if(!strings) {
printf("Issue reallocating memory for array of strings.\n");
exit(EXIT_FAILURE);
}
}
/* Remove newline from fgets(), and check for buffer overflow */
slen = strlen(buffer);
if (slen > 0) {
if (buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
printf("Exceeded buffer length of %d.\n", BUFFSIZE);
exit(EXIT_FAILURE);
}
}
/* Parsing of words from stdin */
word = strtok(buffer, delim);
while (word != NULL) {
/* allocate space for one word, including nullbyte */
strings[str_count] = malloc(strlen(word)+1);
if (!strings[str_count]) {
printf("Issue allocating space for word.\n");
exit(EXIT_FAILURE);
}
/* copy strings into array */
strcpy(strings[str_count], word);
str_count++;
word = strtok(NULL, delim);
}
}
/* print and free strings */
printf("Your array of strings:\n");
for (i = 0; i < str_count; i++) {
printf("strings[%d] = %s\n", i, strings[i]);
free(strings[i]);
strings[i] = NULL;
}
free(strings);
strings = NULL;
return 0;
}