scanf doesn't input space as a character - c

The program below is supposed to print out all the characters of a line from a file between two entered characters. It works fine for all test cases except for the one where one of the explicitly entered characters is a space.
ex.
Input:
12345 Maja Majovska: 54
15145 Aco Acoski: 95
14785 Martin Martinoski: 87
#
: //Under the hashtag is the space
Correct output:
Maja Majovska
Aco Acoski
Martin Martinoski
My output:
54
15145 Aco Acoski 95
14785 Martin Martinoski 87
#include<stdio.h>
#include<string.h>
void wtf() {
FILE *f = fopen("podatoci.txt", "w");
char c;
while((c = getchar()) != '#') {
fputc(c, f);
}
fclose(f);
}
int main()
{
wtf();
getchar();
char z1, z2, c;
FILE *f;
f=fopen("podatoci.txt", "r");
int flag=0;
scanf(" %c %c", &z1, &z2);
while((c=fgetc(f))!=EOF){
if(c==z1){
flag=1;
continue;
}
if(c==z2){
flag=0;
printf("\n");
}
if(flag)
printf("%c", c);
}
fclose(f);
return 0;
}
Can someone point out simply what am i doing wrong? I'm new to working with files.

I think this code should work. Enter the two characters at the same time #: (# is space) followed by enter
#include<stdio.h>
#include<string.h>
void wtf() {
FILE *f = fopen("podatoci.txt", "w");
char c;
while((c = getchar()) != '#') {
fputc(c, f);
}
fclose(f);
}
int main()
{
// wtf();
// getchar();
char z1, z2, c;
FILE *f;
f=fopen("podatoci.txt", "r");
int flag=0;
printf("enter chars : ");
// scanf(" %c %c", &z1, &z2);
// printf("chars are |%c| |%c|",z1,z2);
char name[3];
fgets(name, 3, stdin);
// printf("chars are |%c| |%c|",name[0],name[1]);
char buffer[512]; // I suppose 512 is enough (see Two problems below)
int i=0;
while((c=fgetc(f))!=EOF){
/* Different problems :
1 : you have a 'space' followed by 'end of line' before ':'
example you have a space before 54 but end of line before :
So you could not display characters when a 'space' is a found.
You have to use a temporary buffer
2 : you have to reset your flag at the end of line
3 : If you have ':' alone, do not printf("\n")
4 : If you have a 'space' after the first 'space', it must be printed
example : Maja Majovska
^ (this space)
*/
if(c=='\n') { // Address pb 2
flag=0;
i=0;
continue;
}
if(!flag&&c==name[0]){ // Address pb 4
flag=1;
continue;
}
if(flag&&c==name[1]){ // Address pb 3
flag=0;
buffer[i]='\0'; // end of string
printf("%s\n",buffer);
i=0;
}
if(flag)
buffer[i++]=c; // Address pb 1
}
fclose(f);
return 0;
}
Some remarks :
Most of the explanations are in the code comments (you could remove it after reading)
I use fgets instead of scanf (https://stackoverflow.com/a/1248017/7462275)
I do it like that to keep the code as close to its original form as possible. But, I think it would be better to get text line by line and use string functions (string.h). For example, to print the first substring between two characters (no sanity checks done : these two characters must be in the string)
while((fgets(buffer,512,f))!=NULL){
i=strchr(buffer,name[0]);
*(strchr(i,name[1]))='\0';
printf("%s\n",i);
}

Related

For loop doesn`t work when add [^\n] to a scanf_s

This program should ask you to add member (people) to a struct and print them on a file but after the first for loop just stop working and jump over the name part. I just found that thing that allow you to add space to a string, tried it but no success...
I tried to remove it and it work without any problem so the [^\n] make something go wrong.
What is wrong ?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Staff {
char Surname[100];
char Name[100];
int age;
char spec[100];
int id;
} person;
void write();
void leggi();
void trova();
int main() {
write();
}
void write() {
int i = 0;
int n = 1;
int r;
FILE *fp;
fopen_s(&fp, "index.txt", "w+");
if (fp == NULL) {
printf("Failed to open file\n");
exit(1);
}
fprintf(fp, "%d\n", i);
for (i = 0; i < n; i++) {
printf("Surame:\n");
scanf_s("%[^\n]s", person.Surname, 100);
fprintf(fp, "%s\t\t", person.Surname);
//loop just get over the name part
printf("Name:\n"); //after the first loop
scanf_s("%s", person.Name, 100);
fprintf(fp, "%s\t", person.Name);
printf("Age:\n");
scanf_s("%d", &person.age);
fprintf(fp, "%d\t", person.age);
printf("Specialization\n");
scanf_s("%s", person.spec, 100);
fprintf(fp, "%s\n", person.spec);
printf("Want to enter another? 1=yes 0=no...\n");
scanf_s("%d", &r);
if (r == 1)
n = n + 1;
}
rewind(fp);
fprintf(fp, "%d\n", i);
fclose(fp);
}
There are multiple problems in your code:
you use the so called secure functions fopen_s, scanf_s etc, but you do not check the return values to detect invalid input. You should instead use standard functions, pass the appropriate arguments and check the return values.
using scanf_s is actually non portable: the scanf_s function defined in Annex K of the C Standard requires the length argument after the pointer to have size_t type, whereas the function with the same name in the Microsoft library uses type UINT, which has a different representation on 64-bit versions of their Windows OS. A classical case of the Embrace, enhance and extinguish strategy. In Standard C, one should write: scanf_s("%s", person.Name, (size_t)100) or better:
scanf_s("%s", person.Name, sizeof person.Name)
there is no need to open the output file for update with "w+", just use "w".
you rewind the stream pointer back to the beginning of file and overwrite the number of entries at the start of the file. This works as long as you have less than 10 entries, but beyond that, the number has more digits so some characters in the file will be corrupted. You could use a format with padding such as "%6d\n" which would allow for up to 1 million records without risks.
"%[^\n]s" is not a correct scanf format: you should just write "%[^\n]" or better " %99[^\n]" to skip initial white space and limit the input to 99 characters.
Here is a modified version:
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Staff {
char Surname[100];
char Name[100];
int age;
char spec[100];
int id;
};
void write(void);
void leggi(void);
void trova(void);
int main() {
write();
}
int flush_input(void) {
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
return c;
}
void write(void) {
int n = 0;
int r;
FILE *fp = fopen("index.txt", "w");
if (fp == NULL) {
fprintf("Failed to open file index.txt: %s\n",
strerror(errno));
exit(1);
}
fprintf(fp, "%6d\n", n);
for (;;) {
struct Staff person = { 0 };
printf("Surname:\n");
if (scanf(" %99[^\n]", person.Surname) != 1)
break;
flush_input();
fprintf(fp, "%s\t\t", person.Surname);
//loop just get over the name part
printf("Name:\n"); //after the first loop
scanf(" %99[^\n]", person.Name);
flush_input();
fprintf(fp, "%s\t", person.Name);
printf("Age:\n");
scanf("%d", &person.age);
flush_input();
fprintf(fp, "%d\t", person.age);
printf("Specialization\n");
scanf(" %99[^\n]", person.spec, 100);
flush_input();
fprintf(fp, "%s\n", person.spec);
n++;
printf("Want to enter another? 1=yes 0=no...\n");
if (scanf("%d", &r) != 1 || r != 1) {
flush_input();
break;
}
flush_input();
}
rewind(fp);
// update the entry count on 6 characters
fprintf(fp, "%6d\n", n);
fclose(fp);
}
Change the call of scanf below for entering strings by inserting a space in the beginning of the format string. For example instead of this call
scanf_s("%[^\n]s", person.Surname, 100);
(where the letter s must be removed from the format string) write
scanf_s(" %[^\n]", person.Surname, ( rsize_t )100);
^^^^^^^^
This allows to skip leading white space characters in the input buffer.
Pay attention to that changing the condition or the for loop the was as you are doing
for (i = 0; i < n; i++) {
//...
if (r == 1)
n = n + 1;
}
makes the code unclear. Instead of the for loop you could use do-while loop.

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

How to get exclude null terminators when reading in a string?

This program opens a file that contains a lake's name and its volume in units of hundreds of cubic miles--separated by a space. Its output is supposed to be the lake's name followed by a number of asterisks to represent its volume to the nearest hundred cubic mile (for example, a lake that has 12.7 hundred cubic miles in volume would print 13 asterisks). However, when it reads in a name that contains a space, it reads up until the space and then prints the next string in a new line. Is there any way I can read "gross dirty lake" as one line instead of "gross\ndirty\nlake" for example? Here's what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void name_asterisks(char name[20], float vol);
main() {
FILE *fp;
fp = fopen("lakes.txt", "r");
char name[20];
float vol;
if (fp == NULL) {
printf("File does not exist.\n");
system("pause");
return 0;
}
while (fscanf(fp, "%s %f", name, &vol) != EOF) {
name_asterisks(name, vol);
}
fclose(fp);
system("pause");
}
void name_asterisks(char name[20], float vol) {
int i;
printf("%s", name);
for (i = 0; i < (int)roundf(vol); i++)
printf("*");
printf("\n");
}
"%s" is for scanning non-white-space. Code needs a different format specifier.
char buf[100];
while (fgets(buf, sizeof buf, fp) != NULL) {
if (sscanf(buf, " %19[A-Za-z ]%f", name, &vol) != 2) {
fprintf(stderr, "Unexpected data\n");
break;
}
name_asterisks(name, vol);
}
" ": Skip white-spaces.
"%19[A-Za-z ]": Scan and save up to 19 letters or spaces, append '\0'.
"%f": Skip white-spaces and save scan a float.
Note about original code: Better to check for what code wants than checking against 1 undesired result
// while (fscanf(fp, "%s %f", name, &vol) != EOF) {
while (fscanf(fp, "%s %f", name, &vol) == 2) {
sample for like as gross dirty lake 12.7\n
#include <string.h> //for strrchr
...
char line[64];//line buffer
...
while (fgets(line, sizeof line, fp)){
char *p = strrchr(line, ' ');//search last ' '
*p = '\0';
//snprintf(name, sizeof(name), "%s", line);
vol = atof(p+1);
name_asterisks(line, vol);//name_asterisks(name, vol);
}

Write a program to count the number of times a character appears in the File. (Case insensitive... 'a' and 'A' are considered to be the same)

I have to write a program to count the number of times a character appears in the File. (Case insensitive... 'a' and 'A' are considered to be the same)
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
int main()
{
FILE *fp1;
char ch,f[100],c,d;
int ct=0;
printf("Enter the file name\n");
scanf("%s",f);
fp1=fopen(f,"r");
printf("Enter character:");
scanf(" %c",&c);
do
{
printf("%c",ch);
ch=fgetc(fp1);
d=toupper(ch);
printf("%c",d);
if(c==d)
++ct;
}while(ch!=EOF);
fclose(fp1);
printf("\n");
printf("%d",ct);
return 0;
}`
This is the program I have written but the output i'm getting it is..
[ a.txt contains the string-
aaa ]
Now when running the program this is the output which I get :
Enter the file name
a.txt
Enter character:a
aAaAa
0
What am I doing wrong here ??
What you need is to check if the character to be searched is equal to a character in the file or its uppercase version and if it is, increment ct.
Simply change
if(c==d)
to
if(c==d || c==ch)
Other problem: ch is not initialized here
printf("%c",ch);
in the first iteration of the do...while loop. Fix it by moving the above printf after
ch=fgetc(fp1);
Also, add a check to see if ch is not EOF before printing it.
If you input 'a', and you transform all your characters toUpper()... It can definitely not work ;:=)
This code works i guess.
#include<stdio.h>
#include<stdlib.h>
void main()
{
FILE *fp1;
char c;
char ai,s;
char fname[20];
int count=0;
clrscr();
printf("enter the character to be counted:");
scanf("%c",&ai);
s=toupper(ai);
printf("enter the file name :");
scanf("%s",&fname);
fp1=fopen(fname,"r");
if(fp1==NULL)
{
printf("cannot open this file");
}
do
{
c=fgetc(fp1);
if(c==ai || c==s)
{
count=count+1;
}
}
while(c != EOF);
printf("\nFILE '%s' has %d instances of letter %c",fname,count,ai);
getch();
}

