Reading and writing binary files in C - c

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;
}

Related

Take data from a text file and insert the data into another file

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.

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.

How can I clear the input buffer without using rewind function? [duplicate]

I am not able to flush stdin here, is there a way to flush it? If not then how to make getchar() to take a character as input from user, instead of a "\n" left by scanf() in the input buffer??
#include "stdio.h"
#include "stdlib.h"
int main(int argc,char*argv[]) {
FILE *fp;
char another='y';
struct emp {
char name[40];
int age;
float bs;
};
struct emp e;
if(argc!=2) {
printf("please write 1 target file name\n");
}
fp=fopen(argv[1],"wb");
if(fp==NULL) {
puts("cannot open file");
exit(1);
}
while(another=='y') {
printf("\nEnter name,age and basic salary");
scanf("%s %d %f",e.name,&e.age,&e.bs);
fwrite(&e,sizeof(e),1,fp);
printf("Add another record (Y/N)");
fflush(stdin);
another=getchar();
}
fclose(fp);
return 0;
}
EDIT: updated code, still not working properly
#include "stdio.h"
#include "stdlib.h"
int main(int argc,char*argv[]) {
FILE *fp;
char another='y';
struct emp {
char name[40];
int age;
float bs;
};
struct emp e;
unsigned int const BUF_SIZE = 1024;
char buf[BUF_SIZE];
if(argc!=2) {
printf("please write 1 target file name\n");
}
fp=fopen(argv[1],"wb");
if(fp==NULL) {
puts("cannot open file");
exit(1);
}
while(another=='y') {
printf("\nEnter name,age and basic salary : ");
fgets(buf, BUF_SIZE, stdin);
sscanf(buf, "%s %d %f", e.name, &e.age, &e.bs);
fwrite(&e,sizeof(e),1,fp);
printf("Add another record (Y/N)");
another=getchar();
}
fclose(fp);
return 0;
}
Output:
dev#dev-laptop:~/Documents/c++_prac/google_int_prac$ ./a.out emp.dat
Enter name,age and basic salary : deovrat 45 23
Add another record (Y/N)y
Enter name,age and basic salary : Add another record (Y/N)y
Enter name,age and basic salary : Add another record (Y/N)
fflush(stdin) is undefined behaviour(a). Instead, make scanf "eat" the newline:
scanf("%s %d %f\n", e.name, &e.age, &e.bs);
Everyone else makes a good point about scanf being a bad choice. Instead, you should use fgets and sscanf:
const unsigned int BUF_SIZE = 1024;
char buf[BUF_SIZE];
fgets(buf, BUF_SIZE, stdin);
sscanf(buf, "%s %d %f", e.name, &e.age, &e.bs);
(a) See, for example, C11 7.21.5.2 The fflush function:
int fflush(FILE *stream) - If stream points to an output stream or an update stream in which the most recent operation was not input, the fflush function causes any unwritten data for that stream to be delivered to the host environment to be written to the file; otherwise, the behavior is undefined.
Update: You need to add another getchar() at the end of your loop to consume the '\n' that follows the Y/N. I don't think this is the best way to go, but it will make your code work as it stands now.
while(another=='y') {
printf("\nEnter name,age and basic salary : ");
fgets(buf, BUF_SIZE, stdin);
sscanf(buf, "%s %d %f", e.name, &e.age, &e.bs);
fwrite(&e,sizeof(e),1,fp);
printf("Add another record (Y/N)");
another=getchar();
getchar();
}
I would suggest reading the data you want to parse (up to and including the '\n') into a buffer and then parse it out using sscanf(). This way you consume the newline and you can perform other sanity checks on the data.
Use this instead of getchar():
char another[BUF_SIZE] = "y";
while( 'y' == another[0] )
{
printf( "\nEnter name,age and basic salary : " );
fgets( buf, BUF_SIZE, stdin );
sscanf( buf, "%s %d %f", e.name, &e.age, &e.bs );
fwrite( &e, sizeof(e) , 1, fp );
printf( "Add another record (Y/N)" );
fgets( another, BUF_SIZE, stdin );
}
It's not a good practice to use fflush( stdin ) as it has undefined behavior. Generally, functions like scanf() leaves trailing newlines in stdin. So, it is better to use functions that are "cleaner" than scanf(). You can replace your scanf() with a combination of fgets() and sscanf() and you can do away with fflush( stdin ).
I would recommend the fgets()+sscanf() approach that a lot of other people have suggested. You could also use scanf("%*c"); before the call to getchar(). That will essentially eat a character.
If you are doing this under windows, you can use winapi to flush input buffer before your getch().
#include <windows.h>
hStdin = GetStdHandle(STD_INPUT_HANDLE);
FlushConsoleInputBuffer(hStdin);
-or-
#include <windows.h>
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
As others already pointed out, you should not write a struct to a file. Instead, try to write the data in a formatted manner. This way your text file can be parsed line-by-line by finding the last and second-to-last delimiters, for example semicolons. Keep in mind that certain characters like '-' or '.' may occur in the stringified float field.
int write_data(FILE *fh, struct emp *e) {
if(fh == NULL || e == NULL)
return -1;
fprintf(fh, "%s;%d;%f", e->name, e->age, e->bs);
return 0;
}
The other thing is how everybody keeps recommending the same scanf family of functions, but nobody ever checks whether the return value is equal to the number of fields to be read. I think that is a bad idea, effectively asking for trouble. Even with the strtol/strtod way you need error checking:
int parse_int(char *buf, long *result) {
if(buf == NULL || result == NULL)
return -1;
errno = 0;
*result = strtoul(buf, NULL, 0);
if(errno != 0) {
perror("strtoul");
return -1;
}
return 0;
}
the two code examples above return silently which is fine if you plan to call them using existing objects all the time; consider printing an error message, though, and illustrate in your documentation that people should check the return values when using your functions.
stdin is not something flushable, you can flush only output streams. I.e. you don't need to call flush on stdin at all.

