C fgets and sscanf in loop : prevent useless looping - c

I have a problem with looping and fgets and sscanf to get the input.
I know to problem is from the size of the malloc with the input. If the user enter number larger than the malloc i want to ask again to enter a new number.
But in this code, if an user enter a too large number, it's looping lot of time (size of the word / 8) i think.
How to ask again to the user to enter new number without looping 4 times for example.
See the example i made with big number.
The idea was to free the input after the loop but it's doesn't works. Any ideas ?
There is my code :
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>
int main(void) {
char x[8];
char y[8];
int result = 0;
char *input=malloc(sizeof(char)*8);
bool answer = 0;
char *pos;
while(!answer) {
fgets(input, sizeof(input)-1, stdin);
//remove the /n from fgets
if ((pos=strchr(input, '\n')) != NULL)
*pos = '\0';
result = sscanf (input, "%s %s", x, y);
printf("%d\n", result);
if(result < 2) {
fprintf(stderr, "There is an error with the number you give, try again\n");
} else {
printf("%s\n", x);
printf("%s\n", y);
}
}
return 0;
}
And the output for : "01 01"
01 01
2
01
01
Output for : 000000005 000000005
0000000000005 000000000000005
1
There is an error with the number you give, try again
1
There is an error with the number you give, try again
2
5
0000
1
There is an error with the number you give, try again
1
There is an error with the number you give, try again

fgets() doesn't throw away the rest of the line when it's longer than its buffer. You have to do it yourself.
If you look at this code I frequently use with fgets, you'll see the two tasks separated, and in which circumstances which one is done:
/*Returns 0 if OK, a negative value if EOF.*/
int fpurge(FILE *f)
{
int c;
while((c=fgetc(f))!=EOF && c!='\n')
{ }
return (c==EOF ? -1 : 0);
}
/* Returns a nonzero value if found, zero if not. */
int truncate_newline(char *str)
{
int bRet=0;
if(str!=NULL)
{
char *pNewLine = strchr(str, '\n');
if(pNewLine!=NULL)
{
bRet = 1;
*pNewLine = '\0';
}
}
return bRet;
}
/* Returns 0 if buffer is full, a positive value if line is complete,
a negative value if EOF (implies buffer full). */
int fclean(char *str, FILE *f)
{
int ret = 1;
if(!truncate_newline(str))
ret = fpurge(f);
return ret;
}
You can see that your own code does the truncate_newline part, but not the "throw away the rest of the line" (here in the function fpurge) part.
If you change your code thusly, it should work:
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>
#define BUFFER_SIZE 8
int main(void) {
char x[BUFFER_SIZE];
char y[BUFFER_SIZE];
int result = 0;
char *input=calloc(BUFFER_SIZE, sizeof(char));
bool answer = 0;
char *pos;
while(!answer) {
fgets(input, BUFFER_SIZE, stdin);
//remove the /n from fgets
if ((pos=strchr(input, '\n')) != NULL)
*pos = '\0';
else
{
int c;
while((c=getchar())!='\n' && c!=EOF) {}
}
result = sscanf (input, "%s %s", x, y);
printf("%d\n", result);
if(result < 2) {
fprintf(stderr, "There is an error with the number you give, try again\n");
} else {
printf("%s\n", x);
printf("%s\n", y);
}
}
return 0;
}
Or simply replace the whole if() with fclean(input, stdin);

Related

How do I output a result to the number of decimal-points user previously entered (C)?

