How to copy a string to a 2D array in c - arrays

I want to create a c program that when the user enters some words like this: "some,words, in, c, proramming." the program save words in the string "str", then it creates Dynamically a 2D array and copies the words into the 2D array:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <math.h>
#include <conio.h>
void freeMememory(int**array, int row){
for(int i=0;i<row;i++)
free(array[i]);
free(array);
}
int lettersCount(char *arr){
int space=0, letters=0;
do{
if(*arr !=' '&& *arr!='\t' && *arr!=','&& *arr!='.'){
letters =letters+1;
}
++arr;
}while(*arr);
return letters;
}
int wordCount(char *arr){
int space=0, words=0;
for(int i=0; arr[i]!='\0'; i++){
if(arr[i] ==' '|| arr[i]=='\t'|| arr[i]=='\n'||arr[i]==','||arr[i]=='.'){
space++;
}
if(space>0){
words++;
space=0;
}
}
return words;
}
int main (){
char arr[100];
int i, j, row, column;
scanf("%[^\n]s", &arr);
int *words = wordCount(arr);
int *letters = lettersCount(arr);
row=words;
column=letters;
int **ptr = (int **)malloc(row*column*sizeof(int));
for(i=0;i<row;i++){ptr[i]=(int*)malloc(column*sizeof(int));}
/*
//how should I write here to copy only words from arr to ptr?
like this:
arr = "some words, two,three,four."
ptr = {
"some", "words", "two", "", "three", "four",
}
*/
freeMememory(ptr, row);
return 0;}
So any ideas how to copy only the words from the string into the 2D array without copying (periods, spaces, cammas)?

What you might be looking for is strtok from <string.h>. I will also replace row with rows and column with columns in the following code snippet, as suggested by tadman in the comments.
/* no need to cast `malloc` */
char *ptr[rows];
for (int i = 0; i < rows; ++i) {
ptr[i] = malloc(columns);
if (!token) {
fprintf(stderr, "Error: memory allocation failed\n");
exit(EXIT_FAILURE);
}
}
const char *delims = " \t\n,.";
/* second argument are delimiters */
strcpy(ptr[0], strtok(arr, delims));
for (int i = 1; i < rows; ++i)
strcpy(ptr[i], strtok(NULL, delims));
I would also suggest simplifying your functions. For example your wordCount function could probably be simplified to this:
int count_words(char *str, const char *delims)
{
words = 1;
for (int i = 0; str[i] != '\0'; ++i)
if (strchr(delims, str[i]))
++words;
return words;
}
The function count_words could then be called like this:
const char *delims = " \t\n,.";
int words = count_words(arr, delims);

First notice that your code isn't using a 2D array. It's using an array of char-pointers that each point to a char-array. It's a different thing but it can be used in much the same way.
Below is an implementation that uses strtok to split the input string. Further, it uses realloc to make the array of char-pointers grow when a new word is found. Finally it uses a sentinel (i.e. NULL) to indicate end-of-words.
The code is pretty simple but the performance is poor.
Example:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char** split(const char* str)
{
if (str == NULL) exit(1);
// Copy input string as strtok changes its input
char* str_cpy = malloc(strlen(str) + 1);
if (str_cpy == NULL) exit(1);
strcpy(str_cpy, str);
unsigned num_rows = 0;
char** arr = NULL;
// Get first token
const char *delims = " \t\n,.";
char* ptr = strtok(str_cpy, delims);
while (ptr)
{
// Allocate one more row
arr = realloc(arr, (num_rows + 1) * sizeof *arr);
if (arr == NULL) exit(1);
// Allocate memory for one more word
arr[num_rows] = malloc(strlen(ptr) + 1);
if (arr[num_rows] == NULL) exit(1);
strcpy(arr[num_rows], ptr);
++num_rows;
// Get next token
ptr = strtok(NULL, delims);
}
// Add a sentinel to indicate end-of-words
arr = realloc(arr, (num_rows + 1) * sizeof *arr);
if (arr == NULL) exit(1);
arr[num_rows] = NULL;
free(str_cpy);
return arr;
}
int main(void)
{
char* str = "some,words, in, c, programming.";
char** arr = split(str);
printf("Original string: %s\n", str);
for (int i=0; arr[i] != NULL; ++i)
{
printf("Word[%d]: %s\n", i, arr[i]);
}
// Free array
for (int i=0; arr[i] != NULL; ++i)
{
free(arr[i]);
}
free(arr);
return 0;
}
Output:
Original string: some,words, in, c, programming.
Word[0]: some
Word[1]: words
Word[2]: in
Word[3]: c
Word[4]: programming

