String split in c without changing original [closed] - c

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
we have an school asignment to make a specific function of string split
the header and arguments of function is given cannot change them .
this function should split original by delimiter into array of string that store each part of string delimiter not included and in additon for each delimiter increase size
void stringSplit(const char *original, char result[50][256], int* size, char delim){
size_t begin = 0;
if(!original){
return;
}
for(size_t i=0; i<strlen(original);i++ ){
if (original[i] == delim){
int str_begin = 0;
for( ; begin < i; begin++ ){
result[*size][str_begin] = original[begin];
str_begin++;
}
(*size)++;
begin ++ ;
}
}
}

The idea is OK, but you have a couple of errors:
Your string copies are not '\0'-terminated. Also you are not taking into
account when multiple delimiters follow in a row.
If you are not allowed to use
functions like strchr and strncpy, then you can do this:
size_t str_begin = 0;
*size = 0; // the user may have passed an uninitialized int variable
for(size_t i = 0; original[i] && *size < 50; ++i)
{
if(original[i] == delim)
{
size_t sublen = i - str_begin;
if(sublen == 0)
{
// skip delimiter, last character was also a
// delimiter and/or the first character in
// original is a delimiter
str_begin++;
continue;
// or if you want to have empty words instead of skipping
// results[*size] = 0;
// (*size)++;
}
if(sublen > 256)
sublen = 256;
for(size_t j = 0; j < sublen; ++j)
result[*size][j] = original + str_begin + j;
result[*size][j] = 0; // \0-terminating string
(*size)++;
str_begin = i + 1; // updating str_begin to next char
}
}

The straight forward way of doing so is - as suggested by Barmar - to make a copy of the original string and then work with strtok() and strcpy.
Alternatively, you could use strchr in a loop to find the delimiters and memcpy to copy the memory between the previous delimiter and the next one. See the following code, which also takes the boundaries of the result array into account:
void stringSplit(const char *original, char result[50][256], int *size, char
delim)
{
if(!original){
return ;
}
*size = 0;
const char* nextDelim;
const char* prevDelim = original;
do {
nextDelim = strchr(prevDelim,delim);
size_t len = nextDelim ? (nextDelim - prevDelim) : strlen(prevDelim);
if (len >= 256) {
len = 256-1;
}
memcpy(result[*size],prevDelim,len);
result[*size][len] = '\0';
(*size)++;
prevDelim = nextDelim ? nextDelim+1 : NULL;
}
while(nextDelim && *size < 50);
}
int main() {
char result[50][256];
int size;
stringSplit("Hello, this, is", result, &size, ',');
return 0;
}
Hope it helps.

Related

How to store a substring given a delimiter in C

Let's say I have a series of data that's in this form:
"SomethingIDontCareAbout : SomethingICareAbout"
where the part after the ":" can vary in length of course.
The goal here is only storing the "SomethingICareAbout" substring efficiently. I made this function but the problem is that I'm storing both substrings,so it seems like a waste of memory. Any help to reduce to the time/space complexity?
char** ExtractKey(char* S)
{
int n = strlen(S);
int count = 0, i = 0, j = 0;
for(i = 0; i < n; i++)
{
if(S[i] == ':')
break;
count++;
}
char** T = (char**)malloc(2 * sizeof(char*));
T[0] = (char*)malloc((count + 1) * sizeof(char));
T[1] = (char*)malloc((n - count) * sizeof(char));
for(i = 0; i < count; i++) // inefficient ? cus we won't need T[0] [j]
{
T[0][j] = S[i];
j++;
}
T[0][j+1] = '\0';
j = 0;
for(i = count + 1; i < n; i++)
{
T[1][j] = S[i];
j++;
}
T[1][j+1] = '\0';
return T;
}
There is no reason to invent a search for a character in a string, or a copy of a string.
If the input data will live long enough for you to use the "value" part, just return a pointer to the value:
char* ExtractKey(char* S)
{
return strchr(S, ':');
}
If it doesn't, or if you for some reason need a separate copy:
char* ExtractKey(char* S)
{
return strdup(strchr(S, ':'));
}
Honestly, this could be done efficiently if strtok() was used to split those strings. I have designed the following code that parses each string of a 2-D array with a common delimiter that is : here.
Now, let's take a look into the code (notice the comments):
#include <stdio.h>
#include <string.h>
#define MAX_LEN 128
int main(void) {
// The 2-D string
char str[][MAX_LEN] = {"SomethingElse : SomethingToCareAbout",
"Something2 : SomethingToCare2",
"Unnecessary : Necessary"};
int size = sizeof(str) / sizeof(str[0]);
// Applying Variable-Length Array (valid in C)
char store_cared_ones[size][MAX_LEN];
for (int i = 0; i < size; i++) {
// Declaring a temporary pointer variable to obtain the required
// substring from each string
char *sub_str = NULL;
sub_str = strtok(str[i], ": ");
sub_str = strtok(NULL, ": ");
// Copying the 'sub_str' into each array element of 'store_cared_ones'
strcpy(store_cared_ones[i], sub_str);
}
// Displaying each of 'store_cared_ones'
for (int i = 0; i < size; i++)
fprintf(stdout, "%s\n", store_cared_ones[i]);
return 0;
}
Finally, let's see what that code does:
rohanbari#genesis:~/stack$ ./a.out
SomethingToCareAbout
SomethingToCare2
Necessary

