I need to make a program that outputs all lines that contain a matching string "target" and sum up the number of matches along with the total cost,
The problem is that the for loop stops at the first iteration regardless of there being a match
I have tried using strstr() instead of strcmp() and it works, but since this is a school assignment, i cant use strstr()
#include<stdio.h>
#include<string.h>
struct data{
char product[100];
char name[100];
int price;
};
int main(){
FILE *f;
f = fopen("Customers.txt", "r");
int x;
scanf("%d",&x);
struct data arr[x];
for(int i=0;i<x;i++){
fscanf(f,"%[^,], %[^,], %d",arr[i].product,arr[i].name,&arr[i].price);
}
char target[100];
int res;
int count=0;
int total=0;
scanf("%s",target);
for(int j=0;j<x;j++){
res=strcmp(arr[j].product,target);
if(res==0){
printf("%s, %s, %d",arr[j].product,arr[j].name,arr[j].price);
count++;
total = total + arr[j].price;
}
else{
continue;
}
}
printf("\nTotal Buyers: %d\n",count);
printf("Total Amount: %d\n",total);
}
File:
Gem, Alice, 2000
Gold, Bob, 3000
Gem, Cooper, 2000
Input:
3 (No. of lines in the file)
Gem (target)
Expectd output:
Alice 2000
Cooper 2000
The fscanf format string is wrong, it should be:
"%[^,], %[^,], %d\n"
Note the final \n. Without that, the \n (new line) will not be absorbed and the first item of the next string read will start with \n.
Or even better: use this format string:
" %[^,], %[^,], %d"
Note the space at the beginning. With that format string all leading and trailing whitespace, including the newline, will be absorbed.
Furthermore you absolutely need to check if fopen fails, in your case it apparently doesn't, but if the file cannot be opened for some reason, and you do not any checks, the subsequent operations on f wont end well.
So you need at least this:
...
f = fopen("Customers.txt", "r");
if (f == NULL)
{
printf("Can't open file\n");
return 1;
}
...
Related
I am trying to write a program in C to take the data from the input.txt file and insert it into the record.txt file in the ascending order of the students’ ID.
Content of input.txt:
1
2012 Bob CS21
1999 Teddy CS35
2
3
2001 Eric CS11
2011 CS12 CS87
Content of record.txt:
1287 Nancy CS11
1865 Brown CS33
When I run the program, the following data from input.txt is supposed to be inserted into the record.file(valid data with students' ID, name and course):
2012 Bob CS21
1999 Teddy CS35
2001 Eric CS11
Then the content of record.txt file after insertion(ascending order):
1287 Nancy CS11
1865 Brown CS33
1999 Teddy CS35
2001 Eric CS11
2012 Bob CS21
Below is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
typedef struct {
int id;
char name[20];
char course[5];
} Student;
int read_records(Student s[]) {
FILE *fp;
int i=0;
fp = fopen("record.txt", "r");
if (fp == NULL) {
printf("File doesn't exist\n");
return 0;
}
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
fclose(fp);
return i;
}
void write_records(Student s[], int n) {
FILE *fp;
fp = fopen("record.txt", "w");
if (fp == NULL) {
printf("File doesn't exist\n");
return;
}
for (int i=0; i<n; i++) {
fprintf(fp, "%d %s %s\n", s[i].id, s[i].name, s[i].course);
}
fclose(fp);
}
void insert_records(Student s[], int n) {
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL) {
printf("File doesn't exist\n");
return;
}
int i=n;
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
fclose(fp);
write_records(s, i);
printf("Insertion is done.\n");
}
void display_records(Student s[], int n) {
for (int i=0; i<n; i++) {
printf("%d %s %s\n", s[i].id, s[i].name, s[i].course);
}
}
int main() {
int n;
Student s[MAX];
n = read_records(s);
int opt;
while (1) {
printf("1. Insert\n");
printf("2. Display\n");
printf("3. Exit\n");
printf("Choose an option: ");
scanf("%d", &opt);
switch(opt) {
case 1: insert_records(s, n);
break;
case 2: display_records(s, n);
break;
case 3: exit(0);
}
}
}
When I run the program, the user will be asked to choose an option.
If I enter 2, it will display the content of record.txt. And it works well, as I expected.
If I enter 1, the insertion should be performed and "Insertion is done" will be printed. However, the program doesn't work as I expected. It just displays nothing.
I am confused about that, and I would like to know how to fix the program.
This loop
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
will run indefinitely1 because when fscanf encounters input it cannot convert, that input is left in the stream. The file will never exhaust.
With the file
1
2012 Bob CS21
1999 Teddy CS35
2
3
2001 Eric CS11
2011 CS12 CS87
the first fscanf call will convert 1 "2012" "Bob". The second call and so on will fail to convert CS21 into an integer.
Additionally, see Why is “while( !feof(file) )” always wrong?.
Also note that an unbounded %s specifier in *scanf functions is as dangerous as gets. Use a field-width specifier to limit the amount of data that can be read into your buffers. This should be the maximum allowed string length, and should be at most the size of your buffer minus one, leaving room for the null-terminating byte. (i.e., char s[256]; scanf("%255s", s);).
The solution is to never ignore the return values of the fscanf family of functions. Use these return values to determine how your program should proceed. If fscanf fails to match the expected number of conversions, you must be ready to clean up the input stream. This usually means consuming characters until the end of a line is found.
This is easier said than done, however. As seen above, the way %s skips whitespace (including newline characters) means it can overreach looking for valid characters.
The general suggestion is to avoid scanf/fscanf, and instead read entire lines with fgets and then parse those lines with sscanf (or other tools).
In practice this looks like:
#include <stdio.h>
#include <stdlib.h>
#define INPUT "input.txt"
int main(void)
{
FILE *file = fopen(INPUT, "r");
if (!file) {
perror(INPUT);
return EXIT_FAILURE;
}
struct {
int id;
char name[32];
char course[16];
} student;
char buffer[512];
while (fgets(buffer, sizeof buffer, file)) {
int cv = sscanf(buffer, "%d%31s%15s",
&student.id, student.name, student.course);
if (3 == cv) {
/* three successful conversions, do whatever with `student` */
printf("%s<%d> [%s]\n",
student.name, student.id, student.course);
}
}
fclose(file);
}
With your input.txt file this prints:
Bob<2012> [CS21]
Teddy<1999> [CS35]
Eric<2001> [CS11]
CS12<2011> [CS87]
Once you know sscanf succeeded in parsing the expected number of conversions you can move on to validating the record (e.g., "CS12" is an invalid name, per your examples, and you probably need a way to avoid duplicate entries).
In your code you should only increment i after making sure all these steps are followed.
After you have your merged list of records, you should use qsort to sort the array with a comparison function such as
int student_sort(const void *va, const void *vb)
{
const Student *a = va, *b = vb;
return (a->id > b->id) - (a->id < b->id);
}
before writing the records out.
1. i will eventually overflow, invoking Undefined Behaviour. The outcome of the program after this point cannot be generally reasoned about.
These are 2 separate applications.
In the first one, I tried to store employee details like name, age and salary in the binary file named emp.bin.
In the second application, I tried to view the contents of the file but in place of the name, only the first character appears.
I tried printing each character separately, and it turns out that there's 3 null characters '\n' after each letter in the name that is why it is not printing after the first character.
"Write" application code:
//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>
int main()
{
FILE *fp;
char another = 'Y';
struct emp
{
char name[40];
int age;
float bs;
};
struct emp e;
fp = fopen("emp.bin", "wb");
if (fp == NULL)
{
puts("Cannot open the file.");
return 1;
}
while(another == 'Y')
{
printf("Enter the employee name, age and salary: ");
scanf("%S %d %f", e.name, &e.age, &e.bs);
while(getchar() != '\n');
fwrite(&e, sizeof(e), 1, fp);
printf("Add another record? (Y/N)");
another = getchar();
}
fclose(fp);
return 0;
}
"Read" application code:
//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>
int main()
{
FILE *fp;
struct emp
{
char name[40];
int age;
float bs;
} e;
fp = fopen("emp.bin", "rb");
if (fp == NULL)
{
puts("Cannot open the file.");
return 1;
}
while (fread(&e, sizeof(e), 1, fp) == 1)
{
printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
}
fclose(fp);
}
Here's the input and output:
How can I correct this code to make it print the whole name?
The problem is in the "writer" application, even before the actual write is performed.
When you get data from the user
scanf("%S %d %f", e.name, &e.age, &e.bs);
you use format %S (capital letter "S". Format specifiers are case sensitive!). As we can read in the printf man page
S
(Not in C99, but in SUSv2.) Synonym for ls. Don't use.
this leads us to %ls format specifier that is described in the following way
s
[...] If an l modifier is present: The const wchar_t * argument is expected to be a pointer to an array of wide characters. Wide characters from the array are converted to multibyte characters
Talking about Windows source we have:
S
Opposite-size character string, up to first white-space character (space, tab or newline). [...]
When used with scanf functions, signifies wide-character array; when used with wscanf functions, signifies single-byte-character array [...]
So, basically, you are reading characters from stdin and converting them to wide chars. In this case every character takes sizeof(wchar_t). Probably in your system this size is 4.
What you need is simply %s format specifier. And since your name array has size 40, I suggest using
scanf("%39s", e.name );
to get the name from user. In this way up to 39 characters will be written, being the 40th reserved to the string terminator '\0'.
As noted by Roberto in his answer, the problem is the %S conversion specifier, which is a typo, you should use %s.
Note however that there are other issues which might pose problems:
you should tell scanf() the maximum number of characters to read for the employee name, otherwise scanf() may write beyond the end of the destination array if input is too long.
if both programs run on separate systems with different endianness, the numbers will be incorrect on the receiving end because their bytes will be stored in the opposite order. For this reason, endianness should be specified and handled explicitly in binary files. Text format tends to be preferred for data transmission.
Here is a modified version:
//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>
int main() {
FILE *fp;
char another = 'Y';
struct emp {
char name[40];
int age;
float bs;
} e;
int c;
fp = fopen("emp.bin", "wb");
if (fp == NULL) {
puts("Cannot open the file.");
return 1;
}
while (another == 'Y') {
printf("Enter the employee name, age and salary: ");
if (scanf("%39s %d %f", e.name, &e.age, &e.bs) != 3)
break;
while ((c = getchar()) != EOF && c != '\n')
continue;
if (fwrite(&e, sizeof(e), 1, fp) != 1)
break;
printf("Add another record? (Y/N)");
another = getchar();
}
fclose(fp);
return 0;
}
"Read" application code:
//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>
int main() {
FILE *fp;
struct emp {
char name[40];
int age;
float bs;
} e;
fp = fopen("emp.bin", "rb");
if (fp == NULL) {
puts("Cannot open the file.");
return 1;
}
while (fread(&e, sizeof(e), 1, fp) == 1) {
printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
}
fclose(fp);
return 0;
}
So I have this code:
#include <stdio.h>
int main()
{
char peopleName[5][20],peopleAge[5];
int i;
int maxAge=0, maxName=-1;
for(i=0;i<5;i++)
{
printf("Name & Age %d :",i+1);
scanf("%s",&peopleName[i]);
scanf("%d",&peopleAge[i]);
if(peopleAge[i]>maxAge)
{
maxAge=peopleAge[i];
maxName=i;
}
}
printf("%s %d", peopleName[maxName],peopleAge[maxAge]);
}
This code finds from 5 people the oldest one. I want to change from 5 people to N number of people, whatever the number I input myself. (For example I put 7, and I can insert seven names and ages and so on).
The question has two parts: How does the user specify how many persons are entered? And how do I store the data?
The second part is easy: No matter how many persons you are going to consider, if you just want to know who is the oldest, it is enough to keep the name and age of the currently oldest person. (Of course, if there is a tie and many people are, say, 80 years old, you just get to keep the first match.)
Not storing anything also simplifies the first question. You could ask the user to specify the number of persons beforehand and that's find if you have few people. If you have a list of many people, the user would have to count the by hand and then enter the count. Miscounting is very likely.
A better way is to indicate the end of input by another means, for example by a negative age or by two dashes as name. There is also the possibility that the input runs out, for example when redirecting input from a file or when pressing one of Ctrl-Z or Ctrl-D, depending on your platform, after the input.
The example below read the input line-wise and then scans that line. The loop while (1) is in theory infinite, in practice execution breaks out of the loop when the input runs out – fgets return NULL –, when a blank line is read or when the input isn't in the format single-word name and age.
#include <stdio.h>
int main(void)
{
char oldest[80] = "no-one";
int max_age = -1;
int count = 0;
puts("Enter name & age on each line, blank line to stop:");
while (1) {
char line[80];
char name[80];
int age;
if (fgets(line, sizeof(line), stdin) == NULL) break;
if (sscanf(line, "%s %d", name, &age) < 2) break;
if (age > max_age) {
strcpy(oldest, name);
max_age = age;
}
count++;
}
printf("The oldest of these %d people is %s, aged %d.\n",
count, oldest, max_age);
return 0;
}
You can do this -
int n; // number of people
scanf("%d",&n); // take input from user
char peopleName[n][20],peopleAge[n]; // declare 2-d array
for(i=0;i<n;i++)
{
// your code
}
Also this statement -
scanf("%s",&peopleName[i]); // pass char * as argument to %s
should be -
scanf("%19s",peopleName[i]); // one space is left for null character
You can use malloc to allocate buffer dynamically.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char (*peopleName)[20];
int *peopleAge;
int i;
int maxAge=0, maxName=-1;
int dataNum;
printf("How many people? :");
if(scanf("%d",&dataNum)!=1)return 1;
peopleName=malloc(sizeof(char[20])*dataNum);
peopleAge=malloc(sizeof(int)*dataNum);
for(i=0;i<dataNum;i++)
{
printf("Name & Age %d :",i+1);
scanf("%s",peopleName[i]);
scanf("%d",&peopleAge[i]);
if(peopleAge[i]>maxAge)
{
maxAge=peopleAge[i];
maxName=i;
}
}
printf("%s %d", peopleName[maxName],peopleAge[maxName]);
free(peopleName);
free(peopleAge);
return 0;
}
Also please note that:
You should pass char*, not char(*)[20], for %s in scanf
peopleAge[maxAge] may be out of bounds. maxName (or other name but same role) should suit here.
I am trying to count words from file called file.txt but it gives me charecters with whitespace.
How to count words without counting the whitespace?
#include<stdio.h>
#include<conio.h>
main()
{
FILE *f1;
char c;
clrscr();
printf("data output");
f1 = fopen("file.txt","r");
while((c=getc(f1))!=EOF)
{
printf("%c",c);
}
fclose(f1);
getch();
}
Please help me to solve it as soon as possible.
Thanks in advance.
// Create a char array to store a word
char word[100];
// Stop when fscanf returns 0
while(fscanf(f1, "%s", word)==1)
{
// print the word
printf("%s ",word);
// Increment count
count ++;
}
// Print count
printf("%d\n", count );
Remember it assumes that no word is longer than 100 characters since fscanf does't check for buffer overflow
Ok, thats half of my code, but i have problem and i cant fix it. For example i need to pick choice 2 it is adding something to file, i enter[ name, surname, date, gender ] press enter and program shows like menu again(2.Add to file) but this time automatically picks 2 choice and i need to write data another time and it happens all the time when picking choice 2. Please help me find solution of this problem.
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#define N 15
struct date
{ int da_year;
int da_month;
int da_day;
};
struct studenti
{
int Nr;
char name[25];
char surname[25];
struct date dzd;
char dzimums;
}students[N] ;
int main()
{
sakums:
// FILE *fails_st;
char line[100];
char *ptk; char * end; int i;int sorted;
int g=0,ch,count=0;
int n;
int choice;
FILE *fails_st = fopen("studenti.txt", "r+");
/* errors ja neizdodas atveert failu */
if (fails_st == NULL)
{
printf("Error opening file!\n");
exit(1);
}
printf("\n2.Add to file");
scanf("%d",&choice);
if(choice==2){
/* write in file */
for (n=0; n<1; n++)
{
printf("%d. Ievadiet: vards, uzvards, datums, dzimums >", n+1);
scanf("%s",&students[n].name);
scanf("%s",&students[n].surname);
scanf("%d.%d.%d", &students[n].dzd.da_day, &students[n].dzd.da_month, &students[n].dzd.da_year);
scanf("%c",&students[n].dzimums);
}
fseek(fails_st, 0, SEEK_END);
for (i=0; i<n; i++)
fprintf(fails_st, " %d. %s %s %d.%d.%d %c\n", N+1, students[i].name,
students[i].surname, students[i].dzd.da_day,
students[i].dzd.da_month, students[i].dzd.da_year,
students[i].dzimums);
fclose(fails_st);
goto sakums;
}
getche();
return 0;
}
Your problem is likely that scanf happily does nothing if the format string that is its first parameter doesn't match the available input. That means it won't change the value of choice, so it will still be 2.
The cause of this is probably that what you input doesn't match your format strings. You can detect when this happens by checking the return value of scanf - it will return the number of variables written to, basically. If that is less than the number of format specifiers in your format string, something went wrong.
At that point, you probably want to consume all the available input (maybe something like int c; do { c = getchar(); } while (c != '\n' && c != EOF); for a simple program like yours) and then prompt the user again.
In particular, I believe your scanf("%c", ...) is likely the culprit: %c, unlike most scanf specifiers, will not ignore leading whitespace, but accept any character. So if you typed in "firstname lastname 1980.6.11 f", for example, the previous scanf call will just have consumed "6.11.1980", leaving " f" in the input buffer (note the space). Then the scanf with %c will read the space into the gender field, and leave the "f" in the input buffer. On the next go around, scanf("%d",&choice); will not do anything because "f" is not a valid number, choice will remain 2 and the "f" will get read as the first name on the next student entry, further confusing matters...
The solution is, I believe, to use scanf(" %c", ...); to explicitly consume leading whitespace.