I currently have a homework assignment and I used gets.
The professor said I should be using getchar instead.
What is the difference?
How would I change my code to use getchar? I can't seem to get it right.
code:
#include <stdio.h>
#include <string.h>
#include <strings.h>
#define STORAGE 255
int main() {
int c;
char s[STORAGE];
for(;;) {
(void) printf("n=%d, s=[%s]\n", c = getword(s), s);
if (c == -1) break;
}
}
int getword(char *w) {
char str[255];
int i = 0;
int charCount = 0;
printf("enter your sentence:\n"); //user input
gets(str);
for(i = 0; str[i] != '\0' && str[i] !=EOF; i++){
if(str[i] != ' '){
charCount++;
} else {
str[i] = '\0'; //Terminate str
i = -1; //idk what this is even doing?
break; //Break out of the for-loop
}
}
printf("your string: '%s' contains %d of letters\n", str, charCount); //output
strcpy(w, str);
// return charCount;
return strlen(w); //not sure what i should be returning.... they both work
}
gets() was supposed to get a string from the input and store it into the supplied argument. However, due to lack of preliminary validation on the input length, it is vulnerable to buffer overflow.
A better choice is fgets().
However, coming to the usage of getchar() part, it reads one char at a time. So basically, you have to keep reading from the standard input one by one, using a loop, until you reach a newline (or EOF) which marks the end of expected input.
As you read a character (with optional validation), you can keep on storing them in str so that, when the input loop ends, you have the input string ready in str.
Don't forget to null terminate str, just in case.
Related
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.
#include <stdio.h>
#include <ctype.h>
int main() {
char str[100];
char out[] = "exit";
do {
printf("Enter a string: ");
scanf("%s", str);
// some if else statement here
} while (toupper(str[3]) != toupper(out[3]));
}
I put the index 3 because if I put the index 0 there, the code will terminate if the entered string starts with letter e. I tried the while loop but it does not work for me. Also I want to print a prompt message that says "detected terminate keyword" after entering the word "exit" and then terminates the loop.
You will also notice the toupper() function. I used it there because I want my loop to be case insensitive, so regarless of lowercase or uppercase or combination of both, the loop should terminate when the word "exit" is entered.
toupper(str[3]) != toupper(out[3]) will compare the upper case 4th letter of str and out, so the loop will iterate till str[3] is 'T'. You want to use strcasecmp(str, out) instead. Remember to #include <strings.h>.
There are multiple problems:
it is confusing for a function isPalindrome() to return 0 for true.
to avoid undefined behavior on negative char values, a char argument to toupper should be cast as (unsigned char).
the test for the exit keyword is incorrect. You exit if the fourth letter is a t or a T. You should use strcasecmp to test for the exit word.
scanf("%s", str) has potential undefined behavior if the user enters a word with more than 99 bytes. Use scanf("%99s", str) and test the return value: it must be 1 for a successful conversion.
instead of a confusing do / while loop, use a for (;;) loop (also known as for ever loop), and test for 2 exit conditions: scanf() failure to read a word and reading the word exit.
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int isPalindrome(const char *str) {
size_t len = strlen(str);
for (size_t i = 0; i < len; i++) {
if (toupper((unsigned char)str[i]) != toupper((unsigned char)str[len - i - 1]))
return 0;
}
return 1;
}
int main() {
char str[100];
for (;;) {
printf("Enter a string: ");
if (scanf("%99s", str) != 1)
break;
if (!strcasecmp(str, "exit"))
break;
if (isPalindrome(str)) {
printf("%s is a palindrome!\n\n", str);
} else {
printf("%s is not a palindrome!\n\n", str);
}
}
return 0;
}
I put the index 3 because if I put the index 0 there, the code will terminate if the entered string starts with letter e
Exactly, and the code:
while (toupper(str[3]) != toupper(out[3])
Suffers from the same problem, any input with a t as its 4th character index 3 will match and the loop will end, you are comparing a specific character of the string, not the string itself. You can use strcasecmp to assess if the input is indeed exit and ignore casing.
Furthermore using %s specifier is not good, you run the risk of overrunning the destination buffer. You should use a width, %99s for a 100 characters buffer to leave space for the nul byte, if possible consider using fgets instead.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main() {
char str[100];
char out[] = "exit";
do {
printf("Enter a string: ");
scanf(" %99s", str); // space before specifier to clean leading whitespaces
// some if else statement here
} while (strcasecmp(str, out) != 0);
puts("Detected terminate keyword. Goodbye!");
}
char *removeLastChar(char *str, char ch)
{
size_t len;
if(str)
{
len = strlen(str);
if(str[len - 1] == ch) str[len -1] = 0;
}
return str;
}
char *strlwr(char *str)
{
char *wrk = str;
if(str)
{
while(*wrk)
{
*wrk = tolower((unsigned char)*wrk);
wrk++;
}
}
return str;
}
int main(void)
{
char str[100];
const char *out = "exit";
int x = 0;
do
{
printf("Enter a string: ");
if(!fgets(str, sizeof(str), stdin)) break;
removeLastChar(str, '\n');
printf("You entered: \"%s\"\n:", str);
} while (strcmp(strlwr(str), out));
}
My program skips the next input after 1 pass through it. I have read the threads on removing the newline character that fgets has, but nothing that was suggested worked. Is there anything that would work with microsoft visual studio? The best suggestion was "words[strcspn(words, "\r\n")] = 0;" and this did not remove the new line, unless I am formatting it incorrectly. I am not allowed to use the strtok function.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 50
#define STOP "quit\n"
char *copywords(char *dest, const char *source, size_t n);
int main(void)
{
char words[50];
char newwords[50];
size_t num;
for (;;) {
printf("\nType a word, or type 'quit' to quit: ");
(fgets(words, SIZE, stdin));
if (strcmp(words, STOP) == 0) {
printf("Good bye!\n");
return 0;
}
printf("Type the # of chars to copy: ");
scanf_s("%d", &num);
copywords(newwords, words, num);
printf("The word was %s\n", words);
printf("and the copied word is %s", newwords);
}
}
char *copywords(char *dest, const char *source, size_t n) {
size_t i;
for (i = 0; i < n && source[i] != '\0'; i++) {
dest[i] = source[i];
}
dest[i] = '\0';
return dest;
}
The problem is that you leave the \n on the input when you call scanf. i.e. the user types number[return]. You read the number. When you loop around and call fgets agains the return is still waiting to be read so thats what fgets gets and it returns immediately.
I would probably just call fgets the second time you want to read input as well and then use sscanf to read from the string. i.e.
printf("Type the # of chars to copy: ");
fgets(buffer, ...)
sscanf(buffer, "%d", ...)
As an aside I would also say to check return values as it is easy for fgets or *scanf to fail.
My program skips the next input after 1 pass through it.
If I understand you correctly, the problem is that scanf_s (which I assume is like the C standard's scanf) will read the digits into num, but scanf won't remove the following newline from stdin, and so in the next iteration of the loop fgets will see that newline and behave as if it had seen a blank line.
I have usually avoided scanf for this reason and instead read a line into a buffer and then parse it. For example:
char buf[50];
...
fgets(buf,sizeof(buf),stdin);
sscanf(buf,"%d",&num);
(I'd also recommend adding a whole lot more error checking throughout.)
Here's a straightforward solution.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 50
#define STOP "quit\n"
char *copywords(char *dest, const char *source, size_t n);
int main(void)
{
char words[50];
char newwords[50];
size_t num, run = 0;
for (;;) {
printf("\nType a word, or type 'quit' to quit: ");
if(run)
getchar();
(fgets(words, SIZE, stdin));
if (strcmp(words, STOP) == 0) {
printf("Good bye!\n");
return 0;
}
printf("Type the # of chars to copy: ");
scanf("%d", &num);
copywords(newwords, words, num);
printf("The word was %s\n", words);
printf("and the copied word is %s", newwords);
run = 1;
}
}
char *copywords(char *dest, const char *source, size_t n) {
size_t i;
for (i = 0; i < n && source[i] != '\0'; i++) {
dest[i] = source[i];
}
dest[i] = '\0';
return dest;
}
Since we know there will be an extra '\n' character left in the stream due to the scanf, just take it out.
I am trying to read a string into a char array with a length chosen by the user. The problem is that getchar() doesn't stop reading until the user manually enters a newline by pressing enter, based on my code. I have read through other's threads, and I understand why I'm not able to do it this way, it's just completely contradictory to my assignment handout.
int chPrompt(int nchars);
void strInput(char str[], int nchars);
int main(void) {
int nchars = chPrompt(nchars);
char str[nchars];
strInput(str, nchars);
return 0;
}
int chPrompt(int nchars) {
printf("How many chars do you need to input? >");
return scanf("%i", &nchars);
}
void strInput(char str[], int nchars) {
int i = 0;
while((str[i] = getchar()) != '\n') {
if(i > nchars-1)
break;
i++;
}
str[i] = '\0';
//Troubleshooting
printf("%s %d", str, strlen(str));
}
This is what the handout says:
Input a string from the keyboard (spaces included) using the technique we talked
about (while with getchar(), not gets() , fgets()or scanf() ),
augmented so that it will input any amount up to, but no more than, 80
characters. Be sure that there’s a null in the proper location after the input.
The technique we talked about in class was the while loop with getchar assignment to char array.
My question:
My professor is very adamant about his instructions. In this handout, he is specifically telling me to input any amount up to, but no more than, 80. This is contradicting the functionality of getchar, correct? Is there any way to limit the length of a string, using this 'technique'?
On some of the threads I found, people mentioned it might be OS dependent. So, if that matters, I am on Windows 8.1.
Op's code is close.
"getchar() doesn't stop reading until the user manually enters a newline by pressing enter" is incorrect.
Typical user input is line buffered. Nothing is given to the program until Enter occurs. At that time the entire line is given to the program. getchar() consumes 1 char at a time from stdin.
1) Need to allocate sufficient buffer memory #Grzegorz Szpetkowski
2) Read input as an int and read extra as needed.
3) Do not return the value from scanf() as the number of to read.
4) Read remaining line after reading the number of char to be read. #Grzegorz Szpetkowski
getchar() returns an unsigned char or EOF. That is typically 257 different results. Reading getchar() into a char loses that distinction.
void strInput(char str[], int nchars) {
int i = 0;
int ch;
while((ch = getchar()) != '\n' && ch != EOF ) {
if (i < nchars) {
str[i++] = ch;
}
}
str[i] = '\0';
}
int main(void) {
int nchars = chPrompt(nchars);
char str[nchars + 1]; // + 1
strInput(str, nchars);
//Troubleshooting
printf("'%s' %zu", str, strlen(str));
return 0;
}
int chPrompt(int nchars) {
printf("How many chars do you need to input? >");
if (scanf("%i", &nchars) != 1) {
printf("Unable to read #\n");
exit(-1);
}
// Consume remaining text in the line
int ch;
while((ch = getchar()) != '\n' && ch != EOF );
return nchars;
}
Note: strlen() returns type size_t, not int, this may/may not be the same on your platform, best to use the right format specifier "%zu" with strlen(). Alternatively use:
printf("'%s' %d", str, (int) strlen(str));
This code could be corrected in few more places (e.g. counting the characters inputted so that you allow the user to input no more than 80 characters, etc.) but this will point you in the right direction:
#include <stdio.h>
#include <string.h>
void strInput(char str[], int nchars);
int main(void) {
int nchars = 0;
printf("How many chars do you need to input?\n");
scanf("%d\n", &nchars);
char str[nchars+1];
strInput(str, nchars);
return 0;
}
void chPrompt(int nchars) {
}
void strInput(char str[], int nchars) {
int i = 0;
char c;
while((c = getchar()) != '\n' && i <= (nchars-1)) {
str[i] = c;
i++;
}
str[i] = '\0';
printf("%s %d\n", str, (int)strlen(str));
}
little change to above answers,Try this it is not giving any "buffer full error"
#include<stdio.h>
#include<string.h>
void getinput(char str1[],int s){
int i=0;
printf("inside fn\n");
char c;
while((c=getchar())!=EOF && i<=(s-1))
{ str1[i++]=c;
}
str1[i]='\0';}
void main()
{ int size;
printf("enter the no. of characters \n");
scanf("%d", &size);
char str1[size+1];
getinput(str1,size);
printf("%s \n",str1);
}
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.