Using strtox for word generator , - c

I'm trying to create word generator in C and found Segmentation Fault message.
gdb output :
_GI___strtok_r (
s=0x562d88201188 "some text without comma",
delim=0x562d8820117f " ", save_ptr=0x7f570a47aa68 <olds>) at strtok_r.c:72
code with strtox function :
char **words = malloc(sizeof(char *) * NUM_WORDS);
int num_words = 0;
char *save_ptr;
char *word = strtok(text, " ");
while (word != NULL) {
// Strip leading and trailing whitespace
while (isspace(*word)) {
word++;
}
int len = strlen(word);
while (len > 0 && isspace(word[len - 1])) {
len--;
}
// Allocate memory for the word and copy it using strdup()
words[num_words] = strdup(word);
// Move to the next word
num_words++;
word = strtok(NULL, " ");
}
how to use function with an indeterminate number of words in text?

Can't believe someone finally asked for this!
You may want to add verification that realloc() hasn't returned a NULL.
In brief, the string is chopped on the delimiters provided to strtok() while realloc() is used to grow an array of pointers to each of those segments.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char buf[] = "Once upon a time there lived a beautiful princess.", *p = buf;
char **t = NULL; size_t sz = sizeof *t;
int n = 0;
while(!!(t=realloc(t,(n+1)*sz))&&!!(t[n]=strtok(p," .\n"))) p=NULL, n++;
for( int i = 0; i < n; i++ )
puts( t[i] );
free( t );
return 0;
}
Once
upon
a
time
there
lived
a
beautiful
princess
EDIT
Then there is the extension that can handle multiple input lines:
int main() {
char *buf[] = { "Once upon a time\n", "there lived\n", " a beautiful princess.\n" };
char **t = NULL; size_t sz = sizeof *t;
int n = 0;
for( int ln = 0; ln < sizeof buf/sizeof buf[0]; ln++ ) {
char *p = buf[ln];
while(!!(t=realloc(t,(n+1)*sz))&&!!(t[n]=strtok(p," .\n"))) p=NULL, n++;
}
for( int i = 0; i < n; i++ )
puts( t[i] );
free( t );
return 0;
}
/* Output same as shown above */
Put the strtok() as the parameter to strdup() and you've got yourself something that will preserve words while using a single line input buffer.

Related

how to determine last words of each string?

