Getting values from a mixed string - c

I am trying to get the values I need from a string using sscanf, but I can't get it done. Here is what I am trying to do:
I have a string which has this pattern
2 7 A BUL
(integer space integer space character space string of 3 elements)
I have to get each value separated by spaces and store it into variables.
This is my code:
sscanf(string[i],"%d %d %s %s",&e,&m,id,modelo);
The problem I'm having is that it only stores the first integers, ignoring the chars.
How can I fix this?
Thank you.
EDIT:
Here's the whole code for the function:
void le_lista(lista *l) {
int e,m;
char id[1],modelo[3],frase[20][12];
int linha=0;
while (1) {
fgets(frase[linha],12,stdin);
//
if (feof(stdin)) {
break;
}
//
linha++;
}
int i;
for(i=0;i<=linha;i++) {
sscanf(frase[i],"%d %d %s %s",&e,&m,&id,&modelo);
enfila(e,m,id,modelo,l);
//printf("%s",frase[i]);
}
printf("Linhas: %d",linha+1);
return;
}

char mystring[] = "2 7 A BUL"
x = strtok(mystring, " "); //separates by spaces
More here Split string with delimiters in C

Given that you want to recognize 2 7 A BUL, you cannot safely use:
int e,m;
char id[1], modelo[3];
sscanf(frase[i], "%d %d %s %s", &e, &m, &id, &modelo);
First, you shouldn't pass char (*)[] values where a char * is expected; do not take the address of an array; pass id and modelo only. GCC will warn you about that if you turn on warnings. If you're learning C, you can't afford not to have the warnings turned on (use -Wall at minimum; -Wall -Wextra if at all possible).
Next, the first %s will read an arbitrary number of characters and null-terminate the result. This is going to overwrite the end of the id array. And you can't safely read 3 characters into modelo either. Because of this, you have two stack overflow problems.
You should write either:
int e, m;
char id[2], modelo[4];
if (sscanf(frase[i], "%d %d %1s %3s", &e, &m, id, modelo) != 4)
...oops...
or perhaps:
int e, m;
char id;
char modelo[4];
if (sscanf(frase[i], "%d %d %c %3s", &e, &m, &id, modelo) != 4)
...oops...
Or, you could use char id[1]; and %c, but that is dangerous; the result is not a null-terminated string.
Your primary input loop is suspect too. You can use feof() as you did, immediately after the fgets(), but it is much more conventional to test the result of fgets() itself; it tells you whether it succeeded or not. That code should probably be:
char frase[20][12];
for (int linha = 0; i < sizeof(frase) / sizeof(frase[0]); i++)
{
if (fgets(frase[linha] ,sizeof(frase[linha]), stdin) == 0)
break;
}
This avoids repeating the 20 or the 12 but protects you from too many lines. It does not protect you from overlong lines; you could add:
size_t len = strlen(frase[linha]);
assert(len != 0);
if (frase[len-1] != '\n')
...input was too long...
to the loop.
You could also think about doing the sscanf() and call to enfila() in the main input loop; you would not then need the frase array to be multi-dimensional.
Putting all the changes together:
char frase[4096];
while (fgets(frase, sizeof(frase), stdin) != 0)
{
int e;
int m;
char id[2];
char modelo[4];
if (sscanf(frase, "%d %d %1s %3s", &e, &m, id, modelo) == 4)
enfila(e, m, id, modelo, l); // l is the parameter to the function
else
...report error...
}
Using fgets() and sscanf() was definitely the correct way to go. It means that error reporting can show the whole line of input, rather than whatever mangled remains scanf() left behind as unreadable.