The program returns some weird and incomplete string

I am trying to write a function that delete whitespaces from a string but the output is not reasonable at all. I need help fam!
Code:
char* deleteSpace(char *String, int n) {
int i;
char* withoutSpaces;
withoutSpaces = calloc(n, sizeof (char));
for (i = 0; i < n - 1; i++) {
if (String[i] == ' ')
withoutSpaces[i] = String[++i];
else
withoutSpaces[i] = String[i];
}
return withoutSpaces;
}
You need to have to indices a "read" index for the source string and a "write" index for the destination.
Also, for better debugability and readability, put the index increment, ++i on a separate line. Oh - it looks like you are incrementing i twice. Once implicitly by the loop and again with the ++i.
Also unclear if n represents the length of the string with or without the null terminator. So let's just let the function deal with figuring that out via strlen.
Don't forget to null terminate the output string.
Several other bugs as well. Here's a version that's improved:
char* deleteSpace(const char *String) {
int j = 0;
const char* ptr = String;
size_t n = 0;
size_t spaces = 0;
char* withoutSpaces = NULL;
// count how many characters we expect to copy over
while (*ptr) {
n += (*ptr == ' ') ? 0 : 1;
ptr++;
}
withoutSpaces = (char*)malloc(n+1); // +1 for null char
for (i = 0; i < n; i++) {
if (String[i] != ' ') {
withoutSpaces[j] = String[i];
j++;
}
}
withoutSpaces[j] = '\0';
return withoutSpaces;
}
Also, if you just want to compact the string in place without allocating a new string.
void deleteSpace(char *String) {
char* ptrWrite = String;
while (*String) {
if (*String != ' ') {
*ptrWrite = *String;
ptrWrite++;
}
String++;
}
*ptrWrite = '\0';
}

concatenate and add an array of pointers into one index of another array of pointers

Is it possible to concatenate and add an array of pointers into one index of another array of pointers. I'm trying to take the strings inside my *token pointer and make it one string inside the first index of my commands pointer array, so on and so forth
cmd = strtok(str, " ");
while(n < 5 && (act_token = strtok(NULL, " ")))
{
token[n] = act_token;
n++;
}
token[n] = NULL;
/* Below is where I'm trying to add all the elements of the token array into one index of the comands array */
while( z < len ){
comands[b] = token[z];
z++;
}
b++;
}
To avoid O(n*n) complexity caused by looping a concatenation, in #zzxyz otherwise good answer, consider copying to the end of the accumulated destination.
char *concat_alloc(const char *token[], size_t n) {
size_t sum = 1;
for (size_t i = 0; i < n; i++) {
size_t len = strlen(token[i]);
sum += len;
if (sum < len) {
return NULL; // Too long
}
}
char *dest = malloc(sum);
if (dest) {
char *p = dest;
for (size_t i = 0; i < n; i++) {
size_t len = strlen(token[i]);
memcpy(p, token[i], len);
p += len; // advance to the end
}
*p = '\0';
}
return dest;
}
I feel your pain. String handling is very bad in C, and almost as bad in C++.
However, once you write the function, all you have to do is call it...
char *GetStringFromStringArray(const char**sourceStrings, size_t nCount)
{
char *destString = NULL;
size_t destLength = 1; //start with room for null-terminator
if (nCount == 0)
return destString;
for (size_t i = 0; i < nCount; i++)
destLength += strlen(sourceStrings[i]);
destString = (char*)malloc(destLength);
strcpy(destString, sourceStrings[0]);
for (size_t i = 1; i < nCount; i++)
strcat(destString, sourceStrings[i]);
return destString;
}
int main()
{
char *tokens[10] = { "bob", "jim", "hank" };
char *destStrings[2];
destStrings[0] = GetStringFromStringArray((const char**)tokens, 2);
destStrings[1] = GetStringFromStringArray((const char**)&tokens[1], 2);
free(destStrings[0]);
free(destStrings[1]);
}
The way I initialized tokens is not ok, by the way. Purely for easy example.