How to write, read and delete a file in a single script in C programming

I created a file and filled it with some entries. However, I want to read this file and show it on the screen. Also, after showing the entries, I want it to be deleted with my permission. But I am stuck at this point please help me.
EDIT: Code is updated but still couldn't figure it out how to do :/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char name[20], surname[20], city[30], country[30], gender[15];
int count = 0;
int main() {
FILE *f1;
f1 = fopen("C:\\FurkanArslan.txt", "r+");
while (count < 10) { // every step provides 5 new data, so 5*10 will provide 50 data in total.
printf("\n*Please enter required information: \n");
printf("Name :"); scanf("%s", name);
printf("Surname:"); scanf("%s", surname);
printf("Country:"); scanf("%s", country);
printf("City :"); scanf("%s", city);
printf("Gender :"); scanf("%s", gender);
fprintf(f1, " %s | %s | %s | %s | %s\n\n", name, surname, gender, city, country);
count++;
}
fclose(f1);
printf("\n<<<<<%d data has been successfully saved!>>>> \n", count * 5);
printf("-------------------------------------\n");
f1 = fopen("C:\\FurkanArslan.txt", "r");
char c, answer;
while ((c = fgetc(f1)) != EOF)
putchar(c); // In this part I displayed file on the screen.
printf("\n\n <<<< %d entries are displayed on the screen! >>>>", count * 5);
printf("\n\nWould you like to remove your file [Y/N] ?");
scanf(" %c", &answer);
if (answer == 'y' || answer == 'Y') {
remove("f1");
printf("\n\n***File successfully removed!");
}
return 0;
}
In order to show the content of a file you have to open it and read it letter by letter, after that, you can use the putchar function to output the current character
FILE *fp = fopen("path/to/file.txt","r");
char c;
while((c=fgetc(fp))!=EOF)
putchar(c);
fclose(fp);
after that to remove a file you need to use the remove function, which receives the name of the file as paramter.
remove("my_file.txt");
There are multiple issues in your code:
there is no need to make the variables and arrays global, just define them in the body of the main() function.
you should tell scanf() the maximum number of characters to store in the destination array with a length specifier in the format string (eg: "%19s") and check for conversion success.
the variable c used in the reading loop must have type int for proper detection of EOF. fgetc() returns a positive byte value if successful and the special negative value EOF at end of file.
you do not need to reopen the file after writing to it. Sine you opened it for update mode, you can just seek back to the beginning of the file with rewind(f1) or fseek(f1, 0L, SEEK_SET).
the file is open for read and update mode ("r+"): it will fail if the file does not exist. You should open it in write and update mode with "w+" to create or truncate it.
you should check that fopen succeeds at opening the file, otherwise you invoke undefined behavior passing a null stream pointer to fprintf.
to remove the file, remove() takes the filename as its argument. You must close the file before attempting to remove it.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
const char *filename = "C:\\FurkanArslan.txt";
char name[20], surname[20], city[30], country[30], gender[15];
int count = 0;
FILE *f1 = fopen(filename, "w+");
if (f1 == NULL) {
printf("Cannot open file %s.\n", filename);
return 1;
}
while (count < 10) { // every step provides 5 new data, so 5*10 will provide 50 data in total.
printf("\n*Please enter required information: \n");
printf("Name :"); if (scanf("%19s", name) != 1) break;
printf("Surname:"); if (scanf("%19s", surname) != 1) break;
printf("Country:"); if (scanf("%29s", country) != 1) break;
printf("City :"); if (scanf("%29s", city) != 1) break;
printf("Gender :"); if (scanf("%14s", gender) != 1) break;
fprintf(f1, " %s | %s | %s | %s | %s\n\n", name, surname, gender, city, country);
count++;
}
printf("\n<<<<< %d data has been successfully saved to %s! >>>>\n",
count * 5, filename);
printf("-------------------------------------\n");
rewind(f1);
int c;
while ((c = fgetc(f1)) != EOF)
putchar(c);
printf("\n\n <<<< %d entries are displayed on the screen! >>>>\n", count);
fclose(f1);
printf("\nWould you like to remove your file [Y/N] ?");
char answer;
if (scanf(" %c", &answer) == 1 && (answer == 'y' || answer == 'Y')) {
if (remove(filename)) {
printf("\n\n***Error removing file %s: %s\n",
filename, strerror(errno));
} else {
printf("\n\n***File %s successfully removed!\n", filename);
}
}
return 0;
}

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);
}

Resources