This seems like it should be a simple thing but after hours of searching I've found nothing...
I've got a function that reads an input string from stdin and sanitizes it. The problem is that when I hit enter without typing anything in, it apparently just reads in some junk from the input buffer.
In the following examples, the prompt is "input?" and everything that occurs after it on the same line is what I type. The line following the prompt echoes what the function has read.
First, here is what happens when I type something in both times. In this case, the function works exactly as intended.
input? abcd
abcd
input? efgh
efgh
Second, here is what happens when I type something in the first time, but just hit enter the second time:
input? abcd
abcd
input?
cd
And here is what happens when I just hit enter both times:
input?
y
input?
y
It happens to return either 'y' or '#' every time when I run it anew. 'y' is particularly dangerous for obvious reasons.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#define STRLEN 128
int main() {
char str[STRLEN];
promptString("input?", str);
printf("%s\n", str);
promptString("input?", str);
printf("%s\n", str);
return EXIT_SUCCESS;
}
void promptString(const char* _prompt, char* _writeTo) {
printf("%s ", _prompt);
fgets(_writeTo, STRLEN, stdin);
cleanString(_writeTo);
return;
}
void cleanString(char* _str) {
char temp[STRLEN];
int i = 0;
int j = 0;
while (_str[i] < 32 || _str[i] > 126)
i++;
while (_str[i] > 31 && _str[i] < 127) {
temp[j] = _str[i];
i++;
j++;
}
i = 0;
while (i < j) {
_str[i] = temp[i];
i++;
}
_str[i] = '\0';
return;
}
I've tried various methods (even the unsafe ones) of flushing the input buffer (fseek, rewind, fflush). None of it has fixed this.
How can I detect an empty input so that I can re-prompt, instead of this annoying and potentially dangerous behavior?
This part of cleanString
while (_str[i] < 32 || _str[i] > 126)
i++;
jumps over \0 when the string is empty.
You should add _str[i] != '\0' into the loop's condition.
To detect an empty string, simply check it's length just after the input:
do {
printf("%s ", _prompt);
fgets(_writeTo, STRLEN, stdin);
} while (strlen(_writeTo) < 2);
(comparing with two because of '\n' which fgets puts into the end of buffer)
Why do you have a bunch of variable names with leading underscores? That's nasty.
Anyway, the first thing you must do is check the return value of fgets. If it returns NULL, you didn't get any input. (You can then test feof or ferror to find out why you didn't get input.)
Moving on to cleanString, you have a while loop that consumes a sequence of non-printable characters (and you could use isprint for that instead of magic numbers), followed by a while loop that consumes a sequence of printable characters. If the input string doesn't consist of a sequence of non-printables followed by a sequence of printables, you will either consume too much or not enough. Why not use a single loop?
while(str[i]) {
if(isprint(str[i]))
temp[j++] = str[i];
++i;
}
This is guaranteed to consume the whole string until the \0 terminator, and it can't keep going past the terminator, and it copies the "good" characters to temp. I assume that's what you wanted.
You don't even really need to use a temp buffer, you could just copy from str[i] to str[j], since j can never get ahead of i you'll never be overwriting anything that you haven't already processed.
Related
I use an fgets to read from stdin a line and save it in a char array, I would like to get the last letter of the line i wrote , which should be in the array before \nand \0.
For example if i have a char line[10] and write on the terminal 1stLine, is there a fast way to get the letter e rather than just cycling to it?
I saw this post How do I print the last element of an array in c but I think it doesn't work for me, even if I just create the array without filling it with fgets , sizeof line is already 10 because the array already has something in it
I know it's not java and I can't just .giveMeLastItem(), but I wonder if there is a smarter way than to cycle until the char before the \n to get the last letter I wrote
code is something like
char command[6];
fgets(command,6,stdin);
If you know the sentinel value, ex: \0 (or \n ,or any value for that matter), and you want the value of the element immediately preceding to that, you can
use strchr() to find out the position of the sentinel and
get the address of retPtr-1 and dereference to get the value you want.
There are many different ways to inspect the line read by fgets():
first you should check the return value of fgets(): a return value of NULL means either the end of file was reached or some sort of error occurred and the contents of the target array is undefined. It is also advisable to use a longer array.
char command[80];
if (fgets(command, sizeof command, stdin) == NULL) {
// end of file or read error
return -1;
}
you can count the number of characters with len = strlen(command) and if this length os not zero(*), command[len - 1] is the last character read from the file, which should be a '\n' if the line has less than 5 bytes. Stripping the newline requires a test:
size_t len = strlen(command);
if (len > 0 && command[len - 1] == '\n')
command[--len] = '\0';
you can use strchr() to locate the newline, if present with char *p strchr(command, '\n'); If a newline is present, you can strip it this way:
char *p = strchar(command, '\n');
if (p != NULL)
*p = '\0';
you can also count the number of characters no in the set "\n" with pos = strcspn(command, "\n"). pos will point to the newline or to the null terminator. Hence you can strip the trailing newline with:
command[strcspn(command, "\n")] = '\0'; // strip the newline if any
you can also write a simple loop:
char *p = command;
while (*p && *p != '\n')
p++;
*p = '\n'; // strip the newline if any
(*) strlen(command) can return 0 if the file contains an embedded null character at the beginning of a line. The null byte is treated like an ordinary character by fgets(), which continues reading bytes into the array until either size - 1 bytes have been read or a newline has been read.
Once you have only the array, there is no other way to do this. You could use strlen(line) and then get the last characters position based on this index, but this basically does exactly the same (loop over the array).
char lastChar = line[strlen(line)-1];
This has time-complexity of O(n), where n is the input length.
You can change the input method to a char by char input and count the length or store the last input. Every O(1) method like this uses O(n) time before (like n times O(1) for every character you read). But unless you have to really speed optimize (and you don't, when you work with user input) should just loop over the array by using a function like strlen(line) (and store the result, when you use it multiple times).
EDIT:
The strchr() function Sourav Ghosh mentioned, does exactly the same, but you can/must specify the termination character.
A straightforward approach can look the following way
char last_letter = command[ strcspn( command, "\n" ) - 1 ];
provided that the string is not empty or contains just the new line character '\n'.
Here is a demonstrative progarm.
#include <stdio.h>
#include <string.h>
int main(void)
{
enum { N = 10 };
char command[N];
while ( fgets( command, N, stdin ) && command[0] != '\n' )
{
char last_letter = command[ strcspn( command, "\n" ) - 1 ];
printf( "%c ", last_letter );
}
putchar( '\n' );
return 0;
}
If to enter the following sequence of strings
Is
there
a
quick
way
to
get
the
last
element
that
was
put
in
an
array?
then the output will be
s e a k y o t e t t t s t n n ?
The fastest way is to keep an array of references like this:
long ref[]
and ref[x] to contain the file offset of the last character of the xth line. Having this reference saved at the beginning of the file you will do something like:
fseek(n*sizeof(long))
long ref = read_long()
fseek(ref)
read_char()
I think this is the fastest way to read the last character at the end of the nth line.
I did a quick test of the three mentioned methods of reading a line from a stream and measuring its length. I read /usr/share/dict/words 100 times and measured with clock()/1000:
fgets + strlen = 420
getc = 510
fscanf with " 100[^\n]%n" = 940
This makes sense as fgets and strlen just do 2 calls, getc does a call per character, and fscanf may do one call but has a lot of machinery to set up for processing complex formats, so a lot more overhead. Note the added space in the fscanf format to skip the newline left from the previous line.
Beside the other good examples.
Another way is using fscanf()/scanf() and the %n format specifier to write to an argument the amount of read characters so far after you have input the string.
Then you subtract this number by one and use it as an index to command:
char command[6];
int n = 0;
if (fscanf(stdin, "%5[^\n]" "%n", command, &n) != 1)
{
fputs("Error at input!", stderr);
// error routine.
}
getchar();
if (n != 0)
{
char last_letter = command[n-1];
}
#include <stdio.h>
int main (void)
{
char command[6];
int n = 0;
if (fscanf(stdin, "%5[^\n]" "%n", command, &n) != 1)
{
fputs("Error at input!", stderr);
// error routine.
}
getchar();
if (n != 0)
{
char last_letter = command[n-1];
putchar(last_letter);
}
return 0;
}
Execution:
./a.out
hello
o
So far I have been using if statements to check the size of the user-inputted strings. However, they don't see to be very useful: no matter the size of the input, the while loop ends and it returns the input to the main function, which then just outputs it.
I don't want the user to enter anything greater than 10, but when they do, the additional characters just overflow and are outputted on a newline. The whole point of these if statements is to stop that from happening, but I haven't been having much luck.
#include <stdio.h>
#include <string.h>
#define SIZE 10
char *readLine(char *buf, size_t sz) {
int true = 1;
while(true == 1) {
printf("> ");
fgets(buf, sz, stdin);
buf[strcspn(buf, "\n")] = 0;
if(strlen(buf) < 2 || strlen(buf) > sz) {
printf("Invalid string size\n");
continue;
}
if(strlen(buf) > 2 && strlen(buf) < sz) {
true = 0;
}
}
return buf;
}
int main(int argc, char **argv) {
char buffer[SIZE];
while(1) {
char *input = readLine(buffer, SIZE);
printf("%s\n", input);
}
}
Any help towards preventing buffer overflow would be much appreciated.
When the user enters in a string longer than sz, your program processes the first sz characters, but then when it gets back to the fgets call again, stdin already has input (the rest of the characters from the user's first input). Your program then grabs another up to sz characters to process and so on.
The call to strcspn is also deceiving because if the "\n" is not in the sz chars you grab than it'll just return sz-1, even though there's no newline.
After you've taken input from stdin, you can do a check to see if the last character is a '\n' character. If it's not, it means that the input goes past your allowed size and the rest of stdin needs to be flushed. One way to do that is below. To be clear, you'd do this only when there's been more characters than allowed entered in, or it could cause an infinite loop.
while((c = getchar()) != '\n' && c != EOF)
{}
However, trying not to restructure your code too much how it is, we'll need to know if your buffer contains the newline before you set it to 0. It will be at the end if it exists, so you can use the following to check.
int containsNewline = buf[strlen(buf)-1] == '\n'
Also be careful with your size checks, you currently don't handle the case for a strlen of 2 or sz. I would also never use identifier names like "true", which would be a possible value for a bool variable. It makes things very confusing.
In case that string inside the file is longer that 10 chars, your fgets() reads only the first 10 chars into buf. And, because these chars doesn't contain the trailing \n, function strcspn(buf, "\n") returns 10 - it means, you are trying to set to 0 an buf[10], so it is over buf[] boundaries (max index is 9).
Additionally, never use true or false as the name of variable - it totally diminishes the code. Use something like 'ok' instead.
Finally: please clarify, what output is expected in case the file contains string longer than 10 characters. It should be truncated?
I am not able to find out the reason for the misbehavior of the below code. This is a simple code to accept characters until either * is entered or until array size is reached and then print the characters read from the keyboard. It looks like the reading part is fine. Also if I enter * before array size is reached everything is OK. But if I do not enter * and wait until array size is reached in reading portion, I have the trouble. While printing it prints the characters read, but after that some garbage is printed. Ran through debugger, but while loop is not breaking when index is 3 or more.
int main()
{
char myStr [3];
unsigned int index=0;
printf("Enter Single characters. Enter * to stop\n");
do
{
scanf(" %c",&myStr[index]);
index++;
} while ((myStr[index-1]!='*')&&((index)<(sizeof(myStr)/sizeof(myStr[0]))));
index=0;
while ((myStr[index]!='*')&&(index<(sizeof(myStr)/sizeof(myStr[0]))))
{
printf("%c",myStr[index]);
index++;
}
printf("\n");
return(0);
}
The code runs into undefined behaviour on the printf loop's last iteration here
while ((myStr[index]!='*')&&(index<(sizeof(myStr)/sizeof(myStr[0]))))
{
...
as it in fact is doing
while ((myStr[3] ....
with myStr[3] accessing myStr out-of-bounds.
To fix this do:
while ((index < (sizeof(myStr)/sizeof(myStr[0]))) && (myStr[index] != '*'))
Boolean short-circuiting will take care of myStr[3] not being executed.
You are manipulating strings, witch need to be null-terminated.
You should use fgets(3) to get your string and then strlen(3) to get the length.
Then you can move in your string by
while (str[i] != '*' && i < strlen(str))
good luck
I'm developing a code where the user will type several paragraphs and it will stop reading when the user begin a paragraph with "END". The code will manipulate the string by counting each letter and showing a graph and blah blah blah, but this is irrelevant to the question.
The thing is: which paragraph must have no more than 1000 characters.
A smaller version of the code is the following (considering I just want to storage 5-char-string - even though I'll expand that).
#include <stdio.h>
#include <string.h>
int main()
{
char paragraph[5];
for ( ; ; )
{
fgets(paragraph, 5, stdin);
if (paragraph[0]=='E' && paragraph[1]=='N' && paragraph[2]=='D')
{ return 0; }
printf("%s", paragraph);
}
return 0;
My problem is: if I type more than 5 characters, the printf function still prints more than 5 characters, I don't know why. I've already checked everything I could possible check.
Help a beginner like me, please.
fgets() reads in at most one less than size characters from stream and
stores them into the buffer pointed to by s. Reading stops after an
EOF or a newline. If a newline is read, it is stored into the buffer.
A terminating null byte ('\0') is stored after the last character in
the buffer.
So when entering more than 4 characters (newline included) only 4 is read and the rest stays in the buffer ready to be read next fgets.
Your printf will not print any newline in this case and will be called multiple times, making it look like printing more than 4 characters.
As suggested in comments, try printf("[%s]", paragraph); to see the individual printf calls.
You should use strstr in string.h because it's cleaner.
if (strstr(paragraph, "END"))
return 0;
instead of
if (paragraph[0]=='E' && paragraph[1]=='N' && paragraph[2]=='D')
return 0;
Try modifying your code in the following way and you'll immediately see what actually happens with the fgets() function when you enter more characters than the size of your buffer. It doesn't read from the keyboard, but from the stdinbuffer. These SO posts may also be interesting for you to read:(1), (2). Enjoy the demo and be sure to thoroughly read the man pages.
#include <stdio.h>
int main()
{
char paragraph[5];
for ( ; ; )
{
printf("Enter the string: \n\t");
if(fgets(paragraph, 5, stdin) != NULL)
printf("%s\n", paragraph);
if (paragraph[0]=='E' && paragraph[1]=='N' && paragraph[2]=='D')
return 0;
}
return 0;
}
I have a problem with reading empty string in C. I want to read string from the following -
ass
ball
(empty)
cat
but when I use gets() it does not treat (empty) as string[2]. It reads 'cat' as string[2]. So how can I solve this problem?
char str1[15002][12];
char str2[15002][12];
char s[25];
map<string,int> Map;
int main()
{
int ncase, i, j, n1, n2, count, Case;
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&ncase);
Case = 1;
while(ncase > 0)
{
Map.clear();
//this is the necessery part
scanf("%d %d\n",&n1,&n2);
count = 0;
printf("n1=%d n2=%d\n",n1,n2);
for(i = 0; i < n1; i++)
{
gets(str1[i]);
}
for(i = 0; i < n2; i++)
{
gets(str2[i]);
}
//end of reading input
for(i = 0; i < n1; i++)
{
for(j = 0; j < n2; j++)
{
strcpy(s,str1[i]);
strcat(s,str2[j]);
if(Map[s] == 0){
count += 1;
Map[s] = 1;
}
}
}
printf("Case %d: %d\n", Case, count);
Case++;
ncase--;
}
return 0;
}
and input can look like
I have given the code here. The input may be like
line1>1
line2>3 3
line3>(empty line)
line4>a
line5>b
line6>c
line7>(empty)
line8>b
And I expect
str1[0]=(empty).
str1[1]=a;
str1[2]=b;
and
str2[0]=c;
str2[1]=(empty);
str2[2]=b;
OK, at last I found the problem. It is the line
printf("n1=%d n2=%d\n",n1,n2);
which creates problem in taking input by gets(). Instead of taking newline with the integer n1, n2, then I take newline as a ("%c",&ch) and then everything is okay.
Thanks to everyone who answered me.
Chances are, the string contains \r\n\0 (or \n\r\0 - never remember which comes first). \r\n is newline on Windows and \0 is the terminating character of the string.
In general, if the first character of the string is \r or\n, you read an empty string. FWIW this should work on all platforms:
char* string;
// initialize string and read something into it
if (strlen(string) == 0 || string[0] == `\r` || string[0] == `\n`)
// string is empty
Update: you mention that you use gets, and read from a file. However, for the latter you need fgets, so there is some confusion here. Note that fgets includes the trailing newline character in the string returned, while gets does not.
Update3: The way you read from the file is indeed fishy. You reopen the standard input to read from the file - why??? The standard practice is to fopen the file, then read from it with fscanf and fgets.
Update2: stupid us (and clever #Salil :-). You say
it read 'cat' as string[3]
Since C arrays are indexed from 0, string[3] contains the 4th line read! The third line is stored in string[2] - I bet that will contain the empty string you are looking for.
Output of this code:
#include <cstdio>
int main ()
{
int i = 0;
char string [256];
while (gets(string)) {
++i;
}
printf("%d\n", i);
return 0;
}
For this input
a
b
d
Is
4
Which means, gets() reads all lines correctly, which in turn means your code must be screwed up. Post it here.
First and foremost, do not use gets!!!!! It is a buffer overflow vulnerability, since you cannot specify the size of the destination buffer, and so gets() can easily overrun your buffer. Instead, use fgets() or getchar().
Since you are using map<string,int>, it is clear that you are actually using C++ code. In that case, an even better approach is to use the C++ iostreams libraries for your input and output.
Now that I've done with my rant, the problem is this... gets -- which, again, you should never ever use -- according to the spec, will read up until a newline, and "any <newline> shall be discarded". The function fgets() will copy the newline into the destination buffer, giving you the desired behavior.
If there is no string, how do you expect to read it?
Please give us a piece of code :)
==Later edit ==
OK:
"gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with '\0'. "
So basically, if you have:
char x[3];
gets(x);
Then this function will fill in x[0] with '\0'
If you read the manpage you'll see that gets is not recommended. Use fgets instead