Suggest an alternative for gets() function, using gcc compiler

Trying to input more than a single string in my program's strings array, for that used :
scanf("%80[^\r\n]", strings[i]);
fgets(string[i], MAXLEN, stdin);
a custom made function was also used:
int getString(char s[]) {
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF ) {
s[i] = ch;
++i;
}
s[i] = '\0';
fflush(stdin);
return i;
}
but unable to get input with more than one string each including white spaces
function gets() used to work earlier for me but since it is deprecated no alternative can be found
This is where it was used :
int getString(char s[]) {
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF ) {
s[i] = ch;
++i;
}
s[i] = '\0';
fflush(stdin);
return i;
}
struct vechileData
{
char vechileType[MAXLEN];
int begin_month;
int end_month;
double price;
} data[5];
int main(int argc, char const *argv[])
{
printf("Input Vechile data: \n");
int i=0;
while(i < 5) {
printf("Input vechile Type : \n");
fgets(data[i].vechileType, MAXLEN, stdin);
printf("Input begin month : \n");
scanf("%d", &data[i].begin_month);
printf("Input end monhth : \n");
scanf("%d", &data[i].end_month);
printf("Input price : \n");
scanf("%lf", &data[i].price);
++i;
}
printf("Input Vechile Type to display information about the vechile : \n");
char vech[MAXLEN];
fgets(vech, MAXLEN, stdin);
i=0;
while(i < 5) {
if (strcmp(vech,data[i].vechileType) == 0)
{
printf("vechileType: %s\n", data[i].vechileType);
printf("Begin month: %d\n", data[i].begin_month);
printf("End month: %d\n", data[i].end_month);
printf("Price : %lf\n", data[i].price);
}
++i;
}
return 0;
}
It skips the next input to string statement during run time, "seems to"
Your problem is really not a gets() issue.
None of the scanf("%d", ...) and scanf("%lf", ...) consume the '\n' after the number and thus contribute to your issue. It is the next read of stdin to take in the '\n'. So when the next car type is read, it gets the lingering '\n'. Your 2nd car type ends up being "\n".
Use of fgets(data[i].vechileType, MAXLEN, stdin); puts a '\n' in data[i].vechileType. You likely do not want this. Your former use of gets() consumed, but did not put the '\n' in its return.
I long ago gave up doing user input with scanf() due to these subtle issues.
Recommend to separate input from parsing, use fgets() and then sscanf(). Example:
char number[80];
if (fgets(number, sizeof(number), stdin)) {
sscanf(number, "%d", &x)
Your implementation of a gets() replacement differs as follows
1) It does not return s (or NULL or error/eof).
2) It does not set eof indicator on eof.
3) Should getchar() return a '\0', your while loop errantly continues.
Recommend that if you must replace gets(), do so via fgets().
#define My_gets_N (1024 /* Some BA number */)
char *My_gets(char * str) {
char buffer[My_gets_N];
char *retval = fgets(buffer, sizeof(My_gets_N), stdin);
if (retval) {
int l = strlen(buffer);
/* fgets() saves '\n', but gets() does not */
if ((l > 0) && (buffer[l-1] == '\n')) {
l--;
}
memcpy(str, buffer, l);
str[l] = '\0';
return str;
}
else {
return 0;
}
}
If you replacement solution needs to deal with string length > the fixed My_gets_N, other coding is needed.
You must be more specific about what went wrong with the fgets() approach, that's the one I would recommend and it does work.
Note that fgets() will input the entire line, including linefeed/carriage returns at the end, so you might need to clean those off if they're undesirable to keep.
I don't understand how gets() worked for you, despite the warning that practically every C book post K&R gives, as it's not only deprecated, but extremely dangerous to use. Like the others have said, fgets() would definitely work if you used it correctly.
Instead of replacing all the instances of uses of gets with fgets. Use following Macros:
#define TRUNCATE_NULL(strText) \
 { \
   int _strlen = strlen(strText); \
   if (_strlen > 0 && strText[_strlen - 1] == '\n') strText[_strlen - 1] = '\0'; \
   else while(fgetc(stdin)!='\n'); \
 }