Related

Dynamic memory allocation for an array of pointers to char in C

I'm building a word counter program. To achieve this, I was thinking about saving the string the user inputted, and using strtok() to split the sentence with space as the delimiter. But first I want to allocate enough memory for each word. Let's say the sentence is "Hello World". I've already dynamically allocated memory for the string itself. Now I want to split Hello World into 2 strings, "Hello" and "World". My goal is to allocate enough memory so that there's not too much empty space but I also don't want to allocate too little space. Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
char *strmalloc(char **string);
char *user_input = NULL;
char *word_array[];
int main(void) {
printf("Enter a sentence to find out the number of words: ");
user_input = strmalloc(&user_input);
return 0;
}
char *strmalloc(char **string) {
char *tmp = NULL;
size_t size = 0, index = 0;
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
if (size <= index) {
size += 1;
tmp = realloc(*string, size);
if (!tmp) {
free(*string);
string = NULL;
break;
}
*string = tmp;
}
(*string)[index++] = ch;
}
return *string;
}
How would I go about doing this? Should I do the splitting first or allocate the space required for the array first?
You can count words without splitting the sentence, here is an example :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
// Change this to change the separator characters
static inline char isSeparator(char ch) { return isspace(ch) || ispunct(ch); }
char * jumpSeparator(char *string) {
while(string[0] && isSeparator(string[0])) string++;
return string;
}
char * findEndOfWord(char *string) {
while (string[0] && !isSeparator(string[0])) string++;
return string;
}
int countWords(char *string) {
char * ptr = jumpSeparator(string);
if (strlen(ptr) == 0) return 0;
int count = 1;
while((ptr = findEndOfWord(ptr)) && ptr[0]) {
ptr = jumpSeparator(ptr);
if (!ptr) break;
count++;
}
return count;
}
int main() {
char * sentence = "This is,a function... to||count words";
int count = countWords(sentence);
printf("%d\n", count); //====> 7
}
EDIT : Reusing the same functions here is another example that allocates substrings dynamically :
int main() {
char * sentence = "This is,a function... to||split words";
int count = countWords(sentence);
char * ptr = sentence, *start, *end;
char ** substrings = malloc(count * sizeof(char *));
int i=0;
while((ptr = jumpSeparator(ptr)) && ptr[0]) {
start = ptr;
ptr = findEndOfWord(ptr);
end = ptr;
int len = end-start;
char * newString = malloc(len + 1);
memcpy(newString, start, len);
newString[len] = 0;
substrings[i++] = newString;
}
// Prints the result
for(int i=0; i<count; i++) printf("%s\n", substrings[i]);
// Frees the allocated memory
for(int i=0; i<count; i++) free(substrings[i]);
free(substrings);
return 0;
}
Output :
This
is
a
function
to
split
words

Copying specific number of characters from a string to another

