This question already has an answer here:
How to use sscanf in loops?
(1 answer)
Closed 4 years ago.
I used sscanf to segment one string taken from the input and store every token in a structure. The problem is that sscanf only reads the first word of the string and doesn't move ahead to the next word, printing the same token over and over. Here's the code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define dim 30
typedef struct string {
char* token[dim];
}string;
int main() {
string* New = (string *)malloc(dim*sizeof(string));
char* s;
char buffer[dim];
int i = 0, r = 0, j = 0;
s = (char*)malloc(sizeof(char*));
printf("\nString to read:\n");
fgets(s, dim, stdin);
printf("\nThe string is: %s", s);
while(sscanf(s, " %s ", buffer) != EOF) {
New->token[i] = malloc(dim*sizeof(char));
strcpy(New->token[i], buffer);
printf("\nAdded: %s", New->token[i]);
++i;
}
}
For example, if i give "this is a string" as an input, sscanf will only get the word "this" multiple times without moving on to the next word.
You need to increment the pointer of the source sscanf() reads from, so that it won't read from the same point, again and again.
Furthermore, the memory dynamically allocated for s by you didn't make any sense. It was too less in any case. By the call to fgets() later in the code I can see you meant to say s = malloc(dim * sizeof(char));, so I went ahead and fixed that.
Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define dim 30
typedef struct string {
char* token[dim];
} string;
int main() {
string* New = malloc(dim*sizeof(string));
char* s;
char buffer[dim];
int i = 0;
s = malloc(dim * sizeof(char));
fgets(s, dim, stdin);
printf("The string is: %s\n", s);
char* ptr = s;
int offset;
while (sscanf(ptr, "%s%n", buffer, &offset) == 1) {
ptr += offset;
New->token[i] = malloc(strlen(buffer) + 1);
strcpy(New->token[i], buffer);
printf("Added: %s\n", New->token[i]);
++i;
}
// do work
for(int j = 0; j < i; ++j)
free(New->token[i]);
free(New);
free(s);
return 0;
}
Output:
The string is: this is a string
Added: this
Added: is
Added: a
Added: string
PS: I am not sure about the schema of structures you have in mind, maybe you need to spend a moment or two, thinking about that twice; I mean whether your design approach is meaningful or not.
PPS: Unrelated to your problem: Do I cast the result of malloc? No!
Edit: As #chux said, " " in " %s%n" of sscanf() serves no purpose. I changed it to "%s%n".
Moreover, in order to reserve exactly as much memory as needed (which is the thing to do, when dealing with dynamic memory allocation), New->token[i] = malloc(dim*sizeof(char)); was changed to New->token[i] = malloc(strlen(buffer) + 1);.
Related
hey guys i have looked around for a solution and tried everything i can think of im new to pointers and dynamic strings and i could really do with some help with problem. im currently learning c and i need to get the user to input a dynamic size for the string length . i need to make it so the users input can not be bigger then 100 . here's where i am at currently . i have the code booting but if i try set the size to let's say 5 i can still input way more chars into the string. cheers really appreciate any help .
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main () {
int maxSize = 100;
char *name = (char*) malloc (maxSize * sizeof (char));
int n;
char text1[]= ("input string size\n");
printf ("your string lenght is %d\n", strlen(name));
//getting size
n=intPrintScanner(text1);
printf ("your size is %d\n",n);
name = realloc (name, sizeof (char) * n);
//printing results
printf ("your string lenght is %d\n",strlen (name));
scanf("%s", name);
printf("your string is %s",name);
free(name);
fflush(stdin);
printf("press any key to close");
getchar();
return (0);
}
Bugs:
You never assign any data to name so it just contains garbage. You can't use strlen on it before you have stored valid data there either. You can store a string inside name by for example using strcpy.
When using realloc, there's no guarantee that the old pointer is the same as the returned pointer. Also, you need error handling. Implement it like this instead:
char* tmp = realloc (name, n);
if(tmp == NULL)
{
/* actual error handling here in case realloc fails */ }
}
name = tmp; // assign pointer to the new area only if allocation succeeded
fflush(stdin); is not well-defined, never use fflush on input streams. Instead you should discard unwanted line feed characters from stdin - which could be as trivial as an extra getchar() call just after reading something. Check out How to read / parse input in C? The FAQ for lots of general good advise regarding how to take input from stdin.
Cosmetic/style:
No need for parenthesis here: char text1[]= ("input string size\n");. All it achieves it to make the code look strange.
The correct form of main is int main (void). The int main() is obsolete style.
There is no need to wrap the expression passed to return in a parenthesis.
There is never a need to multiply something with sizeof (char), since sizeof (char) is by definition always 1 no matter system.
There is no need to cast the result of malloc.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
char* read_until(int fd, char end) {
int i = 0, size;
char c = '\0';
char* string = (char*)malloc(sizeof(char));
while (1) {
size = read(fd, &c, sizeof(char));
if (c != end && size > 0) {
string = (char*)realloc(string, sizeof(char) * (i + 2));
string[i++] = c;
} else {
break;
}
}
string[i] = '\0';
return string;
}
int main()
{
char *name;
int correct=0;
do{
write(1,"Put a name: ",strlen("Put a name: "));
name = read_until(STDIN_FILENO,'\n');
if(strlen(name) > 99){
write(1,"Error\n",strlen("Error\n"));
}else{
correct=1;
}
}while(correct != 1);
write(1,name,strlen(name));
free(name);
}
Try using write and read instead of printf and scanf, it is better for allocating dynamic memory, read and try to understand the read_until function, there are better ways to do main.
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int n_of_words = 0;
#define MAX_STR_SZ 256
// asking for user input
char string[50];
printf("\nPlease input a string of text.\n\n");
fgets(string, MAX_STR_SZ, stdin);
char * words[n_of_words];
// extracting the first word
words[n_of_words] = strtok(string, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
// looping through the string to extract all other words
while( words[n_of_words] != NULL )
{
n_of_words ++;
words[n_of_words] = strtok(NULL, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
}
sleep(10);
return 0;
}
I'm very new to programming, but I was trying to write a function to extract words from a user inputted string and save them in an array for later use in the program. I added the 2 printf lines of code to see if it was working properly.
I always get a segmentation fault error after the second iteration of the while loop.
Also, somehow this problem didn't present itself when I compiled the same code on the CS50 ide (Cloud9), but it happens in any other case.
Few issues which can be resolved to prevent segmenatation fault:
No string.h header in the source code for strtokfunction
#include <stdio.h>
#include <unistd.h>
Macros are generally declared in the top of the source code and not inside any function
#define MAX_STR_SZ 256
The char string array is of length 50 but the fgets is allowing 256 and can lead to bufferoverflow.
char string[50];
printf("\nPlease input a string of text.\n\n");
fgets(string, MAX_STR_SZ, stdin);
The value of the variable n_of_words is 0. So, the declaration
char * words[n_of_words];
Will not create an array of the desired length.
The root cause of your question lies here:
while( words[n_of_words] != NULL )
{
n_of_words ++;
words[n_of_words] = strtok(NULL, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
}
You are accessing a memory location which was never declared,
n_of_words ++;
words[n_of_words] = strtok(NULL, " "); //words[1] or any index was never declared.
Every C program gets for free a list of the command line parameters, in general declared as int main(int argc, char* argv[]); or int main(int argc, char** argv);
This is precisely what you are trying to replicate with int n_of_words and char* words[n_of_words];
But you are doing it the wrong way.
A first note on this 3 lines from your code:
#define MAX_STR_SZ 256
char string[50];
fgets(string, MAX_STR_SZ, stdin);
You are setting 256 as the limit for fgets() to read, but you have only 50 chars in string. Many times it will work in this case, since you are reading from the keyboard and many of us would not key more than a few words in, but you have a problem. Change the limits.
strtok() is probably not the best one to choose here. A single loop using scanf() could read many lines and break all of then in words skipping over the newlines and such, and you may find it easier to code.
Anyway, back to your code: since you do not know in advance the number of words, you can estimate a limit or allocate memory for the strings one by one, or even in blocks. But
you need to allocate memory for the strings you will have a SegFault
at the moment you try to write in the words[] array.
I changed a minimum of your code so you can see an example, and I fixed the number of strings in a #define similar of what you have written so far.
A simple way to go is declare --- as C does in main() --- words[] as char** and allocate memory for them as soon as you know you have at least one string to record.
But then you need to note that you will have just the pointers. They are still pointing to nothing.
As soon as you have a string to load you need to allocate memory for it, plus 1 byte for the terminating '\0', and then copying the string and saving the address in the corresponding pointer in the words[] array.
See the code.
#define MAX_STR_SZ 256
#define MAX_N_OF_STRINGS 30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// https://stackoverflow.com/questions/63343800/
// c-program-segfaulting-with-strtok
int main(int argc, char** argv)
{
int n_of_words = 0;
int max_n_of_words = MAX_N_OF_STRINGS;
char** words;
// asking for user input
char string[MAX_STR_SZ];
printf("\nPlease input a string of text: ");
fgets(string, MAX_STR_SZ, stdin);
string[strlen(string) - 1] = 0; // drops the final '\n'
printf("full string was '%s'\n", string);
if (strlen(string) == 0) return -1; // no input
// we have at least one byte
// before anything build words[]
words = (char**)malloc(max_n_of_words * sizeof(char*));
// now words[] points to an array of pointers to char
// extracting the first word
char* a_word = strtok(string, " ");
// looping through the string to extract all other words
do
{
printf("\n%i %s\n", 1+n_of_words, a_word);
words[n_of_words] = malloc(1 + sizeof(a_word));
strcpy(words[n_of_words], a_word);
n_of_words++;
if (n_of_words >= MAX_N_OF_STRINGS) break;
a_word = strtok(NULL, " ");
} while (a_word != NULL);
printf("\n%d words at the end of the loop:\n\n", n_of_words);
for (int i = 0; i < n_of_words; i += 1)
{
printf("%i %s\n", 1 + n_of_words, words[i]);
free(words[i]); // deletes words[i]
}; // for()
free(words); // deletes the array
return 0;
};
As a result:
Please input a string of text: we have at least one byte
full string was 'we have at least one byte'
1 we
2 have
3 at
4 least
5 one
6 byte
6 words at the end of the loop:
1 we
2 have
3 at
4 least
5 one
6 byte
There are a few problems that could lead to a seg fault. First, I get warnings compiling your code:
../main.c: In function 'main':
../main.c:17:25: warning: implicit declaration of function 'strtok' [-Wimplicit-function-declaration]
words[n_of_words] = strtok(string, " ");
^~~~~~
../main.c:17:23: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
words[n_of_words] = strtok(string, " ");
^
../main.c:24:27: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
words[n_of_words] = strtok(NULL, " ");
All of this is because you didn't include the proper header for strtok, namely string.h. This could potentially cause problems because the default return type is assumed to be int, which may not be large enough to hold a pointer.
Second, you are passing an incorrect size to fgets(). The size should be the size of the buffer for holding the result. If the buffer is overflowed, undefined behavior results.
Finally, the words array is declared with a size n_of_words, which is zero at that point. This results in a zero size array. Arrays in C do not automatically grow.
Here is your code with these issues fixed:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
int n_of_words = 0;
#define MAX_STR_SZ 256
// asking for user input
char string[MAX_STR_SZ]; // <--- Use macro to define buffer size
printf("\nPlease input a string of text.\n\n");
fgets(string, sizeof string, stdin);
char * words[MAX_STR_SZ]; // <--- Should never be more words than characters in the buffer
// extracting the first word
words[n_of_words] = strtok(string, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
// looping through the string to extract all other words
while( words[n_of_words] != NULL )
{
n_of_words ++;
words[n_of_words] = strtok(NULL, " ");
printf("\n%i %s\n", n_of_words, words[n_of_words]);
}
sleep(10);
return 0;
}
Let's say I have the following string stored in char *m;
char *m = "K: someword\r\n";
The m will be inputed by the user so the user will write in the console:
K: someword\r\n
The someword can have different length, while K: \r\n will always be the same.
Now my question is, which is the best way after I read this input to extract someword from it and save it into a new char* variable?
Use sscanf() like this:
#include <stdio.h>
int main (void)
{
char buffer [50], k, return_car, new_line;
int n = sscanf ("K: someword\r\n", "%c: %s%c%c", &k, buffer, &return_car, &new_line);
printf ("The word is \"%s\". sscanf() read %d items.\n", buffer, n);
return 0;
}
Output:
The word is "someword". sscanf() read 4 items
Since both the substrings we aren't interested in ("K: " and "\r\n") are of fixed length, you can do this:
char *s;
size_t len = strlen(m);
s = malloc(len);
strcpy(s, m + 3);
s[len - 4] = 0;
printf("%s\n", s);
free(s);
Note that I declared a new char * variable to copy to since m is in read-only memory, and that robust code would handle the case where malloc failed and returned NULL.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char *m = "K: someword\r\n";
const size_t someword_len = strlen(&m[3]);
char *someword = malloc(someword_len);
if (someword == NULL) { fprintf(stderr, "Malloc error\n"); abort(); }
memcpy(someword, &m[3], someword_len - 2);
someword[someword_len - 1] = '\0';
puts(someword);
free(someword);
}
You assume that string m always starts with "K: " (that's 3 characters) and ends with "\r\n" (that's two characters).
I believe strlen(m) will be faster then strchr(m, '\r') or strrchr(m, '\r') on most platforms
After you have the length of the string, using memcpy instead of strcpy will be faster.
Remember to null terminate your string
Remember to handle errors.
So I started to learn how to code a few weeks ago, and this site helped me so much, thank you for that. But this time I got stuck and canĀ“t really figure out why...Hope you can help me.
Basically I have a function prototype I have to use in my program and I have my troubles with it. The function should receive a string and then only copy every second char of that string and return the result...
This is what I've got so far:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define max_size 1000
char * everySecondChar(char * dest, char * input);
int main() {
char inputstr[max_size] = {0};
char *input[max_size] = {0};
char *dest[max_size] = {0};
char temp[max_size] = {0};
int i = 0;
while (fgets(inputstr, max_size, stdin) != NULL)
{
input[i] = strndup(inputstr, max_size);
strcat(temp,inputstr);
i++;
}
input[0] = strndup(temp, max_size);
printf("Inputted text:\n%s", *input);
printf("\n");
printf("\n");
printf("Resulting string:\n");
everySecondChar(*dest, *input);
printf("%s", *dest);
return 0;
}
char * everySecondChar(char * dest, char * input)
{
int i = 0;
for(i = 0; i < max_size; i+=2) {
strcat(dest,input);
}
return dest;
}
I know this is probably a 1-min challenge for the most of you, but I am having my troubles whenever I see those nasty * in a function prototype :(
Congrats on getting started with programming!
To your question: there's quite a few things that could be addressed, but since there seems to be some more basic confusion and misunderstanding, I'll address what makes sense given the context of your issue.
First, you're using strcat which concatenates strings (e.g. adds to the string), when you just need simple character assignment.
Next, you have a lot of pointers to arrays and there seems to be some confusion regarding pointers; in your main function, you don't need all of the temporary variables to do what you're wanting.
You could have simply:
char inputstr[MAX_SIZE] = {0};
char dest[MAX_SIZE] = {0};
You could have less (realistically) but we'll stick with the basics for now.
Next, you're looping to get user input:
while (fgets(inputstr, max_size, stdin) != NULL)
{
input[i] = strndup(inputstr, max_size);
strcat(temp,inputstr);
i++;
}
Here, you don't check if i exceeds max_size which your input variable has been allocated for; if i exceeds max_size when you go to assign input[i] to the memory location returned by strndup (which calls malloc), you are writing beyond your memory bounds, which is also known as a buffer overflow. This is potentially where your segmentation fault is happening. You could also have some issues when you do strcat(temp,inputstr); since strcat:
Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.
If you're simply just trying to get what the user entered, and print every 2nd character with your function, you don't need to loop:
if (fgets(inputstr, MAX_SIZE, stdin) != NULL) {
everySecondChar(dest, inputstr);
printf("Inputted text:\n%s\n\nResulting string:\n%s\n", inputstr, dest);
}
Lastly, in your everySecondChar function, you're using strcat again when all you need to do is simple assignment (which does a 'copy'):
char * everySecondChar(char * dest, char * input)
{
int i, j;
for(i = 0, j = 0; i < MAX_SIZE; ++i, ++j) {
if (input[i] == 0) break; // end if string?
dest[j] = input[i++];
}
return dest;
}
Putting all of it together, you get:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_SIZE 1000
char * everySecondChar(char * dest, char * input);
int main(void)
{
char inputstr[MAX_SIZE] = {0};
char dest[MAX_SIZE] = {0};
printf("Enter some text: ");
if (fgets(inputstr, MAX_SIZE, stdin) != NULL) {
everySecondChar(dest, inputstr);
printf("Inputted text:\n%s\n\nResulting string:\n%s\n", inputstr, dest);
}
return 0;
}
char * everySecondChar(char * dest, char * input)
{
int i, j;
for(i = 0, j = 0; i < MAX_SIZE; ++i, ++j) {
if (input[i] == 0) break; // end if string?
dest[j] = input[i++];
}
return dest;
}
That aside, I'll address some other things; typically if you have a constant value, like your max_size variable, it's considered "best practice" to capitalize the entire thing:
`#define MAX_SIZE 1000`
I am having my troubles whenever I see those nasty * in a function prototype :(
Those nasty *'s in your function prototype (and variable declarations) are known as a pointer qualifier; it indicates that the type of the variable is a pointer to the type specified. A pointer isn't something to be scared of, you're learning C, it's highly important you understand what a pointer is and it's utility.
I won't dive into all of the specificities of pointers, aliases, etc. etc. since that is beyond the scope of this Q&A, but WikiBooks has a great intro and explanation covering a lot of those concepts.
Hope that can help!
I am a newbie in c programming language and I have a university tutorial assignment that is related with working with chars(I wont be graded for this assignment) where you have to count words, I have to compile and submit my answers in an online web environment where my code will run against test cases that are not visible to me.here is my assignment:
Write the function 'wc' which returns a string containing formatted as follows: "NUMLINES NUMWORDS NUMCHARS NUMBYTES" .
Whitespace characters are blanks, tabs (\t) and new lines (\n). A character is anything that is not whitespace. The given string is null-char (\0) terminated.
here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* wc(char* data) {
char* result ;
int numLine ;
int numWords ;
int numChars ;
int i;
int numBytes =strlen(data);
char* empty=NULL;
while(strstr(data,empty)>0){
numWords=1;
for (i = 0; i < sizeof(data); i++) {
if(data[i]=='\n'){
numLine++;
}
if(data[i]==' ' ){
numWords++;
}
if(data[i]!=' '){
numChars++;
}
}
}
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
return result;
}
this code will give me the correct output result but I am missing something here at least the test tells me that.
You've got a very serious error:
char* result;
...
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
This is not allowed in C. You need to allocate sufficient memory for the string first. Declare result as a large enough static array, or use malloc if you've covered that in your course.
e.g.
char buf[100]; // temporary buffer
sprintf(buf, "%d %d %d %d", numLine, numWords, numChars, numBytes);
char *result = malloc(strlen(buf) + 1); // just enough for the string
strcpy(result, buf); // store the string
return result;
What if you have this input?
Two Words.
You have to count the transitions between whitespace/non-whitespace, not just count spaces.
Also, I'm pretty sure strstr(data,NULL) will not do anything useful.
You also appear to be missing the \t for tab in your white space checker, and you're not correctly checking when you're in or out of a word. You can use the boolean type bool for this defined in stdbool.h for this.
Source code of wc unix command:
http://www.gnu.org/software/cflow/manual/html_node/Source-of-wc-command.html
All test cases handled.
1) sizeof is wrong:
Instead of sizeof operator you need to use strlen() in for loop, like:
for (i = 0; i < strlen(data); i++)
^ not sizeof
sizeof(data) returns only size of data pointer address that is 4. Because you are to read all char in data[] you need strlen() that will return length of data[] (or number of chars in data[])
2) memory error:
Next Error I can notice there is no memory allocated for result. it declare like:
char* result ;
and No memory allocate! and you are writing using sprintf that cause undefined behavior of your code
3) while(strstr(data,empty)>0) is wrong
strstr() search position of a string in to other you empty string is NULL , CHECK:
char *strstr(const char *s1, const char *s2);
you strstr() always returns data, Why are you calling this? I believe you don't need this while() loop.
I improved you code upto some extend as below, There was only three error as I mentioned above now corrected(to understand read comments), You basic algo is correct:
#define SIZE 256 // added size macro
char* wc(char* data)
char* result = malloc(SIZE*sizeof(char)); //(2) allocated memory for result
int numLine ;
int numWords ;
int numChars ;
int i;
int numBytes =strlen(data);
numWords=1;
// (3) remove while loop
for (i = 0; i < strlen(data); i++) { //(1) change size
if(data[i]=='\n'){
numLine++;
}
if(data[i]==' ' ){
numWords++;
}
if(data[i]!=' '){
numChars++;
}
}
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
return result;
}
int main(){
printf("\nresult: %s\n", wc("q toei lxlckmc \t \n ldklkjjls \n i \t nn "));
return 1;
}
Output:
result: 2 14 28 41