#define gets(strText) fgets(strText, sizeof(strText), stdin); TRUNCATE_NULL(strText);
Why use fgets?
Because it is more secure than gets.
Is gets really insecure?
Yes. It is greedy indeed, it will accept as much food as you give, even if it can not eat.
So technically, as #halfer rightly commented below,
with the use of gets, program is prone to buffer overflow.
How ?
char name[5];
gets(name);
Now provide input of more than 5 characters, it will accept it. This would overwrite data from memory, which should not be overwritten this way.
Ok with fgets, but why use TRUNCATE_NULL macro ?
fgets is not perfect either. it will accept \n (Enter) as character to be placed in input name.So to remove unnecessary \n, and to make sure expected functionality of gets is achieved we can use it.
Actually, there you can use while((getchar())!='\n'); to avoid such type of problem and one thing there is no need to use of fflush(stdin) function.
Here's code you can use
#include<stdio.h>
#include<string.h>
#define MAXLEN 50
int getString(char s[])
{
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF )
{
s[i] = ch;
++i;
}
s[i] = '\0';
return i;
}
struct vechileData
{
char vechileType[MAXLEN];
int begin_month;
int end_month;
double price;
}data[5];
int main(int argc, char const *argv[])
{
printf("Input Vechile data: \n");
int i=0;
while(i < 2)
{
printf("Input vechile Type : \n");
fgets(data[i].vechileType, MAXLEN, stdin);
printf("Input begin month : \n");
scanf("%d", &data[i].begin_month);
printf("Input end monhth : \n");
scanf("%d", &data[i].end_month);
printf("Input price : \n");
scanf("%lf", &data[i].price);
while((getchar())!='\n');
++i;
}
printf("Input Vechile Type to display information about the vechile : \n");
char vech[MAXLEN];
fgets(vech, MAXLEN, stdin);
i=0;
while(i < 2)
{
if (strcmp(vech,data[i].vechileType) == 0)
{
printf("vechileType: %s\n", data[i].vechileType);
printf("Begin month: %d\n", data[i].begin_month);
printf("End month: %d\n", data[i].end_month);
printf("Price : %lf\n", data[i].price);
}
++i;
}
return 0;
}
I hope this will help you.....

Resources