I have a variable length string that I am trying to divide from plus signs and study on:
char string[] = "var1+vari2+varia3";
for (int i = 0; i != sizeof(string); i++) {
memcpy(buf, string[0], 4);
buf[9] = '\0';
}
since variables are different in size I am trying to write something that is going to take string into loop and extract (divide) variables. Any suggestions ? I am expecting result such as:
var1
vari2
varia3
You can use strtok() to break the string by delimiter
char string[]="var1+vari2+varia3";
const char delim[] = "+";
char *token;
/* get the first token */
token = strtok(string, delim);
/* walk through other tokens */
while( token != NULL ) {
printf( " %s\n", token );
token = strtok(NULL, delim);
}
More info about the strtok() here: https://man7.org/linux/man-pages/man3/strtok.3.html
It seems to me that you don't just want to want to print the individual strings but want to save the individual strings in some buffer.
Since you can't know the number of strings nor the length of the individual string, you should allocate memory dynamic, i.e. use functions like realloc, calloc and malloc.
It can be implemented in several ways. Below is one example. To keep the example simple, it's not performance optimized in anyway.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
char** split_string(const char* string, const char* token, int* num)
{
assert(string != NULL);
assert(token != NULL);
assert(num != NULL);
assert(strlen(token) != 0);
char** data = NULL;
int num_strings = 0;
while(*string)
{
// Allocate memory for one more string pointer
char** ptemp = realloc(data, (num_strings + 1) * sizeof *data);
if (ptemp == NULL) exit(1);
data = ptemp;
// Look for token
char* tmp = strstr(string, token);
if (tmp == NULL)
{
// Last string
// Allocate memory for one more string and copy it
int len = strlen(string);
data[num_strings] = calloc(len + 1, 1);
if (data[num_strings] == NULL) exit(1);
memcpy(data[num_strings], string, len);
++num_strings;
break;
}
// Allocate memory for one more string and copy it
int len = tmp - string;
data[num_strings] = calloc(len + 1, 1);
if (data[num_strings] == NULL) exit(1);
memcpy(data[num_strings], string, len);
// Prepare to search for next string
++num_strings;
string = tmp + strlen(token);
}
*num = num_strings;
return data;
}
int main()
{
char string[]="var1+vari2+varia3";
// Split the string into dynamic allocated memory
int num_strings;
char** data = split_string(string, "+", &num_strings);
// Now data can be used as an array-of-strings
// Example: Print the strings
printf("Found %d strings:\n", num_strings);
for(int i = 0; i < num_strings; ++i) printf("%s\n", data[i]);
// Free the memory
for(int i = 0; i < num_strings; ++i) free(data[i]);
free(data);
}
Output
Found 3 strings:
var1
vari2
varia3
You can use a simple loop scanning the string for + signs:
char string[] = "var1+vari2+varia3";
char buf[sizeof(string)];
int start = 0;
for (int i = 0;;) {
if (string[i] == '+' || string[i] == '\0') {
memcpy(buf, string + start, i - start);
buf[i - start] = '\0';
// buf contains the substring, use it as a C string
printf("%s\n", buf);
if (string[i] == '\0')
break;
start = ++i;
} else {
i++;
}
}
Your code does not have any sense.
I wrote such a function for you. Analyse it as sometimes is good to have some code as a base
char *substr(const char *str, char *buff, const size_t start, const size_t len)
{
size_t srcLen;
char *result = buff;
if(str && buff)
{
if(*str)
{
srcLen = strlen(str);
if(srcLen < start + len)
{
if(start < srcLen) strcpy(buff, str + start);
else buff[0] = 0;
}
else
{
memcpy(buff, str + start, len);
buff[len] = 0;
}
}
else
{
buff[0] = 0;
}
}
return result;
}
https://godbolt.org/z/GjMEqx

Array of dynamic length

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *names = NULL;
int capacity = 0;
int size = 0;
printf("Type 'end' if you want to stop inputting names\n");
while (1) {
char name[100];
printf("Input:\n");
fgets(name, sizeof(name), stdin);
if (strncmp(name, "end", 3) == 0) {
break;
}
if (size == capacity) {
char *temp = realloc(names, sizeof(char) * (size + 1));
if (!temp) {
if (names) {
return 1;
}
}
names = temp;
capacity++;
}
names[size] = name;
size++;
}
for (int i = 0; i < size; i++) {
printf("OUTPUT :%c\n", names[i]);
}
if (names) {
free(names);
}
}
I am trying to create an array of dynamic length in C but I don't know what is wrong with my code? I think it is cause of how I take the user input and the problem occurs when the code names[size] = name is executed.
You need to declare an array of pointers to strings (char**), not a pointer to a single character string (char*).
This means that when you want to add a new entry, not only do you need to make space for the new pointer in your array (type is char* not char) but you also have to separately allocate storage for the string itself and then set the pointer you just made space for to that allocated string.
There are multiple problems in the code:
names should be defined as a pointer to an array of char *: char **names = NULL;
you must make copies of the strings read with fgets(), otherwise all entries in names will point to the same array name.
you actually do not need to distinguish capacity and size if you reallocate one extra slot at a time.
the format "OUTPUT :%c\n" is incorrect for a string.
Here is a corrected version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char **names = NULL;
int capacity = 0;
int size = 0;
printf("Type 'end' if you want to stop inputting names\n");
for (;;) {
char name[100];
printf("Input:\n");
if (!fgets(name, sizeof(name), stdin)) {
break;
}
if (strncmp(name, "end", 3) == 0) {
break;
}
if (size == capacity) {
// reallocate array with geometric growth
int new_capacity = capacity + (capacity / 2) + 4;
char *temp = realloc(names, sizeof(*names) * new_capacity);
if (!temp) {
free(names);
printf("out of memory\n");
return 1;
}
names = temp;
capacity = new_capacity;
}
name[strcspn(name, "\n")] = '\0'; // strip the trailing newline if any
names[size++] = strdup(name); // allocate a copy of the string
}
for (int i = 0; i < size; i++) {
printf("OUTPUT: %s\n", names[i]);
}
free(names);
return 0;
}