I have this assignment:
Enter a sequence of sentences from the keyboard into the string array (the end of entering - empty string). Determine the last word of each of these sentences.
The problem is that my program outputs the last word of the last sentence, and I need the last word of each sentence to be output.
Program I have tried:
#include <stdio.h>
#include <string.h>
int main() {
char str[10][100];
int i;
printf("Enter a sequence of sentences:\n");
for (i = 0; i < 10; i++) {
if (*gets(str) == '\0')
break;
}
printf("The last word of each of these sentences is:\n");
for (i = 0; i < 10; i++) {
char *word;
word = strtok(str[i], ".");
while (word != NULL) {
char *last_word = word;
word = strtok(NULL, ".");
}
printf("%s\n", last_word);
}
return 0;
}
The delimiter in this call
word = strtok(str[i], ".");
does not make sense.
It seems you mean
word = strtok(str[i], " \t.");
provided that a sentence can be ended only with a dot and words are separated by spaces or tab characters.
Another problem is that the variable last_word must be declared before the while loop.
For example
char *last_word = NULL;
char *word;
word = strtok(str[i], " \t.");
while (word != NULL) {
last_word = word;
word = strtok(NULL, " \t.");
}
And it is better to use for loop instead of the while loop
char *last_word = NULL;
for ( char *word = strtok(str[i], " \t." );
word != NULL;
word = strtok(NULL, " \t.") )
{
last_word = word;
}
Pay attention to that the function gets is unsafe and is not supported by the C Standard. Instead use standard C function fgets.
And the condition in the second for loop
for(i=0; i<10; i++)
{
char *word;
//...
is incorrect because the user can enter less than 10 sentences.
Without repeating the commentary of the accepted answer provided by #Vlad (kudos!), here is an alternative offering (with comments)
#include <stdio.h>
#include <string.h>
int main( void ) {
// A single large buffer allowing very long lines to be entered.
char buf[ 10 * 100 ], *p = buf;
size_t left = sizeof buf;
int i = 0;
// up to 10 'lines' of input, breaking on an empty line, too
while( i++ < 10 && fgets( p, left, stdin ) && p[0] != '\n' ) {
// typical invocation of strtok() to isolate "words"
// and a "wasteful" copy of each word to the current start of the buffer
for( char *tkn = p; ( tkn = strtok( tkn, " .\n" ) ) != NULL; tkn = NULL )
strcpy( p, tkn );
// having copied the last "word", append '\n' and advance the pointer
size_t len = strlen( p );
p += len;
strcpy( p++, "\n" );
left -= len + 1; // eroding the available size of the buffer
}
printf( "%s", buf ); // a single output of "word1\nword2\nword3\n..."
return 0;
}
NB: strcpy() of overlapping buffers is fraught with hazards. This works in this case, but the practise and its effects must be very well considered before using this technique.

Why is my function to reverse string in c not working?

I am writing some C Code where the user enters the desired string size and then a string which will be reversed then printed (as opposed to being printed in reverse.) I also would like to mention that I don't want to use external libraries, the whole point of this is to be able to do it manually. I used dynamic memory allocation to create a string of a size inputted by the user and called a "Reverse Array" function. Everything works fine until the function is called. My method for reversing the string followed the same principle as reversing a normal array but instead of moving integers around I moved characters around. Can you explain to me what I have done wrong?
My Code:
#include <stdio.h>
#include <stdlib.h>
int RvsArr(char *Str, int end)
{
int start = 0;
char tmp;
while (start < end)
{
tmp = Str[start];
Str[start] = Str[end];
Str[end] = tmp;
start++;
end--;
}
printf("%s", Str);
return 0;
}
int main()
{
int ArrSz;
printf("Please enter array size: ");
scanf("%i", &ArrSz);
char *Str;
Str = (char *)malloc(ArrSz * sizeof(char));
printf("Please enter your string: ");
scanf("%s", Str);
RvsArr(Str, ArrSz);
free(Str);
return 0;
}
You need to reverse the actual string, not the full buffer.
char *RvsArr(char* Str)
{
char *end, *wrk = Str;
char tmp;
if(wrk && *wrk)
{
end = Str + strlen(wrk) - 1;
while(wrk < end)
{
tmp = *wrk;
*wrk++ = *end;
*end-- = tmp;
}
}
return Str;
}
int main()
{
int ArrSz;
printf("Please enter array size: ");
scanf(" %i", &ArrSz);
char* Str;
Str = malloc(ArrSz * sizeof(char));
printf("Please enter your string: ");
scanf(" %s", Str);
printf("\n`%s`\n", RvsArr(Str));
free(Str);
return 0;
}
https://godbolt.org/z/azob5s
For starters the user can enter a string the size of which can be less than the size of the dynamically allocated character array that stores the string.
So passing the size of the array does not make a sense. The size of the array is not the same as the size of the entered string.
Also this expression Str[end] access memory beyond the allocated array in the first iteration of the while loop.
And the return type int also does not make a sense.
Apart from this the function should not output anything. It is the caller of the function that will decide to output the result string or not.
Pay attention to that this call
scanf("%s", Str);
is unsafe. It would be better to use the function fgets. For example
fgets( Str, ArrSz, stdin );
In this case you will need to remove the new line character '\n' that the function can append to the entered string.
Without using standard string functions the function can be defined the following way as it is shown in the demonstrative program below.
Instead of the senseless return type int the function returns a pointer to the first character of the reversed string.
#include <stdio.h>
char * RvsArr( char *s )
{
char *last = s;
while ( *last ) ++last;
if ( last != s )
{
for ( char *first = s; first < --last; ++first )
{
char c = *first;
*first = *last;
*last = c;
}
}
return s;
}
int main(void)
{
char s[] = "Hello World!";
puts( s );
puts( RvsArr( s ) );
return 0;
}
The program output is
Hello World!
!dlroW olleH
If you are allowed to use standard string functions then the function RvsArr can look the following way (provided that the header <string.h> is included)
char * RvsArr( char *s )
{
char *last = s + strlen( s );
if ( last != s )
{
for ( char *first = s; first < --last; ++first )
{
char c = *first;
*first = *last;
*last = c;
}
}
return s;
}
Character arrays or string in c(as it is generally referred to) requires one extra byte which store null character ('\o' or 0) to indicate the end of string. You can store ArrSz - 1 character in your array and ArrSz byte stores the termination character('\o' or 0).
int RvsArr(char* Str, int end)
{
if (Str == 0 || end <= 1)
return 0;
int start = 0;
char tmp;
while(start < end)
{
tmp = Str[start];
Str[start] = Str[--end]; // pre decrement the counter to last char
Str[end] = tmp;
start++;
}
printf("%s", Str);
return 0;
}
or in other version
int RvsArr(char* Str, int end)
{
if (Str == 0 || end <= 1)
return 0;
int start = 0;
int last = end - 1;
char tmp;
while(start < last)
{
tmp = Str[start];
Str[start] = Str[last];
Str[last] = tmp;
start++;
last--;
}
printf("%s", Str);
return 0;
}
And some changes in main function are
int main()
{
int ArrSz;
printf("Please enter array size: ");
scanf("%i", &ArrSz);
char *Str;
Str = (char *)malloc(ArrSz * sizeof(char));
printf("Please enter your string: ");
scanf("%s", Str);
Str[ArrSz] = '\0'; // Here we have no control on how many characters are read, scan is a security vulnerability becuse of this
printf("Input=%s, len=%d\n", Str, strlen(Str));
RvsArr(Str, strlen(Str));
free(Str);
return 0;
}

Why are these words not appending in C?

I'm trying to write a function in which two words are appended into a third string, and this function must use malloc(). I'm first writing it in the main before I put it in a function. I have:
int main(void){
char *word = "Johnny";
char *word2 = " Boy";
char *buff = malloc(100 * sizeof(char));
printf("%d\n", sizeof(buff));
int ct;
for(ct = 0; ct < strlen(word); ct++){
buff = word;
}
printf("%s\n", buff);
printf("the counter is now %d\n", ct);
buff += ct;
for(ct; ct < 13; ct++){
buff = word2;
}
printf("%s\n", buff);
}
I want buff to say "Johnny Boy" but at the end of it, "Johnny" is overwritten, and it just says " Boy"
Listen, while we want to help, SO is not really meant to be a classroom environment. Plainly, you're struggling with a fundamental lack of understanding about pointers / string manipulation in C, which is very basic material. Get a BETTER book on C and compare this code to your work, and study on it until you understand the differences and exactly what's being done at each step.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
char word[] = "Johnny";
char word2[] = " Boy";
char *temp;
char *buff = malloc(32 * sizeof(char));
if (buff == NULL) {
puts("allocation error");
return 1;
}
for (int ct = 0; ct <= strlen(word); ct++) {
buff[ct] = word[ct];
}
printf("%s\n", buff);
temp = buff + strlen(word);
for (int ct = 0; ct <= strlen(word2); ct++) {
temp[ct] = word2[ct];
}
printf("%s\n", buff);
free(buff);
return 0;
}
Okay. The first problem here is you must understand that strings are arrays. You cannot in c say that an array is another array. To be honest there are a lot of problems with this code. The guy above me's code is probably gonna be pretty complicated for you to understand so I will try to give you some more understandable code. One more thing, I won't be using pointers because I haven't mastered them yet.
#define BUF 255
int main( void)
{
char word1[BUF], word2[BUF], word_full[BUF];
int ct, length;
printf( "Input your first word\n" );
scanf( " %s", &word1);
printf( "Input your second word." );
scanf( " %s", &word2);
length = strlen( word1 );
for ( ct = 0; ct < length; ct++ )
{
word_full[ct] = word1[ct];
word_full[ ct + length ] = word2[ct];
}
word_full[BUF] = 0;
printf( word_full );
return 0;
}
return 0;
}