Print out the longest substring in c

Suppose that we have a string "11222222345646". So how to print out subsequence 222222 in C.
I have a function here, but I think something incorrect. Can someone correct it for me?
int *longestsubstring(int a[], int n, int *length)
{
int location = 0;
length = 0;
int i, j;
for (i = 0, j = 0; i <= n-1, j < i; i++, j++)
{
if (a[i] != a[j])
{
if (i - j >= *length)
{
*length = i - j;
location = j;
}
j = i;
}
}
return &a[location];
}
Sorry,I don't really understand your question.
I just have a little code,and it can print the longest sub string,hope it can help.
/*breif : print the longest sub string*/
void printLongestSubString(const char * str,int length)
{
if(length <= 0)
return;
int i ;
int num1 = 0,num2 = 0;
int location = 0;
for(i = 0; i< length - 1; ++i)
{
if(str[i] == str[i+1])
++num2;//count the sub string ,may be not the longest,but we should try.
else
{
if(num2 >num1)//I use num1 store the sum longest of current sub string.
{ num1 = num2;location = i - num2;}
else
;//do nothing for short sub string.
num2 = 0;
}
}
for(i = location;str[i]== str[num1];++i)
printf("%c",str[i]);
printf("\n");
}
int main()
{
char * str = "1122222234566";
printLongestSubString(str,13);
return 0;
}
From your code it appears you want to return the longest sub-sequence (sub-string). Since I'm relearning C I thought I would give it a shot.
I've used strndup to extract the substring. I'm not sure how portable it is but I found an implementation if needed, just click on the link. It will allocate memory to store the new cstring so you have to remember to free the memory once finished with the substring. Following your argument list, the length of the sub-string is returned as the third argument of the extraction routine.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *extract_longest_subsequence(const char *str, size_t str_len, size_t *longest_len);
int main()
{
char str[] = "11222234555555564666666";
size_t substr_len = 0;
char *substr = extract_longest_subsequence(str, sizeof(str), &substr_len);
if (!substr)
{
printf("Error: NULL sub-string returned\n");
return 1;
}
printf("original string: %s, length: %zu\n", str, sizeof(str)-1);
printf("Longest sub-string: %s, length: %zu\n", substr, substr_len);
/* Have to remember to free the memory allocated by strndup */
free(substr);
return 0;
}
char *extract_longest_subsequence(const char *str, size_t str_len, size_t *longest_len)
{
if (str == NULL || str_len < 1 || longest_len == NULL)
return NULL;
size_t longest_start = 0;
*longest_len = 0;
size_t curr_len = 1;
size_t i = 0;
for (i = 1; i < str_len; ++i)
{
if (str[i-1] == str[i])
{
++curr_len;
}
else
{
if (curr_len > *longest_len)
{
longest_start = i - curr_len;
*longest_len = curr_len;
}
curr_len = 1;
}
}
/* strndup allocates memory for storing the substring */
return strndup(str + longest_start, *longest_len);
}
It looks like in your loop that j is supposed to be storing where the current "substring" starts, and i is the index of the character that you are currently looking at. In that case, you want to change
for (i = 0, j = 0; i <= n-1, j < i; i++, j++)
to
for (i = 0, j = 0; i <= n-1; i++)
That way, you are using i to store which character you're looking at, and the j = i line will "reset" which string of characters you are checking the length of.
Also, a few other things:
1) length = 0 should be *length = 0. You probably don't actually want to set the pointer to point to address 0x0.
2) That last line would return where your "largest substring" starts, but it doesn't truncate where the characters start to change (i.e. the resulting string isn't necessarily *length long). It can be intentional depending on use case, but figured I'd mention it in case it saves some grief.

search array for string