I'm trying to output my results to the decimal accuracy the user has input. In other words, if they enter 145, then it outputs to 0 d.p. - if they enter 145.67, it outputs the result to 2 d.p.
I've tried achieving this by trying different things with %.lf but didn't manage to do it so far. Is there a way to do this in C? If so, is there a name for it - so I can learn more about it.
#include <math.h>
#include <stdio.h>
int main() {
// Using long, double or something else?
double number, fourthRoot;
printf("Enter a number: ");
scanf("%lf", &number);
fourthRoot = sqrt(sqrt(number));
printf("4th root of number %lf is %.10lf", number, fourthRoot);
return 0;
}
Read user input as a line with fgets() or some scanf("%s",...-like code.
char buf[400];
if (fgets(buf, sizeof buf, stdin)) {
And then process the string. One way is with sscanf()
int n;
// Get the number and offset to end of scan
if (sscanf(buf, "%lf%n", &number, &n) == 1) {
// Look for '.'
char *dp = strchr(buf, '.');
if (dp && dp < buf + n) {
// decimal point found
int pre = buf + n - dp - 1;
printf("%.*f\n", pre, number);
This works OK but does get fooled if exponential notation used. Additional code could look for the E or e (or P, p with hexadecimal FP notation).
There are a lot of edge cases that you should worry about, but this should get you started:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int
compute_root(const char *s)
{
char *end;
double number = strtod(s, &end);
if( *end ){
fprintf(stderr, "Invalid input: %s\n", s);
return 1;
}
char *dp = strchr(s, '.');
int precision = dp ? end - dp - 1: 0;
char fmt[256];
snprintf(fmt, sizeof fmt, "4th root of %%s is %%.%dlf\n", precision);
printf(fmt, s, pow(number, .25));
return 0;
}
int
main(int argc, char **argv)
{
int errors = 0;
char buf[64];
if( argc > 1 ){
while( *++argv ){
errors += compute_root(*argv);
}
} else while( scanf("%63s", buf) == 1 ){
errors += compute_root(buf);
}
return errors ? EXIT_FAILURE : EXIT_SUCCESS;
}
As an exercise, consider inputs like 5.345e4. The above code will output with a precision of 5, but you probably want 3. Handling those cases is non-trivial (for certain definitions of "trivial").

Null When Passing String

I got this output "Hospital (null) ΓÇô Report for COVIC19 ΓÇô Community Visit"
I'm trying to printout the name of the hospital from the function readHospital() but all I got for the output is these weird looking text. So sorry I'm very new to coding.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
char readHospital();
void intro(char a);
int main() {
char hospital_name;
hospital_name = readHospital();
intro(hospital_name);
}
char readHospital() {
char a[100];
printf("Enter Hospital Name: ");
fgets(a, 100, stdin);
return a;
}
void intro(char hospital_name) {
printf("Hospital %s – Report for COVIC19 – Community Visit", hospital_name);
}
I've changed your code, The readHospital function that you are using in your code is not a correct function for reading the input string from the user and returning it.
Instead, you can use the readNewString function that I've written.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
char * readNewString();
void intro(char a[100]);
int main() {
char * hospital_name;
hospital_name = readNewString();
intro(hospital_name);
}
char *readNewString(void) {
char buffer[1024];
if (!fgets(buffer, sizeof buffer, stdin)) {
return NULL; // read failed, e.g., EOF
}
int len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') {
buffer[--len] = '\0'; // remove the newline
// You may also wish to remove trailing and/or leading whitespace
} else {
// Invalid input
//
// Depending on the context you may wish to e.g.,
// consume input until newline/EOF or abort.
}
char *str = malloc(len + 1);
if (!str) {
return NULL; // out of memory (unlikely)
}
return strcpy(str, buffer); // or use `memcpy` but then be careful with length
}
void intro(char hospital_name[100]) {
printf("Hospital %s – Report for COVIC19 – Community Visit", hospital_name);
}
Please note that hospital_name is of char type and you return a string, char list char*

How to exit code if nothing is entered? Code example: Check if string is a palindrome or not? (in C Programming Language)

QUESTION: How to exit code if nothing is entered into STDIN (console)?
For example:
*Input
"NULL - NOTHING - ZERO" :)
Expected Output
(Close program quit loop)*
Input:
Hello
Output:
Hello is not a palindrome
Input:
otto
Output:
otto is a palindrome
Code Explanation:
A palindrome is a string phrase that reads the same backwards as well as forwards. Examples of palindromes are “ABCDCBA”, “otto”, “i am ma i”, “C”. Write a program that reads in a line of text, and prints out whether or not that line of text is a palindrome.
#include <stdio.h>
#include <string.h>
#define MAXLEN 100
void reverseString(char *str, char *reversedStr)
{
int i;
for (i=strlen(str)-1; i>=0; i--)
{
*reversedStr++ = *(str+i);
}
*reversedStr = '\0';
}
int main(int argc, char **argv) {
char str[MAXLEN];
char reversedStr[MAXLEN];
while (fgets(str, sizeof(str)-1, stdin) != NULL)
{
str[strlen(str)-1] = '\0'; // the last character is the newline. Replace with null
reverseString(str, reversedStr);
if (strcmp(str, reversedStr) == 0)
printf("%s is a palindrome\n", str);
else {
printf("%s is not a palindrome\n", str);
}
}
return 0;
}
Code Snippet:
https://onlinegdb.com/ByGKe8LnE
Your code works. Just press ctrl-d right after enter (signals end of input) to exit.
Couple of other things:
fgets() doesn't need size minus 1. per the manual: "fgets() reads in at most one less than size characters..."
there is really no need to copy/reverse the entire string and compare, half will suffice, OR you could simply compare beginning with end until center, without copying.
strlen() "walks" the string - you can reuse the result of it instead of calling again.
.
#include <stdio.h>
#include <string.h>
#define MAXLEN 100
int isPalindrome(char *str, size_t len) {
char *end = str + len - 1;
while (end > str)
if (*end-- != *str++) return 0;
return 1;
}
int main(int argc, char **argv) {
char str[MAXLEN];
while (fgets(str, sizeof(str), stdin) != NULL) {
size_t len = strlen(str) - 1;
str[len] = 0; // the last character is the newline. Replace with null char
printf("%s is %sa palindrome\n", str, isPalindrome(str, len) ? "" : "not ");
}
return 0;
}

Segmentation fault when reading floats from terminal input

After confirming input Segmentation fault pops up and I can't see why. I was told to use fgets and sscanf in loop to read an undefined number of floats from terminal input and this is what i came up with..
Code
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define EMPTY 3.141592
#define BUFFERSIZE 100
void removeSubstring(char *s,const char *toremove){
while( (s=strstr(s,toremove)) )
memmove(s,s+strlen(toremove),1+strlen(s+strlen(toremove)));
}
int main(){
int x=0;
int y=0;
float a[BUFFERSIZE] = {EMPTY};
char buffer[BUFFERSIZE] = {EMPTY};
char *pos;
char *start = 0;
int space = ' ';
printf("Input: ");
fgets(buffer, BUFFERSIZE, stdin);
while(x< BUFFERSIZE){
sscanf(buffer, "%f ", &a[x]);
pos = strchr(buffer, space);
removeSubstring(start, pos);
x++;
}
printf("Saved input: ");
while(y<BUFFERSIZE && a[y] != EMPTY){
printf("%.2f", a[y]);
y++;
}
printf("\n");
return 0;
}
Edit: Increased both array sizes and removed feof
Almost certainly the problem is in the line pos = strchr(buffer, space);. If the input does not contain a space character, then pos is set equal to NULL. Passing a NULL to strstr() will likely result in a SEGV.

Sscanf a double value

I need scan a double from a string, can you help me? It need to be something like this:
#include <stdio.h>
#include <string.h>
int main()
{
const char *key="infinity";
const char *str = malloc(2*sizeof(double*));
double a;
printf("Write the value: "
scanf("%s",str);
if (strcmp(str,key)==0)
printf ("ok");
else
{
sscanf(str,"%lf",&a);
printf("ok, a is %lf",a);
}
return 0;
When I put infinity, it works properly. But when I enter with some double value (like 1.25), it doesn't.
1.25
ok, a is 0.000000
First all, there is a way you should check if the line of code runs successfully or not.
You are using sscanf(), check it's return value to be sure if it is being successful in what it is supposed to do. (To get the double from str).
Also why const char*. It is pointing to an immutable character string. You can't change it's value. So the first scanf will fail. You can simply use char* here, that will be sufficient for your requirement.
Allocating 2*sizeof(double*) doesn't ensure that you will be able to hold all the digits of a double number. That's a wrong idea. You can consider a rough value to hold (like upto 12 digits after decimal point) and then scan it.
Considering the return value of malloc is another good practice. In case it fails it returns NULL. And after getting NULL if you go on accessing it, then it invokes undefined behavior.
Whatever you allocate using malloc, it's need to be freed after you are done working with it.
Also read some good books. There are plenty of them.
A small example:
#include <stdio.h>
#include <string.h>
#include <float.h>
#include <stdlib.h>
#include <ctype.h>
char *trimwhitespace(char *str)
{
char *end;
while(isspace((unsigned char)*str)) str++;
if(*str == 0)
return str;
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
*(end+1) = 0;
return str;
}
int main()
{
const char *key="infinity";
char *str = malloc(sizeof(char)*(3 + DBL_MANT_DIG - DBL_MIN_EXP+1));
if( str == NULL){
fprintf(stderr,"Error in malloc");
exit(1);
}
char *store = str;
double a;
printf("Write the value: ");
if( fgets(str, (3 + DBL_MANT_DIG - DBL_MIN_EXP+1), stdin) == NULL){
fprintf(stderr,"Error in string input");
exit(1);
}
str = trimwhitespace(str);
if( strlen(str) == 0){
printf("Empty line detected.\n");
return EXIT_FAILURE;
}
for(size_t i = 1; i < strlen(str); i++){
if( !isspace(str[i]) && isspace(str[i-1])){
printf("Error extra input detected\n");
return EXIT_FAILURE;
}
}
if (strcmp(str,key)==0)
printf ("ok");
else
{
char *err;
a = strtod(str, &err);
if (*err!=0 && !isspace((unsigned char)*err)) {
printf("Error extra input detected ");
}
else{
printf("ok, a is %lf",a);
}
}
free(store);
return 0;
}
Reference:
† After finding a bit I could finally cite the correct reference for the 3 + DBL_MANT_DIG - DBL_MIN_EXP part. Check this answer.

Resources