I'm just learning C, and having worked with languages as javascript and php up till now, i am having trouble converting some of my thinking steps to the possibilities of C. The program i am writing ( that sounds bigger than it really is ) uses an input menu that lets the user pick an option. The options can be 1, 2 or 3.
Now, i'm doing:
int menu;
scanf("%d", &menu);
Which works fine. However, entering a character of a string will cause problems.
In javascript i'd simply match the menu variable against the options:
if ( menu != 1 && menu != 2 && menu != 3 ){
menu = 4; // a switch later on will catch this.
}
However, that does not seem to work in C.
How should i approach this?
You should check for errors:
int menu;
if (scanf("%d", &menu) != 1)
{
/* error */
/* e.g.: */ menu = 4;
}
(On success, scanf returns the number of items that you wanted to read.) How you handle the error is up to you. You could loop until you have a valid value, or abort immediately, or set the variable to a default value.
An alternative is to read a whole line of input with fgets and then attempt to tokenize and interpret that, e.g. with strtok and strtol.
The scanf function is returning a result, the count of successfully read inputs. (And there is also the %n format sequence to get the number of consumed characters.).
So you could use either solutions.
if (scanf(" %d", &menu) != 1) {
/* handle error */
}
or perhaps :
int pos = -1;
if (scanf(" %d %n", &menu, &pos) <=0 || pos <= 0) {
/* handle error */
}
My second example is not really useful in your case. But sometimes %n is very useful.
I am putting a space before the %d on purpose: the C library would skip spaces.
Related
I want the user to be able to input two types of input like: "C[some size_t]" and "O[some memory address]" and depending on whether it's a C or O at the beginning, I'll call different functions. I also want to be able to take in an unknown number of those inputs and in any order.
My way of going around it was something like this:
int main()
{
// variables
while (1) { // Infinite loop to take in multiple unknown amount of inputs?
while (fgets(input, BUFFER_SIZE, stdin)) {
if (sscanf(input, "%c%zu", &branch, &num) == 2) {
if (strcmp(&branch, "C")
// function call
} else if (sscanf(input, "%c%c", &branch, (char *)addr) == 2) {
if (strcmp(&branch, "O")
// function call
}
}
}
return 0;
}
I understand why it's not working of course and I know my not-solution is wrong but I have no idea how else to go about this. The code takes in the first input and just hangs and if I start with an input beginning with O, it'll go into the first if statement when it's not supposed to. I'm also not sure if my while(1) loop is the correct way to deal with multiple user inputs.
You try to read the branch char and the respective argument in one single go – however the argument differs in both cases. So you need first to read the character, then decide and only then scan the argument – as soon as you know what to scan at all and thus are able to select the appropriate format string.
As you just read in any character you could do so a bit simpler with getc, by the way:
char branch = getc(); // instead of `if(scanf("%c", &branch) == 1)`
// note: would have been one single scan (branch) only!
if(branch == 'C')
{
// scan num
}
else if(branch == 'O')
{
// scan addr
}
else
{
// error handling
}
or alternatively (I personally would prefer)
char branch = getc();
switch(branch)
{
case 'C':
// scan num
break;
case 'O':
// scan addr
break;
default:
// error handling
break;
}
Note that strcmp requires null-terminated strings (char arrays) and cannot be used for comparing single characters – these need to be compared via equality operator, see above.
Input : [1,3,2,4]
I want to make arr[4] = {1, 3, 2, 4} from this input using scanf(). How can I do this in C language?
It is possible to parse input such as you describe with scanf, but each scanf call will parse up to a maximum number of fields determined by the given format. Thus, to parse an arbitrary number of fields requires an arbitrary number of scanf calls.
In comments, you wrote that
I want to find a method to ignore '[', ']', ',' and only accept integer units.
Taking that as the focus of the question, and therefore ignoring the issues of how you allocate space for the integers to be read when you do not know in advance how many there will be, and assuming that you may not use input functions other than scanf, it seems like you are looking for something along these lines:
int value;
char delim[2] = { 0 };
// Scan and confirm the opening '['
value = 0;
if (scanf("[%n", &value) == EOF) {
// handle end of file or I/O error ...
} else if (value == 0) {
// handle input not starting with a '[' ...
// Note: value == zero because we set it so, and the %n directive went unprocessed
} else {
// if value != 0 then it's because a '[' was scanned and the %n was processed
assert(value == 1);
}
// scan the list items
do {
// One integer plus trailing delimiter, either ',' or ']'
switch(scanf("%d%1[],]", &value, delim)) {
case EOF:
// handle end of file or I/O error (before an integer is read) ...
break;
case 0:
// handle input not starting with an integer ...
// The input may be malformed, but this point will also be reached for an empty list
break;
case 1:
// handle malformed input starting with an integer (which has been scanned) ...
break;
case 2:
// handle valid (to this point) input. The scanned value needs to be stored somewhere ...
break;
default:
// cannot happen
assert(0);
}
// *delim contains the trailing delimiter that was scanned
} while (*delim == ',');
// assuming normal termination of the loop:
assert(*delim == ']');
Points to note:
it is essential to pay attention to the return value of scanf. Failure to do so and to respond appropriately will cause all manner of problems when unexpected input is presented.
the above will accept slightly more general input than you describe, with whitespace (including line terminators) permitted before each integer.
The directive %1[],] attempts to scan a 1-character string whose element is either ] or ,. This is a bit arcane. Also, because the input is scanned as a string, you must be sure to provide space for a string terminator to be written, too.
it would be easier to write a character-by-character parser for your specific format that does not rely on scanf. You could also use scanf to read one character at a time to feed such a parser, but that seems to violate the spirit of the exercise.
While I think that John Bollinger answer is pretty good and complete (even without considering the wonderful %1[[,]), I would go for a more compact and tolerant version like this:
#include <stdio.h>
size_t arr_input(int *arr, size_t max_size)
{
size_t n;
for (n = 0; n < max_size; ++n) {
char c;
int res = scanf("%c%d", &c, arr + n);
if (res != 2
|| (n == 0 && c != '[')
|| (n > 0 && c != ',')
|| (n > 0 && c == ']')) {
break;
}
}
return n;
}
int main(void)
{
char *test_strings[] = { "[1,2,3,4]", "[42]", "[1,1,2,3,5,8]", "[]",
"[10,20,30,40,50,60,70,80,90,100]", "[1,2,3]4" };
size_t test_strings_n = sizeof test_strings / sizeof *test_strings;
char filename[L_tmpnam];
tmpnam(filename);
for (size_t i = 0; i < test_strings_n; ++i) {
freopen(filename, "w+", stdin);
fputs(test_strings[i], stdin);
rewind(stdin);
int arr[9];
size_t num_elem = arr_input(arr, 9);
printf("%zu: %s -> ", i, test_strings[i]);
for (size_t j = 0; j < num_elem; ++j) {
printf("%d ", arr[j]);
}
printf("\n");
fclose(stdin);
}
remove(filename);
return 0;
}
The idea is that you allocate space for the maximum number of integers you accept, then ask the arr_input() function to fill it up to max_size elements.
The check after scanf() tries to cope with incorrect input, but is not very complete. If you trust your input to be correct (don't) you can even make it shorter, by dropping the three || cases.
The most complex thing was to write the test driver with tmp files, strings, reopening and such. Here I'd have loved to have std::istream to just drop a std::stringstream. The fact that the FILE interface doesn't support strings really bugs me.
int arr[4];
for(int i=0;i<4;i++) scanf("%d",&arr[i]);
Are you asking for this? I was little bit confused with your question, if this doesn't solve your query, then don't hesitate to ask again...
use scanf to read a string input from user then parse that input into an integer array
To parse you can use string function "find" to locate the "," and "[]" and then use "atoi" to convert string into integer to fill the destination input array.
Edit: find is a C++ function.
the C function is strchr
Pre-History:
I had the issue, that the getchar() function did not get processed in the right way as there was not a request for any given input and the program just have continued processing further.
I searched the internet about what this issue could be and found the information that if the scanf() function is implemented into a program before the getchar() function, the getchar() function does not behave in the right way, and would act like my issue was.
Citation:
I will bet you ONE HUNDRED DOLLARS you only see this problem when the call to getchar() is preceded by a scanf().
Don't use scanf for interactive programs. There are two main reasons for this:
1) scanf can't recover from malformed input. You have to get the format string right, every time, or else it just throws away whatever input it couldn't match and returns a value indicating failure. This might be fine if you're parsing a fixed-format file when poor formatting is unrecoverable anyway, but it's the exact opposite of what you want to do with user input. Use fgets() and sscanf(), fgets() and strtok(), or write your own user input routines using getchar() and putchar().
1.5) Even properly used, scanf inevitably discards input (whitespace) that can sometimes be important.
2) scanf has a nasty habit of leaving newlines in the input stream. This is fine if you never use anything but scanf, since scanf will usually skip over any whitespace characters in its eagerness to find whatever it's expecting next. But if you mix scanf with fgets/getchar, it quickly becomes a total mess trying to figure out what might or might not be left hanging out in the input stream. Especially if you do any looping -- it's quite common for the input stream to be different on the first iteration, which results in a potentially weird bug and even weirder attempts to fix it.
tl;dr -- scanf is for formatted input. User input is not formatted. //
Here is the link, to that thread: https://bbs.archlinux.org/viewtopic.php?id=161294
scanf() with:
scanf("%x",integer_variable);
seems for me as a newbie to the scene as the only way possible to input a hex number from the keyboard (or better said the stdin file) and store it to a int variable.
Is there a different way to input a hex value from the stdin and store it into an integer variable?
Bonus challenge: It would be nice also, if i could write negative values (through negative hex input of course) into an signed int variable.
INFO: I have read many threads for C here on Stackoverflow about similar problems but none of those answer my explicit question quite well. So i´ve posted this question.
I work under Linux Ubuntu.
The quote about the hundred dollar bet is accurate. Mixing scanf with getchar is almost always a bad idea; it almost always leads to trouble. It's not that they can't be used together, though. It's possible to use them together -- but usually, it's just way too difficult. There are too many fussy little details and "gotcha!"s to keep track of. It's more trouble than it's worth.
At first you had said
scanf() with ... %d ... seems for me as a newbie to the scene as the only way possible to input a hex number from the keyboard
There was some side confusion there, because of course %d is for decimal input. But since I'd written this answer by the time you corrected that, let's proceed with decimal for the moment.
(Also for the moment I'm leaving out error checking -- that is, these code fragments don't check for or do anything graceful if the user doesn't type the requested number.) Anyway, here are several ways of reading an integer:
scanf("%d", &integer_variable);
You're right, this is the (superficially) easiest way.
char buf[100];
fgets(buf, sizeof(buf), stdin);
integer_variable = atoi(buf);
This is, I think, the easiest way that doesn't use scanf. But most people these days frown on using atoi, because it doesn't do much useful error checking.
char buf[100];
fgets(buf, sizeof(buf), stdin);
integer_variable = strtol(buf, NULL, 10);
This is almost the same as before, but avoids atoi in favor of the preferred strtol.
char buf[100];
fgets(buf, sizeof(buf), stdin);
sscanf(buf, "%d", &integer_variable);
This reads a line and then uses sscanf to parse it, another popular and general technique.
All of these will work; all of these will handle negative numbers. It's important to think about error conditions, though -- I'll have more to say about that later.
If you want to input hexadecimal numbers, the techniques are similar:
scanf("%x", &integer_variable);
char buf[100];
fgets(buf, sizeof(buf), stdin);
integer_variable = strtol(buf, NULL, 16);
char buf[100];
fgets(buf, sizeof(buf), stdin);
sscanf(buf, "%x", &integer_variable);
These should all work, too. I wouldn't necessarily expect them to handle "negative hexadecimal", though, because that's an unusual requirement. Most of the time, hexadecimal notation is used for unsigned integers. (In fact, strictly speaking, %x with scanf and sscanf must be used with an integer_variable that has been declared as unsigned int, not plain int.)
Sometimes it's useful or necessary to do this sort of thing "by hand". Here's a code fragment that reads exactly two hexadecimal digits. I'll start out with the version using getchar:
int c1 = getchar();
if(c1 != EOF && isascii(c1) && isxdigit(c1)) {
int c2 = getchar();
if(c2 != EOF && isascii(c2) && isxdigit(c2)) {
if(isdigit(c1)) integer_variable = c1 - '0';
else if(isupper(c1)) integer_variable = 10 + c1 - 'A';
else if(islower(c1)) integer_variable = 10 + c1 - 'a';
integer_variable = integer_variable * 16;
if(isdigit(c2)) integer_variable += c2 - '0';
else if(isupper(c2)) integer_variable += 10 + c2 - 'A';
else if(islower(c2)) integer_variable += 10 + c1 - 'a';
}
}
As you can see, it's a bit of a jawbreaker. Me, although I almost never use members of the scanf family, this is one place where I sometimes do, precisely because doing it "by hand" is so much work. You can simplify it considerably by using an auxiliary function or macro to do the digit conversion:
int c1 = getchar();
if(c1 != EOF && isascii(c1) && isxdigit(c1)) {
int c2 = getchar();
if(c2 != EOF && isascii(c2) && isxdigit(c2)) {
integer_variable = Xctod(c1);
integer_variable = integer_variable * 16;
integer_variable += Xctod(c2);
}
}
Or you could collapse those inner expressions down to just
integer_variable = 16 * Xctod(c1) + Xctod(c2);
These work in terms of an auxiliary function:
int Xctod(int c)
{
if(!isascii(c)) return 0;
else if(isdigit(c)) return c - '0';
else if(isupper(c)) return 10 + c - 'A';
else if(islower(c)) return 10 + c - 'a';
else return 0;
}
Or perhaps a macro (though this is definitely an old-school sort of thing):
#define Xctod(c) (isdigit(c) ? (c) - '0' : (c) - (isupper(c) ? 'A' : 'a') + 10)
Often I'm parsing hexadecimal digits like this not from stdin using getchar(), but from a string. Often I'm using a character pointer (char *p) to step through the string, meaning that I end up with code more like this:
char c1 = *p++;
if(isascii(c1) && isxdigit(c1)) {
char c2 = *p++;
if(isascii(c2) && isxdigit(c2))
integer_variable = 16 * Xctod(c1) + Xctod(c2);
}
It's tempting to omit the temporary variables and the error checking and boil this down still further:
integer_variable = 16 * Xctod(*p++) + Xctod(*p++);
But don't do this! Besides the lack of error checking, this expression is probably undefined, and it definitely won't always do what you want, because there's no longer any guarantee abut what order you read the characters in. If you know p points at the first of two hex digits, you don't want to collapse it any further than
integer_variable = Xctod(*p++);
integer_variable = 16 * integer_variable + Xctod(*p++);
and even then, this will work only with the function version of Xctod, not the macro, since the macro evaluates its argument multiple times.
Finally, let's talk abut error handling. There are quite a few possibilities to worry about:
The user hits Return without typing anything.
The user types whitespace before or after the number.
The user types extra garbage after the number.
The user types non-numeric input instead of a number.
The code hits end-of-file; there are no characters to read at all.
And then how you handle these depends on what input techniques you're using. Here are the basic rules:
A. If you're calling scanf, fscanf, or sscanf, always check the return value. If it's not 1 (or, in the case where you had multiple % specifiers, it's not the number of values you expected to read), it means something went wrong. This will generally catch problems 4 and 5, and will handle case 2 gracefully. But it will often quietly ignore problems 1 and 3. (In particular, scanf and fscanf treat an extra \n just like leading whitespace.)
B. If you're calling fgets, again, always check the return value. You'll get NULL on EOF (problem 5). Handling the other problems depends on what you do with the line you read.
C. If you're calling atoi, it will deal gracefully with problem 2, but it will ignore problem 3, and it will quietly turn problem 4 into the number 0 (which is why atoi is usually not recommended any more).
D. If you're calling strtol or any of the other "strto" functions, they will deal gracefully with problem 2, and if you let them give you back an "end pointer", you can check for and deal with problems 3 and 4. (Note that I left the end-pointer handling out of my two strtol examples above.)
E. Finally, if you're doing something down-and-dirty like my "hardway" two-digit hex converter, you generally have to take care of all these problems, explicitly, yourself. If you want to skip leading whitespace you have to do so (the isspace function from <ctype.h> can help), and if there might be unexpected non-digit characters, you have to check for those, too. (That's what the calls to isascii and isxdigit are doing in my "hardway" two-digit hex converter.)
Per scanf man page, you can use scanf to read hex number from stdin into (unsigned) integer variable.
unsigned int v ;
if ( scanf("%x", &v) == 1 ) {
// do something with v.
}
As per man page, %x is always unsigned. If you want to support negative values, you will have to add explicit logic.
As mentioned in the link you posted, using fgets and sscanf is the best way to handle this. fgets will read a full line of text and sscanf will parse the line.
For example
char line[100];
fgets(line, sizeof(line), stdin);
int x;
int rval = sscanf(line, "%x", &x);
if (rval == 1) {
printf("read value %x\n", x);
} else {
printf("please enter a hexadecimal integer\n");
}
Since you're only reading in a single integer, you could also use strtol instead of sscanf. This also has the advantage of detecting if any additional characters were entered:
char *ptr;
errno = 0;
long x = strtol(line, &ptr, 16);
if (errno) {
perror("parsing failed");
} else if (*ptr != '\n' && *ptr != 0) {
printf("extra characters entered: %s\n", ptr);
} else {
printf("read value %lx\n", x);
}
I'd like some assistance with understand how inputting data in a program of C works. So far I'm used the java syntax having the convenient try{}catch(){}; clause but I don't see it anywhere on C (or I haven't found it?).
Assuming I have the following array;
float f_array[10];
Normally for me to input data I'd either use a scanf(...); or a file which I can read input from, but for the shake of simplicity let's assume I use scanf(...);
And I have the following;
int i;
for(i = 0; i<10; i++){
scanf("%f", &f_array[i]);
}
Now , my question is how to restrain the user from putting in the input a character or a string or the wrong data type for that matter? Also , should I always try to initialize the array before actually putting values in it?
Note that scanf() returns number of elements successfully read, you can check it:
int success = scanf(...);
if (!success) {
scanf("%*[^\n]%*c"):
// OR while(getchar() != '\n');
}
There is, however, a complex solution. You don't use scanf(), but write a custom input method that processes keystrokes and filters out invalid characters, possibly using getch() (Windows/nCurses). Here's a minimized Windows version:
void readFloat(float* in){
int ch, ind = 0;
char buf[100];
while (1){
ch = getch();
if (ch >= '0' && ch <= '9' || ch == '.') {
buf[ind++] = (char)ch;
putchar(ch);
}
else if (ch == 8) /* Backspace */ {
printf("\b \b");
ind --;
}
}
buf[ind] = '\0';
float ret;
sscanf(buf, "%f", &ret);
return ret;
}
So a possible result of the code:
User input (key presses): 123aaa.bbb456
Program filter (displayed on screen): 123.456
Return value: (float)123.456
Now , my question is how to restrain the user from putting in the input a character or a string or the wrong data type for that matter?
Without dedicated hardware support (say, using a keyboard that does not have letter keys, or some device that gives the user an electric shock to discourage them from hitting the 'A' key) there is no way to restrain a user from entering unwanted data.
Instead, you need to write your code with the ASSUMPTION that the user will enter invalid or poorly formed data, and cope with that. It is true that your code is simpler if you can assume an obedient and tractable user who only gives correct input, but the real world isn't like that.
scanf() - reading and interpreting data directly from stdin doesn't actually work well with such an assumption. The return value from scanf() can give you an indication a problem after the fact (e.g. the return value is number of fields successfully input, or EOF). However, when a problem occurs, scanf() handles it in a way you cannot control. Let's say you code has a
scanf("%f", &f_array[i]);
and the user hits the 'X' followed by the Enter key. scanf() will recognise the 'X' character is waiting to be read, and return immediately. The value it returns will not be 1 (which would indicate success). Even worse, the 'X' will be left to be read by a subsequent call of scanf() and the same will happen again (unless a different format is specified). Which means, if you call scanf() in a loop this way, the same will happen over and over again.
Some folks will tell you to simply find a way to read and discard the character 'X'. The problem with that approach is that there are MANY ways for the user to enter bad inputs, and you need to account for all of them. If the user does something you (or your code) doesn't expect, you get problems (e.g. program hanging waiting for the same input repeatedly, input being used as data when it isn't). You're back where you started.
The more robust approach is to simply read a line of input, and do checks before trying to extract a floating point value from it, such as
char buffer[20];
int got_one = 0;
while (!gotone && fgets(buffer, sizeof buffer, stdin) != NULL)
{
if (check_string(buffer))
{
if (sscanf(buffer, "%f", &f_array[i]) == 1)
{
/* yay - we got a floating point value */
got_one = 1;
}
else
{
fprintf(stderr, "Floating point scanning failed. Try again\n");
}
}
else
{
fprintf(stderr, "Bad data discarded. Try again\n");
}
}
Essentially, this provides several hooks so you can check user input in various ways. If you want to, it can be adapted to discard part of a line, and scan useful data from whatever's left.
The key, however, is that the code does not assume the user is well behaved. It only attempts to read a floating point value after a gauntlet of checks, and still copes if the reading fails.
The code can also be adapted to deal with users who enter data that overflows the buffer (e.g. entering 30 floating point characters on a single line). I'll leave that as an exercise.
Also , should I always try to initialize the array before actually putting values in it?
That depends on the needs of your code, but generally speaking I would not bother.
With approaches like I suggest above, you can avoid a circumstance of using the array (or elements of the array) unless valid data has actually been put into it.
All initialising the array will do is obscure cases where the code doing input (reading from the user) has not properly dealt with bad user input.
give the user some feedback on a per input basis.
process each input and allow user to make corrections as you go.
use "atof()" to do the conversion, but it has a couple of quirks:
it tells you there is an error by returning a value of 0.0
it stops processing if/when it finds an invalid char and returns what it has up to that point
eg. 6.35k gives 6.35 -- this usually works out ok;
otherwise you have to check for invalid chars yourself.
try this
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main () {
float f_array[10];
int i;
float input_value;
char temp_string[32];
for(i = 0; i<10; i++){
printf("input a floating point number: ");
scanf("%s",&temp_string[0]);
input_value = atof(temp_string);
while(input_value == 0.0) {
printf("%s is not a valid floating point number\n");
printf("example is 5.6 or 1e32 or 17\n");
printf("try again - input a floating point number: ");
scanf("%s",&temp_string[0]);
input_value = atof(temp_string);
}
f_array[i] = input_value;
printf("String value = %s, Float value = %f\n", temp_string, input_value);
}
/* use the data */
for(i = 0; i<10; i++){
printf("%f\n",f_array[i]);
/* do something */
}
}
I have a text file that looks like the following:
12345678909876543211234567890
09876543122345678900
I will eventually need to add these two values together using separate stacks so I want to push each digit into a stack separately so I have code like the following:
test=fopen("test.txt","r");
while (!feof(fp)) {
fscanf(test, "%1d", &number);
Push((Item)number, &num1);
}
I need to modify my code though so that it reads the first line 1 digit at a time, pushing on each digit, and then for the next line I need it to push into another stack called num2 instead of num1 as you see in the current code.
You've not told us about the stack types, so I'm inventing one — typedef name is Stack:
Stack num1, num2;
Stack *stacks[2] = { &num1, &num2 };
…initialize stacks
…open file and check that the open was successful
for (int i = 0; i < 2; i++)
{
char line[4096];
if (fgets(line, sizeof(line), test) == 0)
…report unexpected EOF or other error; do not continue…
char *digit = line;
while (isdigit((unsigned char)*digit))
Push((Item)(*digit++ - '0'), stacks[i]);
if (*digit != '\n' && *digit != '\0')
…report unexpected (non-digit) data on input; do not continue;
}
I assume that Push is a function, not a macro that might evaluate its first argument more than once. If it is such a macro, the loop body needs be split onto two lines and braces added so that the increment of digit is separate from the function call.
Note that one major advantage of this method is that you have the whole line of data available for error reporting, which is typically easier for people to understand than only being able to report on the dribs and drabs left over after a semi-indeterminate number of characters was read from the line by a scanf() loop. You can even use sscanf() on the line if you want to — see How to use sscanf() in loops?