Split line into array of words + C

I am trying to split a line into an array of words, but I am stuck on how to do this in C. My skills in C aren't very good, so I can't think of a way to "execute" my idea. Her is what I have so far:
int beginIndex = 0;
int endIndex = 0;
int maxWords = 10;
while (1) {
while (!isspace(str)) {
endIndex++;
}
char *tmp = (string from 'str' from beginIndex to endIndex)
arr[wordCnt] = tmp;
wordCnt++;
beginIndex = endIndex;
if (wordCnt = maxWords) {
return;
}
}
In my method I receive (char *str, char *arr[10]), and str is the line that I want to split when I encounter a space. arr is the array where I want to store the words. Is there any way to copy the 'chunk' of string that I want from 'str' into my tmp variable? This is the best way that I can think of right now, perhaps it's a terrible idea. If so, I would be happy to get some documentation or tips on a better method.
You should check out the C Library function strtok. You simply feed it the string you want to break up and a string of delimiters.
Here is an example of how it works (taken from the linked site):
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL) {
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
In your case instead of printing each string you would assign the pointer returned by strtok to the next element in your array arr.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int split(char *str, char *arr[10]){
int beginIndex = 0;
int endIndex;
int maxWords = 10;
int wordCnt = 0;
while(1){
while(isspace(str[beginIndex])){
++beginIndex;
}
if(str[beginIndex] == '\0')
break;
endIndex = beginIndex;
while (str[endIndex] && !isspace(str[endIndex])){
++endIndex;
}
int len = endIndex - beginIndex;
char *tmp = calloc(len + 1, sizeof(char));
memcpy(tmp, &str[beginIndex], len);
arr[wordCnt++] = tmp;
beginIndex = endIndex;
if (wordCnt == maxWords)
break;
}
return wordCnt;
}
int main(void) {
char *arr[10];
int i;
int n = split("1st 2nd 3rd", arr);
for(i = 0; i < n; ++i){
puts(arr[i]);
free(arr[i]);
}
return 0;
}

How can I split a char* into substrings in C?

I have a text like this:
char* str="Hi all.\nMy name is Matteo.\n\nHow are you?"
and I want to split the string by "\n\n" in to an array like this:
char* array[3];
array[0]="Hi all.\nMy name is Matteo."
array[1]="How are you?"
array[2]=NULL
I've tried the strtok function but it does not split the string correctly.
#include <stdio.h>
#include <string.h>
int main(){
char *str="Hi all.\nMy name is Matteo.\n\nHow are you?";
char *array[3];
char *ptop, *pend;
char wk[1024];//char *wk=malloc(sizeof(char)*(strlen(str)+3));
int i, size = sizeof(array)/sizeof(char*);
/*
array[0]="Hi all.\nMy name is Matteo."
array[1]="How are you?"
array[2]=NULL
*/
strcpy(wk, str);
strcat(wk, "\n\n");
for(i=0, ptop=wk;i<size;++i){
if(NULL!=(pend=strstr(ptop, "\n\n"))){
*pend='\0';
array[i]=strdup(ptop);
ptop=pend+2;
} else {
array[i]=NULL;
break;
}
}
for(i = 0;i<size;++i)
printf("array[%d]=\"%s\"\n", i, array[i]);
return 0;
}
The strtok() function works on a set of single character delimiters. Your goal is to split by a two character delimiter, so strtok() isn't a good fit.
You could scan your input string via a loop that used strchr to find newlines and then checked to see if the next char was also a newline.
A more generic method based on strstr function:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char* str="Hi all.\nMy name is Matteo.\n\nHow are you?\n\nThanks";
char **result = NULL;
unsigned int index = 0;
unsigned int i = 0;
size_t size = 0;
char *ptr, *pstr;
ptr = NULL;
pstr = str;
while(pstr) {
ptr = strstr(pstr, "\n\n");
result = realloc(result, (index + 1) * sizeof(char *));
size = strlen(pstr) - ((ptr)?strlen(ptr):0);
result[index] = malloc(size * sizeof(char));
strncpy(result[index], pstr, size);
index++;
if(ptr) {
pstr = ptr + 2;
} else {
pstr = NULL;
}
} ;
for(i = 0; i < index; i++) {
printf("Array[%d] : >%s<\n", i, result[i]);
}
return 0;
}

Resources