Why is printf printing more than size of array? - c

char ad[8];
char ct[40];
printf("postal code: ");
scanf(" %[^\n]7s", ad);
printf("city: ");
scanf(" %[^\n]40s", ct);
printf("Address: |%s|%s\n", ad, ct);
sample input for ad: m2r 3r3 t4t .
output should be: m2r 3r3 .
but
output is: m2r 3r3 t4t

I've had a lot more luck with fgetc than with scanf although it needs some additional code to work smoothly, this just emphasizes what a hassle user entry can be:
char ad[8];
int maxlen = 7; //must be 1 less than array size for terminator
char a = '0';
int count = 0;
while (a != '\n' && count < maxlen) { //read until newline or size limit
a = fgetc(stdin);
ad[count] = a;
count++;
}
if (a == '\n') { //case: user entry < maxlen
buffer[count - 1] = '\0'; //get rid of newline char
}
else { //case: user entry >= maxlen
buffer[count] = '\0'; //terminate the string
do {
a = fgetc(stdin);
} while (a != '\n') //safely flush stdin so next entry works
}
Granted this looks like a ridiculous amount of code but you can stick it in a reusable ```getEntry`` function. Also, just avoid user entry if at all possible especially when building your code. The above so far seems fairly foolproof, at least. It's what I'm now using so any bug-spotting is more than welcome.

If your intention is to read 7 characters and discard the rest of the input you can do:
char ad[8];
char ct[40];
printf("postal code: ");
scanf("%7s%*[^\n]", ad);
printf("city: ");
scanf("%40s%*[^\n]", ct);
printf("Address: |%s|%s\n", ad, ct);
The %7s will read 7 characters. And the %*[^\n] will read until the end of the line and discard the result.
And just to emphasize, I'm not advocating to use scanf() but if you really want to the above code will do the trick.

Related

Is there an elegant way to handle the '\n' that gets read by input functions (getchar(), fgets(), scanf()) in C?

I am trying a simple exercise from K&R to append string2 at the end of string1 using pointers. In case of overflow i.e. buffer of string1 can't contain all of string2 I want to prompt the user to re-enter string2 or exit.
I have written the following code:
#include<stdio.h>
#include<string.h>
#define MAXLINE 1000
int get_input(char *s);
int str_cat(char *s, char *t);
void main()
{
char input1[MAXLINE], input2[MAXLINE], c;
get_input(input1);
check:
get_input(input2);
if((strlen(input1) + strlen(input2) + 2) <= MAXLINE)
{
str_cat(input1, input2);
printf("%s\n", input1);
}
else
{
input2[0] = '\0';
printf("String overflow\n Press: \n 1: Re-enter string. \n 2: Exit.\n");
scanf(" %d", &c);
if(c == 1){
input2[0] = '\0';
get_input(input2);
goto check;
}
}
}
int get_input(char *arr)
{
int c;
printf("Enter the string: \n");
while(fgets(arr, MAXLINE, stdin))
{
break;
}
}
int str_cat(char *s, char *t)
{
while(*s != '\0')
{
s++;
}
while((*s++ = *t++) != '\0')
{
;
}
*s = '\0';
}
Initially, I was using the standard getchar() function mentioned in the book to read the input in get_input() which looked like this:
int get_input(char *arr)
{
int c;
printf("Enter the string: \n");
while((c = getchar()) != '\n' && c != EOF)
{
*arr++ = c;
}
*arr = '\0';
}
I am new and I read this and understood my mistake. I understand that one isn't supposed to use different input functions to read stdin and the '\n' is left in the input stream which is picked by the getchar() causing my condition to fail.
So, I decided to use fgets() to read the input and modified the scanf("%d", &c) as mentioned in the thread with scanf(" %d", c). This does work (kinda) but gives rise to behaviors that I do not want.
So, I have a few questions:
What's a better way to fgets() from reading the input on encountering '\n' than the one I have used?
while(fgets(arr, MAXLINE, stdin))
{
break;
}
fgets() stops reading the line and stores it as an input once it either encounters a '\n' or EOF. But, it ends up storing the '\n' at the end of the string. Is there a way to prevent this or do I have to over-write the '\n' manually?
Even though I used the modified version of scanf(" %d", &c), my output looks like
this: (https://imgur.com/a/RaC2Kc6). Despite that I get Enter the string: twice when prompted to re-enter the second string in case of an overflow situation. Is the modified scanf() messing with my input? And how do I correct it?
In general, do not mix fgets with scanf. Although it may be a bit bloaty, you will avoid many problems by being consistent with reading input with fgets and then parse it with sscanf. (Note the extra s)
A good way to remove the newline is buffer[strcspn(buffer, "\n")] = 0
Example:
// Read line and handle error if it occurs
if(!fgets(buffer, buffersize, stdin)) {
// Handle error
}
// Remove newline (if you want, not necessarily something you need)
buffer[strcspn(buffer, "\n")] = 0;
// Parse and handle error
int val;
if(sscanf(buffer, "%d", &val) != 1) {
// Handle error
}
// Now you can use the variable val
There is one thing here that might be dangerous in certain situations, and that is if buffer is not big enough to hold a complete line. fgets will not read more than buffersize characters. If the line is longer, the remaining part will be left in stdin.

User-Defined function for reading input not working

I've made a user-defined function for reading input and replacing newline character '\n' with '\0' so when I use printf statement for printing the string it won't add newline at the end.
char xgets(char *line, int size, FILE *stdn)
{
//READS THE LINE
fgets(line, size, stdn);
//REMOVES NEWLINE CHARACTER '\n' AND ADDS '\0'
line[strcspn(line, "\n")] = '\0';
return line;
}
When I call xgets inside main() function it works properly, but when it is called in other user-defined function it does not wait for user-input.
I'm using Visual Studio 2015 for debugging my code.
Here's my code:
#include<stdio.h>
#include<stdlib.h>
#include<process.h>
//USER-DEFINED FUNCTION
char xgets(char *line, int size, FILE *stdn);
void sortm_hgrade();
void sortm_rcharge();
void header(void);
void header(void)
{
printf("*-*-*-*-*HOTEL_INFO*-*-*-*-*");
printf("\n\n");
}
char xgets(char *line, int size, FILE *stdn)
{
//READS THE LINE
fgets(line, size, stdn);
//REMOVES NEWLINE CHARACTER '\n' AND ADDS '\0' END LINE CHARACTER
line[strcspn(line, "\n")] = '\0';
return line;
}
#define MAX 1000
//PROGRAMS STARTS HERE
int main(void)
{
//VARIABLE-DECLARATION
int i = 0, j = 0, n = 0;
char line[MAX] = { 0 };
char o = { 0 };
char h[10] = { 0 };
//FUCNTION CALL-OUT
header();
printf("Type anything : ");
xgets(h, sizeof(h), stdin);
printf("Enter one option from the following : \n\n");
printf("(a) To Print out Hotels of a given Grade in order of charges. \n");
printf("(b) To Print out Hotels with Room Charges less than a given Value. \n");
printf("Please type a proper option. \n");
while (n == 0){
scanf_s(" %c", &o);
switch (o){
case 'a':
sortm_hgrade();
n = 1;
break;
case 'b':
sortm_rcharge();
n = 1;
break;
default:
printf("Option INVALID \n");
printf("Please type a proper option \n");
n = 0;
break;
}
}
//TERMINAL-PAUSE
system("pause");
}
void sortm_hgrade()
{
//FOR SORTING BY GRADE
char g[10] = { 0 };
printf("Enter the Grade : ");
xgets(g, sizeof(g), stdin);
printf("\n");
}
void sortm_rcharge()
{
printf("----");
}
You should change
scanf(" %c", &o);
to
scanf("%c ", &o);
This force scanf to consume trailing chars, like '\n'
In your code '\n' of user input for scanf %c is not consumed and it is consumed by fgets in your xgets function that exit immediately with an empty buffer.
BTW that solution can wok only if a single char is input by user.
Best code would be
char c;
while (n == 0)
{
o = getchar();
while ((c = getchar()) != EOF && c != '\n') ;
EDIT
With the second solution code is waiting, and discarding, chars until a '\n' is triggered or end of file. In your specific case (using stdin as console) EOF is not mandatory. It will be mandatory in case of input is being read from a "real file".
You need to skip the \n character after you take in a character. you can command scanf for that. fgets reads that newline character up first and then hence it terminates. use this
scanf(" %c *[^\n]", &o);
This should do the trick

C: Clearing STDIN

basically in codeblocks for windows before each printf I have "fflush(stdin);" which works. When I copied my code to Linux, it doesn't work, nor does any of the alternatives for "fflush(stdin);" that I've found. No matter which way I seem to do it, the input doesn't seem to be clearing in the buffer or something in my code is incorrect.
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <ctype.h>
int main()
{
char pbuffer[10], qbuffer[10], kbuffer[10];
int p=0, q=0, k=0;
int r, i, Q, count, sum;
char a[3];
a[0]='y';
while(a[0]=='y' || a[0]=='Y')
{
printf("Enter a p value: \n");
fgets(pbuffer, sizeof(pbuffer), stdin);
p = strtol(pbuffer, (char **)NULL, 10);
printf("Enter a q value: \n");
fgets(qbuffer, sizeof(qbuffer), stdin);
q = strtol(qbuffer, (char **)NULL, 10);
printf("Enter a k value: \n");
fgets(kbuffer, sizeof(kbuffer), stdin);
k = strtol(kbuffer, (char **)NULL, 10);
while(p<q+1)
{
Q=p;
sum=0;
count=0;
while(Q>0)
{
count++;
r = Q%10;
sum = sum + pow(r,k);
Q = Q/10;
}
if ( p == sum && i>1 && count==k )
{
printf("%d\n",p);
}
p++;
a[0]='z';
}
while((a[0]!='y') && (a[0]='Y') && (a[0]!='n') && (a[0]!='N'))
{
printf("Would you like to run again? (y/n) ");
fgets(a, sizeof(a), stdin);
}
}
return 0;
}
Calling fflush(stdin) is not standard, so the behavior is undefined (see this answer for more information).
Rather than calling fflush on stdin, you could call scanf, passing a format string instructing the function to read everything up to and including the newline '\n' character, like this:
scanf("%*[^\n]%1*[\n]");
The asterisk tells scanf to ignore the result.
Another problem is calling scanf to read a character into variable a with the format specifier of " %s": when the user enters a non-empty string, null terminator creates buffer overrun, causing undefined behavior (char a is a buffer of one character; string "y" has two characters - {'y', '\0'}, with the second character written past the end of the buffer). You should change a to a buffer that has several characters, and pass that limit to scanf:
char a[2];
do {
printf("Would you like to run again? (y/n) \n")
scanf("%1s", a);
} while(a[0] !='y' && a[0] !='Y' && a[0]!='n' && a[0]!='N' );
}
I think what you are trying to do is more difficult than it seems.
My interpretation of what you are trying to do is disable type ahead so that if the user types some characters while your program is processing other stuff, they don't appear at the prompt. This is actually quite difficult to do because it is an OS level function.
You could do a non blocking read on the device before printing the prompt until you get EWOULDBLOCK in errno. Or the tcsetattr function family might help. It looks like there is a way to drain input for a file descriptor in there, but it might interact badly with fgets/fscanf
A better idea is not to worry about it at all. Unix users are used to having type ahead and what you want would be unexpected behaviour for them.
Drop the need for flushing the input buffer.
OP is on the right track using fgets() rather than scanf() for input, OP should continue that approach with:
char a;
while(a !='y' && a !='Y' && a!='n' && a!='N' ) {
printf("Would you like to run again? (y/n) \n");
if (fgets(kbuffer, sizeof(kbuffer), stdin) == NULL)
Handle_EOForIOerror();
int cnt = sscanf(kbuffer, " %c", &a); // Use %c, not %s
if (cnt == 0)
continue; // Only white-space entered
}
Best to not use scanf() as it tries to handle user IO and parsing in one shot and does neither that well.
Certain present OP's woes stem from fgets() after scanf(" %s", &a); (which is UB as it should be scanf(" %c", &a);. Mixing scanf() with fgets() typically has the problem that the scanf(" %c", &a); leaves the Enter or '\n' in the input buffer obliging the code to want to flsuh the input buffer before the next fgets(). Else that fgets() gets the stale '\n' and not a new line of info.
By only using fgets() for user IO, there need for flushing is negated.
Sample fgets() wrapper
char *prompt_fgets(const char *prompt, char dest, long size) {
fputs(prompt, stdout);
char *retval = fgets(dest, size, stdin);
if (retval != NULL) {
size_t len = strlen(dest);
if (len > 1 && dest[len-1] == '\n') { // Consume trailing \n
dest[--len] = '\0';
}
else if (len + 1 == dest) { // Consume extra char
int ch;
do {
ch == fgetc(stdin);
} while (ch != '\n' && ch != EOF);
}
return retval;
}

Tokenizing a string

I am in the process of writing a C program that parses a string and tokenizing it by breaking the string characters into words that are seperated by white space. My question is when i run my current program:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char input[20];
printf("Please enter your word:\n");
scanf("%c", &input);
printf("%c", input[1]);
return 0;
}
If i was to enter the word "This", i would expect to get back "h" when i run the program but instead i get a downwards pointing arrow. However, when the input is set to print out input[0] i get back a "T".
Edit: I have modified my code so that it prints out the whole string now which i will show below
int main()
{
char input[20];
printf("Please enter your words:\n");
scanf("%s", input);
printf("%s", input);
return 0;
}
My goal is to be able to break that string into chars that i can search through to find whitespace and thus being able to isolate those words for example, if my input was "This is bad" i'd like the code to print out
This
is
bad
Edit:
I have modified my code to fit one of these answers but the problem i run into now is that it won't compile
int main()
{
char input[20];
printf("Please enter your words:\n");
size_t offset = 0;
do
{
scanf("%c", input + offset);
offset++;
}
while(offset < sizeof(input) && input[offset - 1] != '\n');
}
printf("%c", input[]);
return 0;
Problems:
1) scanf("%c", input); only set the first element of the array input.
2) printf("%c", input[1]); prints the second element of the array input, which has uninitialized data in it.
Solution:
Small state machine. No limit on string size like 20.
#include <ctype.h>
#include <stdio.h>
int main() {
int ch = fgetc(stdin);
while (ch != EOF) {
while (isspace(ch)) {
// If only 1 line of input allowed, then add
if (ch == '\n') return 0;;
ch = fgetc(stdin);
}
if (ch != EOF) {
do {
fputc(ch, stdout);
ch = fgetc(stdin);
} while (ch != EOF && !isspace(ch));
fputc('\n', stdout);
}
}
return 0;
}
scanf("%c", &input); does not do what you think it does.
First of all, %c scans only a single character: http://www.cplusplus.com/reference/cstdio/scanf/
Second, array's name is already a pointer to it's first element, so stating &input you make a pointer to a pointer, so instead of storing your character in array's first element you store it in pointer to the array which is a very bad thing.
If you really want to use scanf, I recommend a loop:
size_t offset = 0;
do
{
scanf("%c", input + offset);
offset++;
}
while(offset < sizeof(input) && input[offset - 1] != '\n');
Using scanf("%s", input") leaves you vulnerable to buffer overflow attacks if the word is longer than 20 characters http://en.wikipedia.org/wiki/Buffer_overflow
In my example I assumed, that you want to finish your word with a newline character.
EDIT: In scanf documentation is also a good example:
scanf("%19s", input);
It scans no more than 19 characters, which also prevent buffer overflow. But if you want to change input size, you have to change it two places.
You can use
char * strtok ( char * str, const char * delimiters );
to tokenize your string. If you have your input in input[] array and want to tokenize the string accoring to whitespace character, you can do the following :
char *ptr;
ptr = strtok(input, " ");
while(ptr != NULL) {
printf("%s\n", ptr);
ptr = strtok(NULL, " ");
}
Only the first call to strtok() requires the character array as input. Specifying NULL in the next calls means that it will operate on the same character array.
Your scanf only picks up the first character, input[1] contains random garbage. Use scanf("%19s", input) instead.

How to prevent the user from entering more data than the maximum limit?

This code asks the user for data and subsequently a number:
$ cat read.c
#include<stdio.h>
#include<stdlib.h>
#define MAX 10
int main() {
char* c = (char*) malloc(MAX * sizeof(char));
int num;
printf("Enter data (max: %d chars):\n", MAX);
fgets(c, MAX, stdin);
// how do I discard all that is there on STDIN here?
printf("Enter num:\n");
scanf("%d", &num);
printf("data: %s", c);
printf("num: %d\n", num);
}
$
The problem is that apart from the instruction that states the maximum number of chars, there is nothing that stops the user from entering more, which is subsequently read into num as junk:
$ ./read
Enter data (max 10 chars):
lazer
Enter num:
5
data: lazer
num: 5
$ ./read
Enter data (max 10 chars):
lazerprofile
Enter num:
data: lazerprofnum: 134514043
$
Is there a way to discard all that is there on STDIN after the fgets call?
The scanf() function is terrible for user input, and it's not that great for file input unless you somehow know your input data is correct (don't be that trusting!) Plus, you should always check the return value for fgets() since NULL indicates EOF or some other exception. Keep in mind that you get the user's newline character at the end of your fgets() data unless the maximum is reached first. I might do it this way as a first pass:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 10
void eat_extra(void) {
int ch;
// Eat characters until we get the newline
while ((ch = getchar()) != '\n') {
if (ch < 0)
exit(EXIT_FAILURE); // EOF!
}
}
int main() {
char c[MAX+1]; // The +1 is for the null terminator
char n[16]; // Arbitrary maximum number length is 15 plus null terminator
int num;
printf("Enter data (max: %d chars):\n", MAX);
if (fgets(c, MAX, stdin)) { // Only proceed if we actually got input
// Did we get the newline?
if (NULL == strchr(c, '\n'))
eat_extra(); // You could just exit with "Too much data!" here too
printf("Enter num:\n");
if (fgets(n, sizeof(n) - 1, stdin)) {
num = atoi(n); // You could also use sscanf() here
printf("data: %s", c);
printf("num: %d\n", num);
}
}
return 0;
}
To my knowledge, the only portable solution is to exhaust the buffer yourself:
while (getchar() != EOF);
Note that fflush(stdin); is not the answer.
EDIT: If you only want to discard characters until the next newline, you can do:
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
What "can happen" to fgets?
it returns NULL when there is an error in input
it returns NULL when it finds an EOF before any "real" characters
it returns the pointer to the buffer
the buffer wasn't completely filled
the buffer was completely filled but there is no more data in input
the buffer was completely filled and there is more data in input
How can you distinguish between 1 and 2?
with feof
How can you distinguish between 3.1., 3.2. and 3.3.
By determining where the terminating null byte and line break were written:
If the output buffer has a '\n' then there is no more data (the buffer may have been completely filled)
If there is no '\n' AND the '\0' is at the last position of the buffer, then you know there is more data waiting; if the '\0' is before the last position of the buffer, you've hit EOF in a stream that doesn't end with a line break.
like this
/* fgets fun */
/*
char buf[SOMEVALUE_LARGERTHAN_1];
size_t buflen;
*/
if (fgets(buf, sizeof buf, stdin)) {
buflen = strlen(buf);
if (buflen) {
if (buf[buflen - 1] == '\n') {
puts("no more data (3.1. or 3.2.)"); /* normal situation */
} else {
if (buflen + 1 == sizeof buf) {
puts("more data waiting (3.3.)"); /* long input line */
} else {
puts("EOF reached before line break (3.1.)"); /* shouldn't happen */
}
}
} else {
puts("EOF reached before line break (3.1.)"); /* shouldn't happen */
}
} else {
if (feof(stdin)) {
puts("EOF reached (2.)"); /* normal situation */
} else {
puts("error in input (1.)");
}
}
The usual, incomplete tests, are buf[buflen - 1] == '\n' and checking fgets return value ...
while (fgets(buf, sizeof buf, stdin)) {
if (buf[strlen(buf) - 1] != '\n') /* deal with extra input */;
}
I would read the data and then check it for user error:
bool var = true;
while var {
printf("Enter data (max: %d chars):\n", MAX);
fgets(c, MAX, stdin);
// how do I discard all that is there on STDIN here?
if(strlen(c) <= 10)
var = false;
else
printf("Too long, try again! ");
}
On the other hand, if you don't want to do this, just read num twice and discard the first one.

Resources