garbage output after whitespace removing

i've this code:
int i =0;
char * str = "ar bitrary whitespace";
int whitespace=0,index;
for(index = 0;index < strlen(str);index++)
{
if(isspace(str[index]) != 0)
{
whitespace++;
}
}
char * tmp = (char *)calloc(strlen(str)-whitespace +1,sizeof(char));
memset(tmp,'\0',strlen(tmp)+1);
while(i < strlen(str))
{
if(isspace(str[i]) != 0)
{
i++;
continue;
}else if(isspace(str[i]) == 0)
{
strcat(tmp,&str[i]);
i++;
}
}
printf("\nnew string is: %s \n",tmp);
the problem is that the output is a string without the whitespace removed + some garbage character.
I've used memset to null terminate tmp,is there the problem?
The length of the source string could be calculated before this loop
for(index = 0;index < strlen(str);index++)
Otherwise if the code will not be optimized the function strlen will be called for each iteration of the loop. In fact using of the function is redundant for such a task.
This statement
memset(tmp,'\0',strlen(tmp)+1);
does not make sense because the call of calloc already initialized the memory with zeroes.
This statement
strcat(tmp,&str[i]);
also copies blanks from the source string after the position i. So it can write beyond the memory allocated for the array pointed to by the pointer tmp.
You can write a separate function that can look as it is shown in this demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
char * remove_blanks( const char *s )
{
size_t n = 0;
const char *p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) ++n;
} while ( *p++ );
char *t = malloc( n );
if ( t )
{
char *q = t;
p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) *q++ = *p;
} while ( *p++ );
}
return t;
}
int main(void)
{
char * str = "ar bitrary whitespace";
printf( "\"%s\"\n", str );
char *t = remove_blanks( str );
printf( "\"%s\"\n", t );
free( t );
}
The program output is
"ar bitrary whitespace"
"arbitrarywhitespace"
this is your problem
memset(tmp,'\0',strlen(tmp)+1);
strlen(tmp) works by looking for '\0' in tmp, you have a chicken and egg situation here.
You should not be doing a memset any way, just tack on a '\0' when you fnish copying
And dont use strcat, instead maintain a pointer to tmp and just do *p = str[i] then increment p
I will not read your question, you overwrite the '\0' terminator for sure.
Now that I read your question, it looks like you need to understand strings and arrays better,
Don't ever write while (i < strlen(str))
Don't use strcat() for adding a single character, you apparently did overwrite the '\0' there. Furthermore, don't ever use strcat() for concatenating more than to pieces of a string.
Also notable,
You memset() after calloc() which already initialized to 0. That means that you are enforcing something that is not necessary, and trying it twice as if it failed the first time which I can guarantee it didn't.
In fact, since you have used calloc() and all bytes pointed to by tmp are 0 then strlen(tmp) will return 0, thus your memset() is equivalent to
tmp[0] = '\0';
and you REALLY don't need initialize tmp except when you finally copy the actual bytes from str.
I always advice against calloc() for strings, because
You don't really need to initialize something twice.
You should be sure your code does take the terminating '\0' into account and not simply assume that it's there because you calloc()ed. That is a bug that you just hide with calloc() but it shows up at some point.
Try this and see if you can understand the reasons for my changes
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(void)
{
int whitespace;
int length;
char *str = "ar bitrary whitespace";
char *tmp;
whitespace = 0;
for (length = 0; str[length] != '\0'; ++length) {
if (isspace(str[length]) != 0) {
whitespace++;
}
}
tmp = malloc(length - whitespace + 1);
if (tmp == NULL)
return -1;
for (int i = 0, j = 0; str[i] != '\0'; ++i) {
if (isspace(str[i]) != 0)
continue;
tmp[j++] = str[i];
}
tmp[length - whitespace] = '\0';
printf("new string is: %s\n",tmp);
free(tmp);
return 0;
}