use instead strtok which is more simpler and IMHO more solid since you can do a simple add error handling
e.g.
int j = 0;
for (char* token = strtok(frase[i]," "; token != NULL; token = strtok(NULL," ")
{
switch(j++)
{
case 0:
e = atoi(token);
break;
case 1:
m = atoi(token);
break;
case 2:
strncpy(id,token,sizeof(id)); // or strcpy_s
break;
case 3:
strncpy(modelo,token,sizeof(modelo)); // or strcpy_s
break;
default:
printf( "invalid number of arguments in line %d\n", i );
break;
}
}

Related

Is this a good scanf alternative?

void fill_boxes(box *boxes, int length)
{
char *n;
for (int i = 0; i < length; i++) {
printf("Enter box %d id: ", i+1);
//scanf("%d",&boxes[i].id);
fgets(n,25,stdin);
boxes[i].id = strtol(n,NULL,10);
printf("Enter box %d name: ", i+1);
//scanf(" %49[^\n]",boxes[i].name);
fgets(boxes[i].name,50,stdin);
}
}
I tried this method to replace scanf and it looks like the input() function in python where it takes the input as a string and then you convert it to whatever type you want, which is what I'm doing with fgets() then strtol().
I'm asking if this is a good alternative to scanf or are there better solutions available ?
fgets() is good, when passed valid arguments.
fgets(n,25,stdin) is bad in this code as the value of pointer n is indeterminate.
Instead of using pointer char *n;, use an ample sized array like char buffer[100];. I recommend 2x the expected max size.
Use sizeof to determine array size, not a magic number.
Check fgets() return value.
Avoid using both fgets() and scanf() in the same program. I recommend using fgets() only until your understand why scanf() is problematic.
void fill_boxes(box *boxes, int length) {
// char *n;
char buffer[100];
for (int i = 0; i < length; i++) {
printf("Enter box %d id: ", i+1);
// fgets(n,25,stdin);
if (fgets(buffer, sizeof buffer ,stdin) == NULL) {
printf("No valid input\n");
break;
}
boxes[i].id = strtol(n,NULL,10);
printf("Enter box %d name: ", i+1);
// fgets(boxes[i].name,50,stdin);
if (fgets(buffer, sizeof buffer ,stdin) == NULL) {
printf("No valid input\n");
break;
}
snprintf(boxes[i].name, sizeof boxes[i].name, "%[^\n]", buffer);
// or
buffer[strcspn(buffer, "\n")] = 0; // Lop off potential \n
snprintf(boxes[i].name, sizeof boxes[i].name, "%s", buffer);
}
}
Additional code concerns:
What should happen if the line of input is excessively long?
What should happen with errant input like "123 zxfc\n" for boxes[i].id?
fgets(boxes[i].name,50,stdin); is a problem as rarely is the trailing '\n' desired in boxes[i].name. Zeroing the '\n' after the read, limits reduces by 1, what could be stored in boxes[i].name.

Using a user-inputted string from one function to another in C

So... the main question is how I can use the string that the user entered in another function? I know it would be a lot easier to do it all in the main function but we are forced to use as many separate ones as possible. Thanks in advance.
Following on from the comment, you most likely want to declare the str in a scope available to both functions:
int enterWord (char *str) {
...
scanf("%24s", str);
...
return str[0];
}
int menuScan (char *str) {
...
}
int main (void) {
char str[25] = {0};
int someint;
...
someint = menuScan (enterWord (str));
return 0;
}
or
int main (void) {
char str[25] = {0};
int someint, someotherint;
...
someint = enterWord (str);
...
someotherint = menuScan (str);
return 0;
}
You may want to employ a bit of additional error checking on the user input as well, e.g.:
int enterWord (char *str) {
printf ("Please enter a single word that is no more than 25 characters: ");
if (scanf ("%24s", str))
printf ("\nThanks! You entered: %s", str);
else
return -1;
return str[0];
}
...
int main (void) {
char str[25] = {0};
int someint, someotherint;
...
if ((someint = enterWord (str)) = -1) {
fprintf (stderr, "enterWord() error: input failure.\n");
return 1;
}
...
someotherint = menuScan (str);
return 0;
}
Remaining Issue With '\n' Left In Input Buffer
Your remaining problems come from the fact that after you call scanf, you are leaving the '\n' (cause by pressing [Enter]) in the input buffer stdin. The next time your program calls scanf it takes the '\n' left in the input buffer as the user input. (if you check, you will find it is using the value 0xa (or 10) which is the value for newline)
You have two options. You can use a loop to empty stdin:
int c;
while ((c = getchar()) != '\n' && c != EOF) {}
You can also use the assignment suppression operator of scanf to read and discard the newline, e.g.:
scanf ("%24[^\n]%*c", str)
Where %24[^\n] read upto 24 chars (not including the '\n' into str) and %*c which reads and discards a single character (the newline). That way your input buffer is empty before the next user input.
Here is a short working example:
#include <stdio.h>
int enterWord (char *str);
void menuOptions ();
int menuScan (char *str);
int main (void) {
char str[25] = {0};
if (enterWord (str) == -1) {
fprintf (stderr, "enterWord() error: input failure.\n");
return 1;
}
do {
menuOptions();
} while (!menuScan (str));
return 0;
}
int enterWord (char *str)
{
printf ("Please enter a single word that is no more than 25 characters: ");
if (scanf ("%24[^\n]%*c", str))
printf ("\nThanks! You entered: %s", str);
else
return -1;
return str[0];
}
void menuOptions ()
{
printf("\n\n========= MENU =========\n\n");
printf("Key Function\n");
printf("=== ========\n");
printf(" C Count the letters\n");
printf(" V Count the vowels\n");
printf(" R Reverse the word\n");
printf(" P Check if the word is a palindrome\n");
printf(" W Enter a new word\n");
printf(" Z Exit\n\n");
}
int menuScan (char *str)
{
/* always initialize variables */
char *p = str;
char menuChoice = 0;
int c = 0;
int charcnt = 0;
printf ("Please enter a character from the options above: ");
if (!scanf ("%c%*c", &menuChoice)) {
fprintf (stderr, "menuScan() error: input failure.\n");
return -1;
}
printf ("\nYou entered: %c\n", menuChoice);
c = menuChoice; /* I don't like to type */
/* validate input */
if (c < 'A' || ('Z' < c && c < 'a') || 'z' < c) {
fprintf (stderr, "menuChoice() error: input is not [a-z] or [A-Z]\n");
return -1;
}
/* convert to lowercase */
if ('A' <= c && c <= 'Z') c += 32;
switch (c) {
case 'c':
for (; *p; p++) charcnt++;
printf ("\n\nThere are '%d' letters in '%s'\n", charcnt, str);
break;
case 'z':
return -1;
default : printf ("(%c) invalid choice -> try again.\n", c);
}
return 0;
}
Compile
gcc -Wall -Wextra -finline-functions -O3 -o bin/menuscan menuscan.c
Example/Use
$ ./bin/menuscan
Please enter a single word that is no more than 25 characters: 0123456789
Thanks! You entered: 0123456789
========= MENU =========
Key Function
=== ========
C Count the letters
V Count the vowels
R Reverse the word
P Check if the word is a palindrome
W Enter a new word
Z Exit
Please enter a character from the options above: c
You entered: c
There are '10' letters in '0123456789'
========= MENU =========
Key Function
=== ========
C Count the letters
V Count the vowels
R Reverse the word
P Check if the word is a palindrome
W Enter a new word
Z Exit
Please enter a character from the options above: z
You entered: z
There are a lot of problems with your code, but I will address only the actual question you posed.
When you have a function which creates a result value to be used somewhere else, you need to return that value when the function ends. The 'return' keyword will do this, but you must bear in mind that the thing being returned must continue to exist after the function has ended (as noted by #David C. Rankin in the comments).
Locally declared variables will cease to exist when the function ends, so the solution is to declare them in a wider scope.
// declare the string in a wider scope
// provide one extra character space for the string terminator \0 character
char inputStr[25 + 1];
// pass the string to the function which will fill it with the entered string
// NOTE: to avoid risk of someone entering too many letters in the string, we
// also pass in the length of the string buffer
enterWord(inputStr, 25);
The changes to the enterWord function would be:
void enterWord(char* str, int length){
printf("Please enter a single word that is no more than %d characters: ", length);
// this should verify the length of the entered text to make sure it isn't too long... but that's not your question
scanf("%s", str);
printf("\nThanks! You entered: %s", str);
}
In the scope where you declared inputStr, the string will now contain the data entered by the user.
In this case we are returning the string from the function by a different mechanism than the 'return' keyword. Here we are passing a pointer to the first letter of the buffer space, so that the function will fill the original inputStr buffer from inside the function.
If you must use a more 'functional' coding paradigm, you might want to consider allocating space for the buffer on the heap using 'malloc', you would then need to remember to use 'free' at a later point in the code to release that allocated memory and avoid a memory leak, which is why that would not be my preferred solution in this case.

C application skips my scanf calls

I'm trying to write code to count how many times a string repeats inside another one. (If there is some easier approach, please let me know.)
Here is the code that I have now:
int getStringLenght (char str[]) {
int lenghtOfTheString;
int i;
for (i = 0; i < 100; i++) {
if(str[i] == '\0') {
lenghtOfTheString = i;
break;
}
}
return lenghtOfTheString;
}
int main()
{
printf("Type a string: ");
char T[1024];
scanf("%s",&T);
char P[100];
printf("Type a substring: ");
scanf("%s",&P);
printf("%s",P);
int stringSize = getStringLenght (P);
int occurences = 0;
int i;
for (i = 0; i < 10; i++) {
int j;
if(T[i] == P[0]) {
for (j = 0;j<10;j++) {
char c1 = T[i+j];
char c2 = P[j];
if(c1 != c2) {
break;
}
if(j == stringSize-1) {
occurences++;
//printf("string iguais em i = %d",i);
}
}
}
}
printf("\nThe substring %s was found %d times", P, occurences);
return 0;
}
The app compiles. When I type "banana", for example, on the first scanf, and then "na" on the second, the app comes out with the right answer. But, if I type "banana and milk" on the first scanf, it automatically interprets the second scanf as "and", even when I don't type anything but "banana and milk ENTER"
What's happening?
scanf's "%s" conversion only reads characters until it encounters white-space (e.g., space, new-line, or tab). When you enter more than one word, it reads the first. The second call reads the second, and so on.
If you want to read an entire line, you usually want to use fgets instead (scanf can do the job as well, but it's a little trickier, and uses a feature of which many are unaware, so they often find it difficult to understand).
You don't understand how scanf works. http://www.cplusplus.com/reference/clibrary/cstdio/scanf/ %s will only read one string, terminated by white space. If you want to keep reading strings, or read a line, you have to keep using scanf until one of your strings ends in a new line or EOF, or use another function, like fgets.
You have to remember that many functions are already implemented. This is why your getStringLength (you have typo in it's name) is needless. You can simply check the string's length using strlen function from string.h. What is more when you import this file you also have access to strstr function which finds the first occurrence of a given substring in a string. Try to use them instead of reinventing the wheel ;)
That is a standart problem with scanf. There are 3 ways to fix this:
1: Call fflush after each scanf:
scanf("%s", some_string); // you don't need to write &some_string because giving a array to a function automatically converts it to a pointer
fflush(stdin);
fflush() isn't available on every system.
2: Putting scanf in a loop:
do
scanf("%s", somestring);
while (getchar() != '\n');
3: Don't use scanf! Use fgets and sscanf!
char buffer[100]; // buffer for fgets()
fgets(buffer, 100, stdin); // read a line from stdin (standart input) into buffer
sscanf(buffer, "%s", some_string); // convert buffer in any format you want

Reading in a variable length string user input in C

I am trying to read in a variable length user input and perform some operation (like searching for a sub string within a string).
The issue is that I am not aware how large my strings (it is quite possible that the text can be 3000-4000 characters) can be.
I am attaching the sample code which I have tried and the output:
char t[],p[];
int main(int argc, char** argv) {
fflush(stdin);
printf(" enter a string\n");
scanf("%s",t);
printf(" enter a pattern\n");
scanf("%s",p);
int m=strlen(t);
int n =strlen(p);
printf(" text is %s %d pattrn is %s %d \n",t,m,p,n);
return (EXIT_SUCCESS);
}
and the output is :
enter a string
bhavya
enter a pattern
av
text is bav 3 pattrn is av 2
Please don't ever use unsafe things like scanf("%s") or my personal non-favourite, gets() - there's no way to prevent buffer overflows for things like that.
You can use a safer input method such as:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
You can then set the maximum size and it will detect if too much data has been entered on the line, flushing the rest of the line as well so it doesn't affect your next input operation.
You can test it with something like:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
In practice you shouldn't bother too much to be precise. Give yourself some slack to have some memory on the stack and operate on this. Once you want to pass the data further, you can use strdup(buffer) and have it on the heap. Know your limits. :-)
int main(int argc, char** argv) {
char text[4096];
char pattern[4096];
fflush(stdin);
printf(" enter a string\n");
fgets(text, sizeof(text), stdin);
printf(" enter a pattern\n");
fgets(pattern, sizeof(pattern), stdin);
int m=strlen(text);
int n =strlen(pattern);
printf(" text is %s %d pattrn is %s %d \n",text,m,pattern,n);
return (EXIT_SUCCESS);
}
Don't use scanf or gets for that matter because as you say, there is not real way of knowing just how long the input is going to be. Rather use fgets using stdin as the last parameter. fgets allows you to specify the maximum number of characters that should be read. You can always go back and read more if you need to.
scanf(%s) and gets read until they find a terminating character and may well exceed the length of your buffer causing some hard to fix problems.
The main problem in your case is having char arrays of unknown size. Just specify the array size on declaration.
int main(int argc, char** argv) {
int s1[4096], s2[4096];
fflush(stdin);
printf(" enter a string\n");
scanf("%s", s1);
printf(" enter a pattern\n");
scanf("%s", s2);
int m = strlen(s1);
int n = strlen(s2);
printf(" text is %s of length %d, pattern is %s of length %d \n", s1, m, s2, n);
return (EXIT_SUCCESS);
}

Unusual Behaviour while passing an array to a function

#include <stdio.h>
#define SIZE 5
void func(int*);
int main(void)
{
int i, arr[SIZE];
for(i=0; i<SIZE; i++)
{
printf("Enter the element arr[%d]: ", i);
scanf("%d", &arr[i]);
}//End of for loop
func(arr);
printf("The modified array is : ");
for(i=0; i<SIZE; i++)
printf("%d ", arr[i]);
return 0;
}
void func(int a[])
{
int i;
for(i=0; i<SIZE; i++)
a[i] = a[i]*a[i];
}
Output :::
While I'm entering integer elements the output is OK.But as I entered a float value like 1.5, it didn't ask for other elements and the O/P is as shown in the figure.I think it should implicitly typecast 1.5 to 1 but it didn't happen..can u plz tell why this happened ? All the info about the compiler is shown in the figure.
When you scanf("%d") a value like 1.5 the scanning will stop at the decimal point and return 1.
The next time you call scanf, the pointer will still point to the decimal point and your scan will return immediately because there are no digits there to scan.
You should be checking the return value from scanf - it gives you the number of items successfully scanned which will be 1 initially for the 1 before the decimal point, and 0 from then on.
As an aside, scanf stands for "scan formatted" and I'll guarantee you won't find anything more unformatted than user input.
Investigate looking into fgets for line input. Here's a copy of a function I often use for such purposes:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
Once you get a line in with that function, you can sscanf it to your heart's content, handling errors much easier.
What's happening is that scanf stops reading an integer when it sees the '.' character, and leaves it in the input buffer. Then subsequent calls to scanf fail because the next character is '.' and not something parseable as an integer.
How do you fix this? The first step is to forget you ever heard of scanf and always use fgets to read whole lines of input, then process them after you read them into a string buffer. You can use sscanf for this purpose, but a robust function like strtol would be a lot better.
Problem with buffer - I think the remaining part (.5) remains on the buffer.
use flushall(); after your scanf("%d..

Resources