How would i implement this?Im trying to compare a array of c strings to a single string and if there is no match append it to the 2d array.
char*mprt,uni[100][16];
mprt = &uni[0][0];
for (int s = 0;s <= 99;s++)
{
for (int z = 0;z <= 15;z++)
{
if (strcmp(mprt++, string1) != 0)
{
uni[s][z] = string1[z];
}
}
}
Ok ... from your comments I now get what you're trying to do. You'd want to make this into a function so you could feed words to it, but it should get you pointed in the right direction.
Note that you can use char[][], but this way your strings can be of any length because we dynamically allocate them when we put them in the list.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
/* space for 100 strings */
char **uni = calloc(100, sizeof(char*));
char **i;
/* Put one word in the list for test */
*uni = calloc(5, sizeof(char*));
strncpy(*uni, "this", 5);
/* here's the string we're going to search for */
char * str2 = "that";
/* go through the first dimension looking for the string
note we have to check that we don't exceed our list size */
for (i = uni; *i != NULL && i < uni+100; i++)
{
/* if we find it, break */
if (strcmp(*i,str2) == 0)
break;
}
/* if we didn't find the string, *i will be null
* or we will have hit the end of our first dimension */
if (i == uni + 100)
{
printf("No more space!\n");
}
else if (*i == NULL)
{
/* allocate space for our string */
*i = calloc(strlen(str2) + 1, sizeof(char));
/* copy our new string into the list */
strncpy(*i, str2, strlen(str2) + 1);
}
/* output to confirm it worked */
for (i = uni; *i != NULL && i < uni+100; i++)
printf("%s\n",*i);
}
For completeness, the char[][] version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char uni[100][16];
int i,j;
/* init our arrays */
for (i=0;i<100;i++)
for (j=0;j<16;j++)
uni[i][j] = '\0';
/* Put one word in the list for test */
strncpy(uni[0], "this",15);
/* here's the string we're going to search for */
char * str2 = "that";
/* go through the first dimension looking for the string */
for (i = 0; uni[i][0] != '\0' && i < 100; i++)
{
/* if we find it, break */
if (strcmp(uni[i],str2) == 0)
break;
}
/* if we didn't find the string, uni[i][0] will be '\0'
* or we will have hit the end of our first dimension */
if (i == 100)
{
printf("No more space!\n");
}
else if (uni[i][0] == '\0')
{
/* copy our new string into the array */
strncpy(uni[i], str2, 15);
}
/* output to confirm it worked */
for (i = 0; uni[i][0] != '\0' && i < 100; i++)
printf("%s\n",uni[i]);
}
Edit to explain C pointers and arrays from comments below:
In C, arrays degrade to pointers. This is actually really confusing when you first start.
If I have char myArray[10] and I want to pass that to a function that takes a char * argument, I can use either &myArray[0] or just myArray. When you leave off the index, it degrades to a pointer to the first element in the array.
In a multidimensional array like yours, &uni[5][0] == uni[5] - both are pointers to the first element in the second dimension at index 5 in the first. It degrades to char* pointed at the beginning of the 6th word in your list.
In your for loop, you need to copy the whole string to append it,
Replace the line by this,
strcpy(uni[s], string1[z]);
Considering string1[z] is an element of an array of char pointers.
Edit:
Not sure if this is what you're trying to do, but you'll end up with all elements set to string1
char string1[] = "String";
char uni[100][16] = {};
for (int s = 0; s < 100; s++)
{
if (strcmp(uni[s], string1) != 0)
{
strcpy(uni[s], string1);
}
}
Or this, without strcpy()
char string1[] = "String";
char uni[100][16] = {};
for (int s = 0; s < 100; s++)
{
for (int r = 0; r < sizeof(string1); r++)
{
uni[s][r] = string1[r];
}
}
to append to the end of the 2D array you need to use dynamic memory allocation
const int row_max = 100, col_max = 16;
char** uni = NULL;
char searchString[col_max] = "xyz";
int currentLength = 0;
uni = (char**) malloc (row_max * sizeof(char*)); //TODO:error handling code to be added
for (int row = 0; row < row_max; row++)
{
uni[row] = (char*)malloc(col_max * sizeof(char));//TODO:error handling code to be added
currentLength = row;
}
for (int row = 0; row < row_max; row++) //fill array uni with data here
{
uni[row] = "abc";
}
for (int row = 0; row < row_max; row++)
{
for (int col = 0; col < col_max; col++)
{
if (strcmp(&uni[row][col], searchString) != 0 )
{//string not found
uni = (char**)realloc(uni, (currentLength + 1) * sizeof(char*));//TODO:error handling code to be added
uni[currentLength + 1] = (char*)malloc(col_max);//TODO:error handling code to be added
currentLength++;
strcpy(uni[currentLength],searchString); //append at end of 2D array
goto stop;
}
}
}
stop:
for (int row = 0; row <= currentLength; row++)
free(uni[row]);
free(uni);
return 0;

Resources