I am trying to write a program that reads 2 words separated by a single space in a single input line.
Normal input should look like this: Apple Banana. Apple should be written to FirstWord and Banana to SecondWord
Currently I am using scanf and reading strings with it.
That is my code:
int main (void) {
char FirstWord[10];
char SecondWord[10];
while (true) {
printf("Enter two words: ");
scanf("%s", FirstWord);
if(!strcmp(FirstWord, "quit")) {
printf("Exit.");
return 0;
}
scanf("%s", SecondWord);
printf("First word is %s \nSecond word is %s\n", FirstWord, SecondWord);
}
}
Program is working, generally speaking, but there is one problem of detecting invalid input from the user.
How can I detect if user enters only one word or empty line? Right now the second scanf waits for the second string in case if only 1 word entered.
Is it possible to deal with that problem with scanf or should I actually get 1 string with fgets and then divide it into two strings?
When you want the user to enter two-words, you want to also be able to handle the case where the user enters one word (maybe thinking they will enter the next word on the next line, etc...) The problem with using scanf() with the "%s" conversion specifier is it ignores leading whitespace and the '\n' character is whitespace. So if only one word is entered, you do not have to opportunity to prompt for the second word because scanf() will block waiting on the second input.
Any time you are taking user input, you are encouraged to use fgets() to fill a buffer (character array) and then parse (separate) the needed values from the buffer. Why? fgets() will consume an entire line of input (given a sufficiently sized buffer). This provides certainty that nothing will remain in stdin unread --- just waiting to bite you on your next input.
You can then use sscanf() to parse values from the buffer just as you would use scanf() to parse values from input. However, you have decoupled the input and conversion, so if a sscanf() conversion fails it will NOT effect your next attempt to read from stdin.
Using fgets() also provides a convenient way of ending input. Since fgets() reads the '\n' character generated by the user pressing Enter you need only check if the first character in the buffer filled is the '\n' character to know the user has simply pressed Enter on a blank line. This can be used as a very convenient way to determine the user is done without requiring additional input such as strcmp(FirstWord, "quit"), etc...
If I understand you want to be able to seamlessly handle either one or two inputs from the user and prompt in the case the user only provides one, you can do:
#include <stdio.h>
#define WORDSZ 32 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void) {
char FirstWord[WORDSZ]; /* declare arrays */
char SecondWord[WORDSZ];
char buf[MAXC]; /* buffer to hold entire line */
puts ("press [Enter] on empty line when done.");
while (1) { /* loop continually until both filled or user cancels */
fputs ("\nEnter two words: ", stdout); /* prompt both */
if (fgets (buf, MAXC, stdin) == NULL) { /* read into buf, validate */
puts ("(user canceled input)");
return 0;
}
else if (*buf == '\n') /* if [Enter] on empty line */
break;
/* validate conversion, check if only one word read */
if (sscanf (buf, "%31s %31s", FirstWord, SecondWord) == 1) {
while (1) { /* if so, loop until second word read */
fputs ("Enter second word: ", stdout); /* prompt 2nd word */
if (!fgets (buf, MAXC, stdin)) { /* read into buf, validate */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%31s", SecondWord) == 1) /* validate word entered */
break;
}
}
printf ("\nFirst word is %s \nSecond word is %s\n", FirstWord, SecondWord);
}
}
(note: you must always provide the field-width modifier of one less than your array size for all string conversions to protect your array bounds when using the scanf() family of functions. Otherwise the use of scanf() is no safer than using gets(), See Why gets() is so dangerous it should never be used!)
Since the user canceling input by generating a manual EOF is perfectly valid, you need to check for a manual EOF after each read with fgets() by checking if the return is NULL. A manual EOF is generated by the user pressing Ctrl + d (Ctrl + z on windows).
Example Use/Output
$ ./bin/firstsecondword
press [Enter] on empty line when done.
Enter two words: hello
Enter second word: world
First word is hello
Second word is world
Enter two words: hello world
First word is hello
Second word is world
Enter two words:
Look things over and let me know if you have further questions.
Related
While executing the small piece of code below, every time I enter a character, the output is repeated and I don't understand why. Can someone explain to me why is it behaving like this?
ps: Just started my programming journey in c.
If I print a character such as 'a' I'm supposed to have 1 as output then another prompt asking me to enter a character again. But I instead get the 1, a prompt, and a 1 again then another prompt asking me to enter a character.
#include <stdio.h>
int main()
{
int usr_ch;
for ( usr_ch = 0; (usr_ch != 'q');){
printf("Enter a single character: (enter 'q' to stop)\n");
usr_ch = getc(stdin);
printf("%d\n", (usr_ch != 'q') ? 1 : 0));
}
return 0;
}
input: u
output:
Enter a single character: (enter 'q' to stop)
1
Enter a single character: (enter 'q' to stop)
1
Enter a single character: (enter 'q' to stop)
You already have a great answer explaining the additional '\n' character generated when the user presses ENTER. Continuing from the comments below the question and comment by #AllanWard about the use of fgets(), it can provide the ability to take all single characters as input and end the input when ENTER alone is pressed. There are a number of other benefits as well.
When reading a line with fgets() you read the input into a buffer (character array or allocated block of memory). Don't skimp on buffer size... fgets() reads and includes the trailing '\n' in the buffer it fills. This means an entire line of input is consumed, including the trailing '\n' given a sufficiently sized buffer. The '\n' is not left in the input buffer (stdin) unread. This will avoid the problem you are experiencing.
To access the first character in the array, all you need do is derefernce the pointer. (an array is converted to a pointer to its first element on access, C18 Standard - 6.3.2.1(p3)). So if you declare char line[1024]; to hold the input, simply referencing *line provides access to the first character.
Using fgets() avoids all of the pitfalls new C programmers fall into using scanf() and eliminates the '\n' being left unread. These are the primary reasons new C programmers (as well as not so new C programmers) are encouraged to take all user input using fgets() (or POSIX getline() which behaves in the same manner, but can also provide auto-allocation to handle a string of any length)
In addition to taking the input, without much more effort you can ensure the user has only entered one-printable character with a few simple tests. This allows you to handle individual error cases as needed. A short example of the use of fgets() and handling several of the foreseeable error cases can be written as:
#include <stdio.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void)
{
char line[MAXC]; /* buffer to hold line */
/* prompt and then read input into line */
while (fputs ("Enter a single character: (enter alone to stop): ", stdout) &&
fgets (line, MAXC, stdin)) {
/* if ENTER alone, break */
if (*line == '\n') {
puts ("exiting");
break;
}
/* if not a single-character, handle error */
else if (line[1] != '\n') {
fputs (" error: more than 1 char entered.\n", stderr);
}
/* if printable character, output */
else if (isprint ((unsigned char)*line)) {
printf (" you entered '%c'\n", *line);
}
else { /* otherwise, handle error */
fputs (" error: non-printable character generated.\n", stderr);
}
}
}
(note: these are only a few examples of the classification test you can use. You are free to add or remove as many as you like. You can even provide a lookup-table for non-printable character and output a representation, e.g. '\t', when one is pressed, it's entirely up to you.)
Example Use/Output
The following exercises each of the covered error cases (the '\t' character is used for the non-printable character), e.g.
$ ./bin/fgets-char
Enter a single character: (enter alone to stop): a
you entered 'a'
Enter a single character: (enter alone to stop): b
you entered 'b'
Enter a single character: (enter alone to stop): q
you entered 'q'
Enter a single character: (enter alone to stop): Q
you entered 'Q'
Enter a single character: (enter alone to stop):
error: non-printable character generated.
Enter a single character: (enter alone to stop): foo
error: more than 1 char entered.
Enter a single character: (enter alone to stop):
exiting
There is absolutely nothing wrong with using getc() or fgetc() or getchar() for taking a single-character as input, but you must handle any additional characters that remain unread (including the trailing '\n'). Then what if the user presses ENTER twice in a row, or a cat steps on the keyboard generating 500 keypresses? That's where fgets() can help.
Another approach, unfortunately non-portable between different OS's, is to place the terminal in raw unbuffered (non-cannonical) mode where the input is processed immediately. For Linux you can use tcsetattr(). (you can also use setvbuf, see man 3 setbuf to switch between unbuffered, line-buffered or fully-buffered input) For Windows getch() can be used.
Worth exploring each as you continue your learning in C. Let me know if you have further questions.
stdin, by default, is line oriented when you enter "u\n" so getc() will first return u and in the next call \n.
In this case it is more natural to use a do-while-loop (or a for(;;) with a break at the end of the loop body). Then read a letter in a loop till you find one you like (i.e. not a \n). It's a good idea to handle EOF.
#include <stdio.h>
int main() {
int usr_ch;
do {
printf("Enter a single character: (enter 'q' to stop)\n");
while((usr_ch = getc(stdin)) && usr_ch == '\n');
printf("%d\n", usr_ch != 'q'));
} while(usr_ch != 'q' && usr_ch != EOF);
}
and here is example runs:
Enter a single character: (enter 'q' to stop)
a
1
Enter a single character: (enter 'q' to stop)
q
0
AYou can also just fgets() a line or use scanf(" %c", &usr_ch) to ignore leading white space.
The following piece of code is continuously asking for me to give it 1 input, meaning that when I press "Enter" it does not skip to the next scanf instead just goes to the next line on console and waits for input.
int main()
{
int i, print, line ;
char oFile[50] , iFile[50] = "listsource.c" ;
printf("Please enter the name of the input file: ") ;
scanf("%s", iFile) ;
printf("Please enter 0 to print to console, 1 to print to another file: ") ;
scanf("%d", &print) ;
printf("%s", iFile) ;
}
I am trying to give it a default value of "listsource.c" if no input is entered, have already tried fgets and I have the same problem
Try this:
#include <stdio.h>
int main()
{
char hello[81];
fgets(hello, 80, stdin);
hello[strlen(hello) - 1 ] = '\0';
// OR
gets(hello);
}
It worked with Online C Compiler.
You will have to strip the newline although...gets() will do the work, but is it dangerous as it will read any number of character regardless of the amount specified by you when you declared the variable it is going into. Thus it will overwrite past the memory allocated by the compiler. (Yes, this is allowed by C. In C, the programmer is expected to know what he is doing, so he gets all the powers he wants!)
All scanf() conversion specifiers except "%c", "%[..]" and "%n" ignore leading whitespace. A '\n' is whitespace. Using scanf() you can press Enter until your finger falls off using the "%d" conversion specifier and it will never be read. scanf() will simply discard all whitespace and continue blocking waiting on valid input or EOF.
That is just one, of the many, many pitfalls scanf() has for new C programmers. That is why it is highly recommended that you use fgets() for ALL user input. With a sufficient size buffer (character array), fgets() will consume an entire line-at-a-time (including the trailing '\n'). This greatly simplifies input because what remains in stdin after the user presses Enter does not depend on whether a scanf() matching-failure occurred.
To remove the trailing '\n' from the end of the buffer filled by fgets(), simply use strcspn() as follows:
iFile[strcspn (iFile, "\n")] = 0; /* trim \n from end of iFile */
If you do need to convert the contents of your buffer, simply use sscanf() providing your buffer as the first argument, the remainder just as you would use scanf() -- but on any failure, nothing is left in stdin because you have completely read the user-input with fgets().
If you had attempted to read an int with scanf() and the user slipped and hit 'r' reaching for '4', then a matching-failure occurs and character extraction from stdin ceases leaving 'r' in stdin unread. If you are taking input in a loop -- you have just created an infinite loop...
In your case here asking the user to enter 0 or 1, there is no need for numeric conversion to begin with. Simply read the input into a buffer with fgets() and then check if the first character in the buffer is '0' or '1' (the ASCII digits). No conversion required.
Don't use MagicNumbers (e.g. 50) in your code. If you need a constant, #define one, or use a global enum to accomplish the same thing, e.g.
#define MAXFN 50 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void)
{
char buf[MAXC], /* oFile[MAXFN] ,*/ iFile[MAXFN];
(note: if programming on a microcontroller, reduce the max number of characters for your read-buffer (MAXC) accordingly, otherwise, for general PC use a 1K buffer is fine)
Putting it altogether, and adding a "print to another file - not implemented", to handle the user entering 1 as asked, you could do:
#include <stdio.h>
#include <string.h>
#define MAXFN 50 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void)
{
char buf[MAXC], /* oFile[MAXFN] ,*/ iFile[MAXFN];
fputs ("Please enter the name of the input file: ", stdout);
if (!fgets (iFile, MAXFN, stdin)) { /* read ALL user input with fgets() */
puts ("(user cancled input)"); /* validate, if manual EOF return */
return 0;
}
iFile[strcspn (iFile, "\n")] = 0; /* trim \n from end of iFile */
for (;;) { /* loop continually until valid input from user or EOF */
fputs ("\nPlease enter 0 to print to console, "
"1 to print to another file: ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read ALL user input with fgets() */
puts ("(user cancled input)"); /* validate, if manual EOF return */
return 0;
}
if (*buf == '0') { /* no need to covert to int, just check if ASCII '0' */
puts (iFile);
break;
}
else if (*buf == '1') { /* ditto -- just check if ASCII '1' */
puts ("print to another file - not implemented");
break;
}
fputs (" error: invalid input, not 0 or 1\n", stderr); /* handle error */
}
}
(note: when you need the user to provide specific input, loop continually until you get what you require, or until the user generates a manual EOF by pressing Ctrl + d (or Ctrl + z on windows))
Example Use/Output
Intentionally pressing Enter alone for the first input and providing invalid input for the next two, you would have:
$ ./bin/console_or_file
Please enter the name of the input file: myInputFilename.txt
Please enter 0 to print to console, 1 to print to another file:
error: invalid input, not 0 or 1
Please enter 0 to print to console, 1 to print to another file: bananas
error: invalid input, not 0 or 1
Please enter 0 to print to console, 1 to print to another file: 2
error: invalid input, not 0 or 1
Please enter 0 to print to console, 1 to print to another file: 0
myInputFilename.txt
Look things over and let me know if you have further questions.
I know that adding a space in front of %c in scanf() will scan my second character; however, if two letters were inputted in the first character, it will input the second letter into the second character. How do I scan a single character only?
#include <stdio.h>
int main(void)
{
char firstch, secondch;
printf("Enter your first character: ");
scanf("%c", &firstch);
printf("Enter your second character: ");
scanf(" %c", &secondch);
printf("\n Fisrt character : %c \n Second character : %c \n", firstch, secondch);
return 0;
}
This is my result after running:
Enter your first character: ab
Enter your second character:
First character : a
Second character : b
I only want to read the first character 'a', but the second letter 'b' was inputted right away before I enter my second character.
When you are reading a line of user-input, use a line-oriented input function like fgets() or POSIX getline(). That way the entire line of input is read at once and you can simply take the first character from the line. Say you read a line into the array used as buffer called buf, e.g.
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer to read each line into */
You can simply access the first character as buf[0], or since buf[0] is equivalent to *(but + 0) in pointer notation, you can simply use *buf to get the first character.
As a benefit, since all line-oriented functions read and include the '\n' generated by the user pressing Enter after the input, you can simply check if the first character is '\n' as a way of indicating end-of-input. The user simply presses Enter alone as input to indicate they are done.
Using a line-oriented approach is the recommended way to take user input because it consumes and entire line of input each time and what remains in stdin unread doesn't depend on the scanf conversion specifier or whether a matching failure occurs.
Using " %c%*[^\n]" is not a fix-all. It leaves the '\n' in stdin unread. That's why you need the space before " %c". Where it is insidious is if your next input uses a line-oriented function after your code reading characters is done. Unless you manually empty the '\n' from stdin, before your next attempted line-oriented input, that input will fail because it will see the '\n' as the first character remaining in stdin.
A short example using fgets() for a line-oriented approach would be:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer to read each line into */
for (;;) { /* loop continually */
fputs ("enter char: ", stdout); /* prompt for input */
/* read/validate line, break on EOF or [Enter] alone */
if (!fgets (buf, sizeof buf, stdin) || *buf == '\n')
break;
printf (" got: %c\n\n", *buf); /* output character read */
}
}
Where you simply take input continually isolating the first character as the value you want until the user presses Enter alone to break the read-loop.
Example Use/Output
$ ./bin/fgetschar
enter char: a
got: a
enter char: ab
got: a
enter char: a whole lot of stuff you don't have to deal with using fgets()
got: a
enter char: banannas
got: b
enter char: cantelopes
got: c
enter char:
Look things over and let me know if you have further questions.
Using a space before the %c will skip whitespace before scanning the next non-whitespace character. %c itself just scans a single character -- the next character in the input after whatever else was scanned or skipped previously.
So the question is, what do you want to do? Do you want to skip over all extraneous input on the line after the first character (up to newline?) fgets or scanf("%*[^\n]"); scanf("%c"); will do that (but be careful -- if firstch was itself a newline, this will skip the next line.) Do you want to check the input and make sure it is exactly one character on a line? If so, use fgets (not scanf) and check that the line read is exactly two characters (a character and a newline). Or perhaps you really want to read keystrokes without having the user hit Enter after esch one? That requires changing the input source setup, which is OS dependent.
I'm still new to C coding, and I've found a suitable answer to my problem by using scanf("%*[^\n]");
#include <stdio.h>
int main(void)
{
char firstch, secondch;
printf("Enter your first character: ");
scanf(" %c%*[^\n]", &firstch);
printf("Enter your second character: ");
scanf(" %c%*[^\n]", &secondch);
printf("\n First character : %c \n Second character : %c \n", firstch,
secondch);
return 0;
}
Results after running:
Enter your first character: ab
Enter your second character: c
First character : a
Second character : c
Thanks to #Eraklon #Chris Dodd #David C. Rankin
Basic encryption with input validation, program works as needed if input is within defined parameters requiring user input to advance through each stage, if input is larger than defined parameters, else should trigger saying password is invalid but instead runs through the entire program without user input to trigger advance.
#include <stdio.h> //library containing built in functions for C
#include <stdlib.h> // contains general purpose library for C
#include <string.h> //library containing methods for manipulating strings
int main()
{
// creates character input array of size 5
char input[20];
//initialize variable i
int i;
//initialize variable length
int length;
//prints the phrase
//printf("Your pass phrase is: ");
// used to read input string, gets replaced with fgets to secure input to array length
//fgets(input,11,stdin);
//assigns input to array length
//length = strlen(input);
//prints the phrase
printf("Your pass phrase is: ");
fgets(input,11,stdin);
//assigns input to array length
length = strlen(input);
// if loop that if entered text is longer than 0 but shorter than 6 will encrypt data
if ((length > 0 && length <=10)){
// encrypts array iteratting through elements
for (i=0; i<length; i++)
// uses bitwise xor cipher to encrypt using 0x7f which shifts the characters out of the standard ascii range
input[i] = 0x7F ^ input[i];
// prints the encrypted text in an unreadable format
puts("To see the encrypted text press enter: ");
getchar();
// iterates through the area printing the encrypted characters.
for(i=0;i<length; i++)
putchar(input[i]);
// uses xor cipher to shift data back into array by undoing the shift caused by 0x7f
for(i=0; i<length; i++)
input[i] = 0x7F ^ input[i];
// prints the now readable array
puts("\nTo see recovered text press enter:");
getchar();
//iterates through the array printing the contained characters
for(i=0; i<length; i++)
putchar(input[i]);
}
// prints the following phrase if the array is empty.
else{
puts("User input has been checked and a valid pass phrase was not entered.");
}
return 0;
}
I have tried to decipher what your objective was with the code, and I've read the tea-leaves and arrived at the conclusion (perhaps wrongly) that the crux of your question is aimed at input and branching control that on the encryption itself.
The "Confucius" comment was only half a half-hearted suggestion as the "Never Skimp on Buffer Size" goes to the heart of your ability to control what branches are taken by prompting the user for directions. Why?
If, as in your original code, you have a buffer size of 20, and tell fgets to read at most 10 chars, then if the user enters anything more than 10 chars, the remainder are left in stdin unread.
any characters that are left in stdin unread, will happily be used as input for each of your later getchar() statements causing you to lose complete control over how your code branches.
using an adequately sized buffer and allowing fgets to consume a complete line at a time will ensure there are no characters left unread in stdin (your input buffer). You will find this to be key on taking most user input in C.
So ensuring you have an adequately sized buffer and consume a line of input at each time is fundamental to being able to control where your code goes next.
An alternative to providing a buffer capable of holding each line of input is to read the wanted number of characters and then manually emptying stdin before the next input. That can be as easy as reading characters until a '\n' or EOF is reached. But... there is a catch. You have to know that characters remain before attempting to empty stdin with whatever function you use (e.g. getchar() will block -- waiting for input until input is present to be read). If you attempt to empty stdin in this manner when there is nothing to be emptied, your user will be left staring at a blinking cursor wondering what to do.
Back to your code. The logic is a bit hard to follow, but from the included comments, it appears that you consider a correct passphrase for purposes of encrypting it, a phrase with 1-5 characters. An empty passphrase or a passphrase with 6 or more characters is considered invalid. (it doesn't matter what the actual numbers are from a passphase standpoint -- this is an exercise about input and branching control)
If I understand the 1-5 char (encrypt) or 0 or 6 or more (invalid) logic, then you will want to adjust the structure of your code to follow that a bit closer. From an overview standpoint, you either have a good passphrase to encrypt or you don't. That would control the primary branches in your code. You can provide the logic in a single if {..} else {..} statement, e.g.
/* if input is longer than 0 but shorter than 6, encrypt data */
if (length && length < 6) {
/* all your good pass phrase code goes here */
}
else { /* invalid pass phrase to begin with */
fputs ("error: pass phrase empty or greater than 5 chars.\n", stderr);
return 1;
}
Contrast this above with the multiple independent statement groups you have in your example and it should make sense why you were having trouble with the decision tree in your code.
Now on to your prompts to the user to press Enter. When you attempt to catch that input with a single getchar() it is horribly fragile and subject to being skipped if any characters exist in stdin and it has the same problem of leaving characters in stdin unread if more than Enter is pressed. Here, just declare another character array to use for temporary input the same size as your input[] array. Now instead of getchar(), just use fgets() again even to capture the Enter alone.
(which it is quite capable of doing, since it reads and include the '\n' in the buffer it fills, it will not block waiting on input like getchar() will)
Concerned about size in using 512-bytes in two buffers -- don't be. Most OS's provide a minimum of 1-Meg of stack space, so that 512-bytes represents only 0.048% of the stack storage you have available. (you still have 1048576 - 512 = 1048064 byte of stack space avaialble)
With that change, your code can be rewritten to contain all logic related to handling a good/encrypted password within the first if {...} block, e.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 256 /* if you need a constant, #define one (or more) */
int main (void) {
char input[MAXC], /* buffer to hold pass phrase */
tmp[MAXC]; /* temporary buffer for [enter] input */
size_t i, /* declares an UNINTIALIZED size_t */
length; /* (ditto) */
fputs ("Your pass phrase is: ", stdout); /* no conversion, fputs is fine */
if (!fgets (input, MAXC, stdin)) {
fputs ("(user canceled input)\n", stderr);
return 1;
}
/* remove '\n' by overwriting with '\0' (if present)
* saving length of input in length
*/
input[(length = strcspn(input, "\n"))] = 0;
/* if input is longer than 0 but shorter than 6, encrypt data */
if (length && length < 6) {
int encrypted = 0; /* flag keeping state of if pw encrypted */
for (i = 0; i < length; i++) /* encrypt the pass phrase */
input[i] ^= 0x7f;
encrypted = 1; /* set flag true */
/* print encrypted text in an hex format (change as desired) */
fputs ("\nTo see the encrypted text press [enter]: ", stdout);
if (fgets (tmp, MAXC, stdin) && *tmp == '\n') {
for (i = 0; i < length; i++)
printf (" %02x", input[i]);
putchar ('\n');
}
/* decrypt restoring plain-text pass phrase */
fputs ("\ndecrypted pass phrase, press [enter]: ", stdout);
if (fgets (tmp, MAXC, stdin) && *tmp == '\n') {
for (i = 0; i < length; i++)
input[i] ^= 0x7f;
encrypted = 0; /* set flag false after decryption */
}
else { /* if user pressed any other key (or generated EOF) */
fputs ("error: user chose not to decrypt pass phrase.\n", stderr);
return 1;
}
/* output decrypted plain-text pass pharase (if decrypted) */
fputs ("\nTo see recovered text press [enter]: ", stdout);
if (fgets (tmp, MAXC, stdin) && *tmp == '\n' && !encrypted) {
for (i = 0; i < length; i++)
putchar (input[i]);
putchar ('\n');
}
}
else { /* invalid pass phrase to begin with */
fputs ("error: pass phrase empty or greater than 5 chars.\n", stderr);
return 1;
}
return 0;
}
(note: above the encrypted flag is simply used to hold the state of whether the contents of input[] is currently encrypted or not to provide an additional conditional check before you attempt to print the decrypted passphrase)
Example Use/Output
Valid passphrse case:
$ ./bin/pass0-5
Your pass phrase is: abcde
To see the encrypted text press [enter]:
1e 1d 1c 1b 1a
decrypted pass phrase, press [enter]:
To see recovered text press [enter]:
abcde
User chooses not to decrypt case:
$ ./bin/pass0-5
Your pass phrase is: abcde
To see the encrypted text press [enter]:
1e 1d 1c 1b 1a
decrypted pass phrase, press [enter]: No, I don't want to!
error: user chose not to decrypt pass phrase.
Passphrase too long case:
$ ./bin/pass0-5
Your pass phrase is: abcdef
error: pass phrase empty or greater than 5 chars.
Passphrase empty case:
$ ./bin/pass0-5
Your pass phrase is:
error: pass phrase empty or greater than 5 chars.
I don't know if I read the tea-leaves properly, but from your comments and original question it looked like this area was the one that was actually your focus. Look things over and let me know if you have further questions, and if I got the main gist of your question wrong, let me know and I'm happy to help further.
Im trying to save a 100 character max string into an array and then print an specified character of the array via an index, yet I get Segmentation error 11, here is the code:`
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str1[100];
int index;
printf("Enter text of max 100 characters: \n");
scanf("%s", str1);
printf("Enter the index to search\n");
scanf("%d", &index);
printf("your char is: %c\n", str1[index]);
return(0);
}
`
Any suggestions?
While user input is generally better handled by reading input with fgets and then parsing what you need from the resulting buffer with sscanf of simply with a pair of pointers and "inch-worming down" (a/k/a "walking") the string testing each char and handling as needed -- it is always worth a look at scanf to detail what you need to do to successfully use it for user input.
While fgets is not without needed validations, the number and types of validations needed with scanf and handling of characters that remain in the input buffer in the different cases of input failure or matching failures create a number of extra pitfalls for new (and not so new) C programmers.
The two primary problems with scanf are (1) there is no default limitation on the number of characters that it will read into any buffer (potentially overflowing your array); and (2) the fact that it does not remove the trailing '\n' (or any of the characters following an input or matching failure) from the input buffer (e.g. stdin). It is up to you to account for all characters in the input buffer and empty the buffer as needed.
Further complicating the picture are the ways the different scanf format specifiers handle leading-whitespace (numeric conversions typically skip leading whitespace, while a character conversion won't) Another issue is handling included whitespace. The "%s" format specifier will only read up to the first whitespace encountered, making it impossible to read "My dog has fleas" with a single %s specifier. (you can use a character class to read included whitespace -- as shown in the example below)
There are many other subtleties with scanf as well, so it is well worth the time it takes to read and understand man scanf.
From the comments, you now know if you ask for a string of 100 chars, you need, at minimum, 101 characters of storage -- we will assume that is learned.
When taking any input with scanf, you must always validate the return to insure that the number of conversion expected, in fact took place. For example, if you are reading "5 dogs" with the conversion specifier "%d %s", a return of 2 indicates a successful conversion to integer and string. However, you also know, at minimum a '\n' remains in the input buffer (and potentially many more characters, if say, "5 dogs and cats" were entered. It is up to you to remove the '\n' and any other characters that remain, before attempting to read more input with scanf.
The following example captures most of the pitfalls with your example and provides a couple of tools you can use when dealing with user input. The bottom line is learn to use fgets, but know how to use scanf as well. Your goal is to provide as robust and bullet-proof input routine as you can. Think about all the dumb things a user might do when prompted for input (or heaven forbid, a cat walks across the keyboard) There are always more validations you can add. Look at each of the included validations, and let me know if you have questions:
#include <stdio.h>
#include <string.h>
#define MAXC 100 /* if you need a constant, declare one */
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
char str1[MAXC+1] = ""; /* initialize to all zero */
int index, rtn, len; /* index, return, length */
for (;;) { /* loop until valid input obtained */
printf ("Enter text of max 100 characters: ");
rtn = scanf ("%100[^\n]", str1); /* read at most MAXC char */
if (rtn != 1) { /* validate scanf return */
if (rtn == EOF) { /* check if EOF, ctrl+d, ctrl+z (windoze) */
printf ("input canceled.\n");
return 0;
}
if (!str1[0]) /* was a character entered? */
fprintf (stderr, "error: string is empty.\n");
/* remove '\n' and any chars that remain in stdin */
empty_stdin();
}
else { /* all conditions met, good entry, empty stdin and break */
empty_stdin();
break;
}
}
len = (int)strlen (str1); /* get string length */
for (;;) { /* now do the same thing for integer */
printf ("Enter the index to search (0-%d): ", len - 1);
if ((rtn = scanf ("%d", &index)) != 1) {
if (rtn == EOF) {
printf ("input canceled.\n");
return 0;
}
fprintf (stderr, "error: invalid input - not integer.\n");
/* only need to strip if non-integer entered, because %d
* will skip leading whitespace, including '\n'.
*/
empty_stdin();
}
else if (index < 0 || len < index + 1) /* validate index value */
fprintf (stderr, "error: invalid index - out of range.\n");
else
break;
}
printf ("your char is: %c\n", str1[index]);
return 0;
}
Example Use/Output
$ ./bin/scanfstr1
Enter text of max 100 characters: 12345678901234567890
Enter the index to search (0-19): -1
error: invalid index - out of range.
Enter the index to search (0-19): 0
your char is: 1
$ ./bin/scanfstr1
Enter text of max 100 characters: 12345678901234567890
Enter the index to search (0-19): foo
error: invalid input - not integer.
Enter the index to search (0-19): 6
your char is: 7
$ ./bin/scanfstr1
Enter text of max 100 characters: My dog has fleas.
Enter the index to search (0-16): d
error: invalid input - not integer.
Enter the index to search (0-16): 3
your char is: d
$ ./bin/scanfstr1
Enter text of max 100 characters:
error: string is empty.
Enter text of max 100 characters: My cats are fine.
Enter the index to search (0-16): meow
error: invalid input - not integer.
Enter the index to search (0-16): input canceled.