C: What's wrong with my reverse string function?

Been solving leetcode puzzles and thought I solved this one pretty quickly, yet I am running into a strange error. My output is matching the expected output, so I have no idea why it's rejecting my solution based off the following test case.
char* reverseString(char* s)
{
/* Sample input: "Hello"
Sample output: "olleh"
*/
char * reversed_string;
char temp[1];
int length = 0;
int i;
if(s == NULL)
return NULL;
length = strlen(s);
/* While string is not null, increment pointer */
while(*s != NULL)
{
s = s + 1;
}
/* Allocate reversed string based off length of original string */
reversed_string = malloc(length + 1);
/* Traverse backwards for length of string */
/* Copy each letter to temp */
/* Concatenate each letter to reversed_string */
for(i = 0; i < length; i++)
{
s = s - 1;
strncpy(temp, s, 1);
strcat(reversed_string, temp);
}
reversed_string[length] = '\0';
/* Return reversed string */
return reversed_string;
}
MOutput = My Output
EOutput = Expected Output
Input: "?CZU.9Iw8G3K?fse,b7 m;0?f :`c9d!D'`Pem0'Du0;9i` 03F,: 7,oPw'T'5`1g!iwR5J71iJ\"f;r6L;qZaDGx?cvkS 8\"UY2u`YC P3CM y`4v 1q7P;Zd1.;:RA!oYh;!2W8xMfMx8W2!;hYo!AR:;.1dZ;P7q1 v4`y MC3P CY`u2YU\"8 Skvc?xGDaZq;L6r;f\"Ji17J5Rwi!g1`5'T'wPo,7 :,F30 `i9;0uD'0meP`'D!d9c`: f?0;Z 7b,esf?K3G8wI9.UmC?"
MOutput: "?CmU.9Iw8G3K?fse,b7 Z;0?f :`c9d!D'`Pem0'Du0;9i` 03F,: 7,oPw'T'5`1g!iwR5J71iJ"f;r6L;qZaDGx?cvkS 8"UY2u`YC P3CM y`4v 1q7P;Zd1.;:RA!oYh;!2W8xMfMx8W2!;hYo!AR:;.1dZ;P7q1 v4`y MC3P CY`u2YU"8 Skvc?xGDaZq;L6r;f"Ji17J5Rwi!g1`5'T'wPo,7 :,F30 `i9;0uD'0meP`'D!d9c`: f?0;m 7b,esf?K3G8wI9.UZC?"
EOutput: "?CmU.9Iw8G3K?fse,b7 Z;0?f :`c9d!D'`Pem0'Du0;9i` 03F,: 7,oPw'T'5`1g!iwR5J71iJ"f;r6L;qZaDGx?cvkS 8"UY2u`YC P3CM y`4v 1q7P;Zd1.;:RA!oYh;!2W8xMfMx8W2!;hYo!AR:;.1dZ;P7q1 v4`y MC3P CY`u2YU"8 Skvc?xGDaZq;L6r;f"Ji17J5Rwi!g1`5'T'wPo,7 :,F30 `i9;0uD'0meP`'D!d9c`: f?0;m 7b,esf?K3G8wI9.UZC?"
Anyone spot what might be wrong with my function? Is there undefined behavior anywhere?
You need to allocate 2 chars for temp and initialize with 0's, so change it to
char temp[2] = { 0 };
Also, initialize reversed_string after allocating memory to it, so that first strcat works properly.
reversed_string = malloc(length + 1);
reversed_string[0] = '\0';
this part of the posted code:
for(i = 0; i < length; i++)
{
s = s - 1;
strncpy(temp, s, 1);
strcat(reversed_string, temp);
}
is not doing the right thing.
It should be copying one byte at a time from the end of the original string to the beginning of the reversed string.
Suggest the following code which:
cleanly compiles
performs the desired functionality
is a complete program
because it is going to be used in one of the online coding contests does not perform any error checking.
and now, the code
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// use meaningful variable and parameter names
char* reverseString(char* original)
{
size_t length = strlen( original );
/* Allocate reversed string based off length of original string */
char *reversed = NULL;
reversed = malloc(length + 1);
char *dest = reversed;
char *source = &(original[ strlen(original) -1 ]);
for( ; source >= original; source--)
{
*dest = *source;
dest++;
}
*dest = '\0';
/* Return reversed string */
return reversed;
} // end function: reverseString
int main( void )
{
char inBuffer[ 4096 ];
fgets( inBuffer, sizeof(inBuffer), stdin );
// eliminate trailing newline if it exists
char * newline = NULL;
if( NULL != (newline = strstr( inBuffer, "\n" ) ) )
{
*newline = '\0';
}
char * newString = reverseString( inBuffer );
printf( "%s\n\n", newString );
} // end function: main

Resources