I' am writing a C program which allows the user to dynamically specify the File name from which the data is to be read. Next the user enters a lower bound and an upper bound. The data in the lines from between the bounds is to be printed.
For this the main function makes a call: readValues(cTargetName, iLower, iHiger);
The function readValues is supposed to work as follows:
Check if file exist, if yes. Open it with fopen
Read with feof and fgets line by line the whole file, and store each line in char string
With a for loop, print the correct range of lines from the string
I'm not sure why but the while loop doesn't seem to exit although I use the feof statement, which should terminate after the end of the File is reached.
The code looks as follows:
#include <stdio.h>
#include <stdlib.h>
void readValues(char cFileName[75], int n, int m)
{
//Variable declaration;
char strArray[50][50];
char *parser;
int i = 0;
FILE *Data;
if(Data = fopen(cFileName, "rt") == NULL){
printf("File could not be opened");
return 1; //Can you return 1 in a void function?
}
//Read the file line by line
while(feof(Data)==0){
fgets(strArray[i], 200, Data);
i++;
}
//Reading the specified lines
for(n; n<=m; n++){
printf("%s", strArray[n]);
}
}
int main()
{
char cTargetName[75] = {"C:/Users/User1/Desktop/C_Projects_1/TestData.txt"};
int iLower = 2;
int iHiger = 4;
readValues(cTargetName, iLower, iHiger);
return 0;
}
All help is appreciated. Thanks in advance!
Here is my solution to your question:
#include <stdio.h>
#include <stdlib.h>
#define MIN_LINE_LENGTH 64
typedef enum {
false, true
} bool;
int main() {
char filename[PATH_MAX] = {0};
printf("Enter filename:\n");
fgets(filename, PATH_MAX, stdin); // get filename from stdin
char *ptr = filename;
while (*ptr) { // remove trailing newline at the end of filename (fgets() includes newline)
if (*ptr == '\n') {
*ptr = 0;
}
++ptr;
}
printf("Enter starting line and end line, separated by a space:\n");
size_t startLine = 0;
size_t endLine = 0;
bool hasFirstNum = false;
bool hasSecondNum = false;
bool hasMiddleSpace = false;
bool hasLastSpace = false;
size_t numCount = 0;
int ch;
while ((ch = fgetc(stdin)) != EOF && ch != '\n') { // continually receive chars from stdin
if (ch != 32 && !(ch >= 48 && ch <= 57)) { // if not a space or number, raise error
fprintf(stderr, "Only numerical values (and spaces) can be entered.\n");
return 1;
}
if (ch == 32) {
if (hasFirstNum) {
hasMiddleSpace = true;
}
if (hasSecondNum) {
hasLastSpace = true;
}
continue;
}
else if (!hasFirstNum) {
++numCount;
hasFirstNum = true;
}
else if (!hasSecondNum && hasMiddleSpace) {
++numCount;
hasSecondNum = true;
}
else if (hasLastSpace) {
++numCount;
}
if (numCount == 1) {
startLine *= 10;
startLine += ch - 48; // '0' character in ASCII is 48
}
else if (numCount == 2){
endLine *= 10;
endLine += ch - 48;
}
else {
break;
}
}
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Error opening file.\n");
return 1;
}
char **lines = malloc(sizeof(char *));
char *line = malloc(MIN_LINE_LENGTH);
*lines = line;
int c;
size_t char_count = 0;
size_t line_count = 1;
while ((c = fgetc(fp)) != EOF) { // continually get chars from file stream
if (c == '\n') { // expand lines pointer if a newline is encountered
*(line + char_count) = 0;
++line_count;
lines = realloc(lines, line_count*sizeof(char *));
line = (*(lines + line_count - 1) = malloc(MIN_LINE_LENGTH));
char_count = 0;
continue;
}
if ((char_count + 1) % MIN_LINE_LENGTH == 0 && char_count != 0) { // expand line pointer if needed
line = realloc(line, char_count + MIN_LINE_LENGTH);
}
*(line + char_count) = c;
++char_count;
}
*(line + char_count) = 0; // to ensure the last line always ends with the null byte
if (startLine >= line_count) { // raise error if starting line specified is greater than num. of lines in doc.
fprintf(stderr, "Specified starting line is less than total lines in document.\n");
return 1;
}
if (endLine > line_count) { // adjust ending line if it is greater than number of lines in doc.
endLine = line_count;
}
if (startLine == 0) { // we will be using the starting index of 1 as the first line
startLine = 1;
}
char **linesPtr = lines + startLine - 1;
while (startLine++ <= endLine) { // print lines
printf("%s\n", *linesPtr++);
}
for (size_t i = 0; i < line_count; ++i) { // free all memory
free(*(lines + i));
}
free(lines);
return 0;
}
It is a little more convoluted, but because it uses dynamic memory allocation, it can handle lines of any length within a text file.
If there is anything unclear, please let me know and I would be happy to explain.
Hope this helps!!
several issues here,
first, you limited the length of lines to 200, not exactly what you might expect to get.
the fgets function returns lines up to specified length unless hit by newline character - this should be taken into account.
additionally, fgets returns NULL if you hit EOF - no real need to use feof.
second, you could save yourself a lot of pain and simply count the number of times you get a string, and for the times you are within the range just print it immediately. will save you a nice amount of overhead
like this:
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 200//or anything else you want
void readValues(char cFileName[75], int n, int m)
{
//Variable declaration;
char line[MAXLINE];
int i = 0;
FILE *Data;
if((Data = fopen(cFileName, "rt")) == NULL){
printf("File could not be opened");
return 1; //Can you return 1 in a void function?
}
//Read the file line by line and print within range of lines
while((line=fgets(line, MAXLINE,Data))!=NULL){//terminates upon EOF
if (++i>=n&&i<=m)
printf(""%s\n",line);
}
}
Related
Program should read list of filenames, open these files and put their handles in the array of structure, then read strings and print consecutive lines of strings to smallest files by using handles contained in array of structures.
My program puts data from all lines to only one file which is initially the smallest which is false because it should the one which is smallest with every time it prints data into the file. This is my program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
struct file_t
{
FILE* f;
int size;
}t[5];
void close_file(struct file_t* f) {
if (f == NULL || f->f == NULL) {
}
else {
fclose(f->f);
}
}
int open_file(struct file_t* f, const char* filename) {
if (f == NULL || filename == NULL) {
return 1;
}
FILE* fp;
fp = fopen(filename, "ab");
if (fp == NULL) {
return 2;
}
long int res = ftell(fp);
fclose(fp);
f->size = res;
f->f = fopen(filename, "ab+");
if (fp == NULL) {
return 2;
}
return 0;
}
struct file_t* find_min(const struct file_t* files, int size) {
if (files == NULL || size <= 0) {
return NULL;
}
int x = (files + 0)->size, i = 0, index = 0;
for (i = 0; i < size; i++) {
if ((files + i)->size <= x) {
x = (files + i)->size;
index = i;
}
}
return (struct file_t*)(files + index);
}
int main() {
puts("Input files' names:");
char tab[100];
int num = 0;
while(1==1){
if(fgets(tab, 100, stdin)==NULL||*tab=='\n'){
if (num == 0) {
printf("Couldn't open file");
return 4;
}
break;
}
int index=strlen(tab);
*(tab+index-1)='\x0';
if (strlen(tab) > 30) {
*(tab + 30) = '\x0';
}
if (open_file((t + num), tab) > 0) {
}
else {
num++;
}
}
if (num == 0) {
printf("Couldn't open file");
return 4;
}
char str[1000];
printf("Input text:");
*str = '\x0';
while (fgets(str, 1000, stdin)==NULL||*str!='\n') {
int index=strlen(str);
*(str+index-1)='\x0';
struct file_t* p = find_min(t, num);
fwrite(str, sizeof(char), strlen(str), p->f);
}
for (int i = 0; i < num; i++) {
close_file(t + i);
}
printf("File saved");
return 0;
}
There are some critical bugs that you need to resolve.
fseek(stdin, 0, SEEK_END) -- fseek normally only work on a disk file, or something reasonably similar. Please refer to this link Using fseek with a file pointer that points to stdin
As a matter of fact even fflush() won't work. fflush is something that is designed for flushing output streams, and its behavior with input streams is implementation-dependent. Please refer to this link for more details stdinflush
scanf("%[^\n]s", tab)
If you are using this in a loop or multiple times, only the first read will succeed. The reason being, the \n character is left out from the previous input, and as said earlier fflush() might not be successful in removing that \n. The further calls to scanf() will simply return without reading anything.
'\0x' If you are intending to use this as string terminator then this is not it. It is a multi-character constant with an integer value 120. Below is a vague test run
Code
#include <stdio.h>
int main()
{
if ('\0' == '\0x' )
printf("both are same\n");
printf("%d",'\0x');
}
Compilation Warnings
test.c: In function ‘main’:
test.c:5:14: warning: multi-character character constant [-Wmultichar]
5 | if ('\0' == '\0x' )
| ^~~~~
test.c:8:14: warning: multi-character character constant [-Wmultichar]
8 | printf("%d",'\0x');
| ^~~~~
Output
120
fseek(fp, 0, SEEK_END); ftell(fp); -- This should not be used to determine the file sizes. The behavior of the fseek() with SEEK_END is undetermined in the case of binary files. Please refer to this link Do not use fseek() and ftell() to compute the size of a regular file
Some Logic Errors
1) You should compute the file size every time in find_min() as it gets changed whenever you write data to the file.
2) fwrite()won't actually dump the data to file immediately. you need to call fflush().
After resolving the above issues, this is the modified code.
Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include <sys/stat.h>
struct file_t
{
FILE* f;
int size;
}t[5];
void close_file(struct file_t* f) {
if (f == NULL || f->f == NULL) {
}
else {
fclose(f->f);
}
}
int open_file(struct file_t* f, const char* filename) {
if (f == NULL || filename == NULL) {
return 1;
}
f->f = fopen(filename, "a");
if (f->f == NULL)
return 2;
struct stat statbuf;
fstat(fileno(f->f), &statbuf);
f->size = statbuf.st_size;
return 0;
}
struct file_t* find_min(const struct file_t* files, int size) {
if (files == NULL || size <= 0) {
return NULL;
}
struct stat statbuf;
fstat(fileno(files->f), &statbuf);
int x = statbuf.st_size, i = 0, index = 0;
for (i = 0; i < size; i++) {
fstat(fileno((files+i)->f), &statbuf);
if (statbuf.st_size < x) {
x = statbuf.st_size;
index = i;
}
}
return (struct file_t*)(files + index);
}
int main() {
puts("Input files' names:");
char tab[100];
int num = 0;
while(1){
int c;
while (1) {
c = getc(stdin);
if (c == EOF || c == ' ')
goto user_input;
if(c != '\n')
break;
}
tab[0] = c;
if (scanf("%[^\n]s", tab+1) == EOF)
break;
if (*tab == '\0') {
if (num == 0) {
printf("Couldn't open file");
return 4;
}
break;
}
if (strlen(tab) > 30) {
*(tab + 30) = '\0';
}
if (open_file((t + num), tab) > 0) {
}
else {
num++;
}
*tab = '\0';
}
user_input:
if (num == 0) {
printf("Couldn't open file");
return 4;
}
fflush(stdin);
char str[1000];
printf("Input text:\n");
*str = '\0';
while(1) {
int c;
while(1) {
c = getc(stdin);
if (c == EOF)
goto main_exit;
if (c != '\n')
break;
}
str[0] = c;
if (scanf("%[^\n]s", str+1) == EOF)
break;
struct file_t* p = find_min(t, num);
fwrite(str, sizeof(char), strlen(str), p->f);
fflush(p->f);
}
main_exit:
for (int i = 0; i < num; i++) {
close_file(t + i);
}
printf("File saved");
return 0;
}
Terminal Session
$ ./a.out
Input files' names:
test file1.txt
test file2.txt
' '(NOTE: Space character inputted before pressing enter.)
Input text:
this is
stackoverflow
File saved
test file1.txt
this is
test file2.txt
stackoverflow
Note for breaking from the first loop (Files input). You need to enter space and then press enter (You can tweak around this).
Where are you updating the file_t->size when you write into a file?
You are calling this:
fwrite(str, sizeof(char), strlen(str), p->f);
But after that you should do p->size += strlen(str) to update its size, otherwise all file sizes are set to initial values, and hence all strings get written to a single file.
As for getting garbage data, try printing the string you are reading from scanf in the while loop.
You are using scanf to read characters until '\n', but you are not reading the '\n' itself. You need a fseek(stdin, 0, SEEK_END); in that loop as well.
Finally, why are you using syntax like this:
(files + i)->size
When you can call it more cleanly like this:
files[i].size
You code is really hard to read because of this.
I want to read the txt line by line and store it to the array;
I am like success to store line by line in array;
I use printf("%s", loadtext[i]) with i= 0,1,2,3-20 respectively to check that things in stored in array;
but I realize that the for loop had done 1912 times when I
type printf("%d", i); right behind the for loops,
Suppose my txt is store like this:
I am a jerk
I am a noob
I am an idiot
I am done
I have another program to add new lines to the text file while this program is running.
How can I detect I am done or the new line added later is the last line to not allow the for loop do so many times?
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
int main(){
FILE *fp = fopen("abc.txt","r");
int i = 0,j=0,k=0;
char ch[10000];
char loadtext[100][100];
for (i=0; ch[i] != EOF; i++){
ch[i] = fgetc(fp);
if (ch[i] != '\n'){
loadtext[j][k] = ch[i];
k++;
}
if(ch[i] == '\n'){
loadtext[j][k] = ch[i];
k=0;
j++;
}
}
printf("%s", loadtext[0]);
printf("%s", loadtext[1]);
printf("%s", loadtext[2]);
fclose(fp);
return 0;
}
To read a whole file into an "array" of pointers to char which represent the lines, you can do:
#include <stddef.h> // size_t
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
#include <stdio.h> // malloc(), realloc(), free(), fgets()
#include <string.h> // strlen(), strcpy()
enum { BUFFER_SIZE = 30 }; // whatever line length you suspect the input file to be + 1
int main(void)
{
char const *filename = "test.txt";
FILE *is = fopen(filename, "r");
if (!is) {
fprintf(stderr, "Couldn't open \"%s\" for reading :(\n\n", filename);
return EXIT_FAILURE;
}
int result = EXIT_SUCCESS; // assume a happy ending
char buffer[BUFFER_SIZE];
size_t num_lines = 0;
char **lines = NULL;
while (fgets(buffer, sizeof(buffer), is)) {
++num_lines;
char **temp = realloc(lines, num_lines * sizeof(*lines));
if (!temp) {
fputs("Not enough memory :(\n\n", stderr);
fclose(is);
result = EXIT_FAILURE;
goto cleanup;
}
lines = temp;
size_t length = strlen(buffer);
length = strlen(buffer);
// remove a trailing newline if any:
if (length && buffer[length - 1] == '\n')
buffer[--length] = '\0';
size_t line_length = length;
lines[num_lines - 1] = malloc((length + 1) * sizeof(*lines));
if (!lines[num_lines - 1]) {
fputs("Not enough memory :(\n\n", stderr);
fclose(is);
result = EXIT_FAILURE;
goto cleanup;
}
strcpy(lines[num_lines - 1], buffer);
// as long as the buffer has been filled completely by the previous
// call to fgets() and a next call to fgets() also succeeds:
while (length == BUFFER_SIZE - 1 && fgets(buffer, sizeof(buffer), is)) {
length = strlen(buffer);
// remove a trailing newline if any:
if (length && buffer[length - 1] == '\n')
buffer[--length] = '\0';
char *temp = realloc(lines[num_lines - 1], line_length + length + 1);
if (!temp) {
fputs("Not enough memory :(\n\n", stderr);
fclose(is);
result = EXIT_FAILURE;
goto cleanup;
}
lines[num_lines - 1] = temp;
strcpy(lines[num_lines - 1] + line_length, buffer);
line_length += length;
}
}
fclose(is);
// use lines:
for (size_t i = 0; i < num_lines; ++i)
puts(lines[i]);
cleanup:
for (size_t i = 0; i < num_lines; ++i)
free(lines[i]);
free(lines);
return result;
}
Using only a fixed-size two-dimensional array and fgetc()*):
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
enum { MAX_LINES = 100, MAX_LINE_LENGTH = 100 };
int main(void)
{
char const *filename = "test.txt";
FILE *is = fopen(filename, "r");
if (!is) {
fprintf(stderr, "Couldn't open \"%s\" for reading :(\n\n", filename);
return EXIT_FAILURE;
}
char text[MAX_LINES][MAX_LINE_LENGTH + 1] = { 0 }; // zero-initialize the array
// so we don't have to care
size_t num_lines = 0; // about zero terminating
size_t current_column = 0; // every line
int ch;
// as long as we are still inside the bounds of the fixed size array
// and fgetc() doesn't return EOF
while (num_lines < MAX_LINES && current_column < MAX_LINE_LENGTH &&
(ch = fgetc(is)) != EOF)
{
if (ch == '\n') { // "move" num_lines and current_column to the next
++num_lines; // line.
current_column = 0;
continue;
}
text[num_lines][current_column++] = ch;
}
if (ch != EOF) {
fputs("The file is too big :(\n\n", stderr);
return EXIT_FAILURE;
}
for (size_t i = 0; i <= num_lines; ++i)
puts(text[i]);
}
*) Could be done with fgets() too.
to read the txt line by line and store it to the array
Code has various problems:
ch[i] != EOF accesses ch[i] before it is assigned! Undefined Behavior (UB).
char can be signed or unsigned. EOF is some negative. When char is unsigned the below is an infinite loop. When char is signed, ch[i] != EOF could exit the loop early.
printf("%s", loadtext[0]); attempts to print loadtext[0] as if it was a string. But lacking a certain null character, and thus not a string, this leading to more UB.
Lack of buffer index checks: loadtext[j][k] = ch[i]; k++; may increment k to beyond 100. Similar weak code with i.
Code may fail to open the file and no check provided.
Instead save the result from fgetc() as an int, test for for EOF, test for '\n' and append a null character to form strings.
Some ideas for alternate code:
#define LINES_N 100
#define LINE_SIZE 100
int main(void) {
FILE *fp = fopen("abc.txt", "r");
if (fp) {
// char ch[10000];
char loadtext[LINES_N][LINE_SIZE];
int ch_index = 0;
int line_count = 0;
int character;
int previous_character = '\n';
while ((character = fgetc(fp)) != EOF) {
// Starting a new line?
if (previous_character == '\n') {
if (++line_count > LINES_N) {
printf("Too many lines\n");
return EXIT_FAILURE;
}
}
loadtext[line_count - 1][ch_index++] = (char) character;
loadtext[line_count - 1][ch_index] = '\0';
if (ch_index + 1 >= LINE_SIZE) {
printf("Too long a line\n");
return EXIT_FAILURE;
}
previous_character = character;
}
if (line_count >= 1) printf("%s", loadtext[0]);
if (line_count >= 2) printf("%s", loadtext[1]);
if (line_count >= 3) printf("%s", loadtext[2]);
fclose(fp);
}
return 0;
}
I recently started at university with C programming (beginner course), and now we are doing our final examination which is about a patients' database.
I'm required to read data from a text file to a struct array (size 10000). The file contains 2 string arrays (personal identification string (10 numbers seperated by a '-') and name string), 1 int array containing photo references and 1 integer containing the amount of photo references per patient. I have tried fscanf but the program just hangs whenever i try to read, when i use fgets, it reads the whole line and stores the integers from the photo reference array into my name array (middle one). I am wondering how I should go about doing this, I've spent days trying to figure out a solution but nothing seems to work. This is what my text file looks like:
123456-1234 Name Name [1, 2, 3, 4]
234567-2345 Name2 Name2 [1, 2]
345678-3456 Name3 Name3 []
And this is my write_to_file function which writes to the file when the program exits:
void write_to_file(Patient reg[], int *pNr_of_patients){
FILE *fp;
fp=fopen("file.txt","w");
if(*pNr_of_patients>0){
int i,j;
for(i=0;i<*pNr_of_patients;i++){
fprintf(fp,"%s\t%s\t[",reg[i].pers_nr,reg[i].name);
for(j=0;j<reg[i].nr_of_ref-1;j++){
fprintf(fp,"%d, ",reg[i].photo_ref[j]);
}
if(reg[i].photo_ref[j]==0){
fprintf(fp,"]");
}else{
fprintf(fp,"%d]",reg[i].photo_ref[j]);
}
fprintf(fp,"\n");
}
fclose(fp);
}
}
This is my read_from_file function, it's missing code for reading the int array values at the end:
Edit: I added a for loop to remove the characters starting at "[" from the name string, now i just need to know how to read the array values at the end into the struct's photo reference array.
void read_from_file(Patient reg[],int *pNr_of_patients){
FILE *fp;
fp=fopen("file.txt","r");
if(fp!=NULL){
reg[*pNr_of_patients].nr_of_ref=0;
int i=0, pos;
while(fgets(reg[*pNr_of_patients].pers_nr,13,fp)!=NULL){
reg[*pNr_of_patients].pers_nr[strlen(reg[*pNr_of_patients].pers_nr)-1]='\0';
fgets(reg[*pNr_of_patients].name,31,fp);
reg[*pNr_of_patients].name[strlen(reg[*pNr_of_patients].name)-1]='\0';
for(pos=0;pos<30;pos++){
if(reg[*pNr_of_patients].name[pos]=='['){
reg[*pNr_of_patients].name[pos]='\0';
}
}
(*pNr_of_patients)++;
}
fclose(fp);
}else{
printf("File does not exist\n");
}
}
This is what my Patient struct looks like:
struct patient{
char pers_nr[12], name[30];
int photo_ref[10], nr_of_ref;
};
typedef struct patient Patient;
Calling read_from_file in main:
int main(void){
Patient patient_register[10000];
int nr_of_patients=0;
read_from_file(patient_register,&nr_of_patients);
database_management(patient_register,&nr_of_patients); //this is where I fill all the data into the array before writing to the file at the end
write_to_file(patient_register,&nr_of_patients);
return 0;
}
I think that scanning input is one of the hardest in C. That's why libraries like cs50 exists, to ease up reading input for new C users. Anyway, I constructed my solution, but I redesigned your function.
The first solution reads a single Patient from a line. It does not use sscanf the only standard call that set's errno is to strtol, which is used to convert up numbers.
The second function uses sscanf and some crazy format string construction to stay safe of buffer overflow.
It all brings down at to how the input stream is constructed and how much you trust it.
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
struct patient{
char pers_nr[12];
char name[30];
int photo_ref[10];
size_t nr_of_ref;
};
typedef struct patient Patient;
int patient_read_from_line_1(const char line[], Patient *p)
{
assert(line != NULL);
assert(p != NULL);
// check the first 12 characters ----------
// first 6 chars must be numbers
for (int i = 0; i < 6; ++i) {
if (!isdigit(line[i])) {
return -__LINE__;
}
}
// followed by a single '-'
if (line[6] != '-') {
return -__LINE__;
}
// followed by 4 numbers
for (int i = 7; i < 7 + 4; ++i) {
if (!isdigit(line[i])) {
return -__LINE__;
}
}
// followed by a space
if (line[7 + 4] != ' ') {
return -__LINE__;
}
// read up first field ---------------------
// cool first field checks out
memcpy(p->pers_nr, line, 11);
p->pers_nr[11] = '\0';
line += 12;
// let's omit spaces
while (line[0] == ' ') {
line++;
}
// read up second field --------------------------
// now we should read a two strings separated by a space
// so we should read up until a second space
if (!isalpha(*line)) {
return -__LINE__;
}
const char *pnt_first_space = strchr(line, ' ');
if (pnt_first_space == NULL) {
return -__LINE__;
}
const char *pnt_another_space = strchr(pnt_first_space + 1, ' ');
if (pnt_another_space == NULL) {
return -__LINE__;
}
const size_t name_to_read_length = pnt_another_space - line;
if (name_to_read_length > sizeof(p->name)) {
return -__LINE__;
}
memcpy(p->name, line, name_to_read_length);
p->name[name_to_read_length] = '\0';
// buh two fields done, now the array
line += name_to_read_length;
// let's omit the spaces
while (line[0] == ' ') {
line++;
}
// read up array -----------------------------------
// array
if (line[0] != '[') {
return -__LINE__;
}
line++;
for (size_t numscnt = 0;; ++numscnt) {
if (numscnt >= sizeof(p->photo_ref)/sizeof(*p->photo_ref)) {
return -__LINE__;
}
char *pnt;
errno = 0;
long num = strtol(line, &pnt, 10);
if (errno) {
return -__LINE__;
}
if (!(INT_MIN < num && num < INT_MAX)) {
return -__LINE__;
}
p->photo_ref[numscnt] = num;
line = pnt;
// omit spaces
while (*line == ' ') line++;
// now we should get a comma
if (line[0] != ',') {
// if don't get a comma, we need to get a ]
if (line[0] == ']') {
// cool
++line;
// but remember to save the count
p->nr_of_ref = numscnt + 1;
// cool
break;
}
return -__LINE__;
}
++line;
// omit spaces
while (*line == ' ') line++;
// start again
}
// this needs to be end of line or newline
if (line[0] != '\0' && line[0] != '\n') {
return -__LINE__;
}
// success!
return 0;
}
// ok, ok, ok, let's use sscanf
int patient_read_from_line_2(const char line[], Patient *p)
{
assert(line != NULL);
assert(p != NULL);
int ret;
int pos;
// read up first fiedl and half of the second ------------------
ret = sscanf(line, "%12s %30[^ ] %n", p->pers_nr, p->name, &pos);
if (ret != 2) {
return -__LINE__;
}
line += pos;
// read up another half of the second field -------------------
const size_t cur_name_len = strlen(p->name);
p->name[cur_name_len] = ' ';
char tmp[20];
ret = snprintf(tmp, 20, "%%%d[^ ] [%%n", (int)(sizeof(p->name) - cur_name_len - 1));
if (ret < 0) {
return -__LINE__;
}
ret = sscanf(line, tmp, &p->name[cur_name_len + 1], &pos);
if (ret != 1) {
return -__LINE__;
}
line += pos;
// read up array *sigh* -------------------------------------------
for (p->nr_of_ref = 0;; ++p->nr_of_ref) {
if (p->nr_of_ref >= sizeof(p->photo_ref)/sizeof(*p->photo_ref)) {
return -__LINE__;
}
ret = sscanf(line, " %d%1s%n", &p->photo_ref[p->nr_of_ref], tmp, &pos);
if (ret == 0) {
// hm...
if (line[0] == ']') {
// ach all ok, empty numbers list;
line++;
p->nr_of_ref++;
break;
}
return -__LINE__;
}
if (ret != 2) {
return -__LINE__;
}
line += pos;
if (tmp[0] != ',') {
if (tmp[0] == ']') {
// whoa! success
p->nr_of_ref++;
// cool
break;
}
return -__LINE__;
}
}
// so what's left? - EOF or newline
if (line[0] != '\0' && line[0] != '\n') {
return -__LINE__;
}
// success!
return 0;
}
long patient_read_from_file(FILE *fp, Patient patients[], size_t patients_len)
{
size_t patients_cnt = 0;
char line[256];
// for each line in file
while (fgets(line, sizeof(line), fp) != NULL) {
const int ret = patient_read_from_line_2(line, &patients[patients_cnt]);
if (ret < 0) {
// hanle reading error
return ret;
}
patients_cnt++;
if (patients_cnt > patients_len) {
// no more memory in patients left
return -__LINE__;
}
}
return patients_cnt;
}
void patient_fprintln(FILE *f, const Patient *p)
{
fprintf(f, "%s %s [", p->pers_nr, p->name);
for (size_t i = 0; i < p->nr_of_ref; ++i) {
fprintf(f, "%d", p->photo_ref[i]);
if (i + 1 != p->nr_of_ref) {
fprintf(f, ",");
}
}
fprintf(f, "]\n");
}
int main()
{
FILE *fp;
fp = stdin; // fopen("file.txt","r");
if (fp == NULL) {
return -__LINE__;
}
Patient patients[3];
const long patients_cnt = patient_read_from_file(fp, patients, sizeof(patients)/sizeof(*patients));
if (patients_cnt < 0) {
fprintf(stderr, "patient_read_from_file error %ld\n", patients_cnt);
return patients_cnt;
}
fclose(fp);
printf("Readed %d patients:\n", patients_cnt);
for (size_t i = 0; i < patients_cnt; ++i) {
patient_fprintln(stdout, &patients[i]);
}
return 0;
}
Live version available at onlinedbg.
This can be simplified for 100%. This has bugs for 100%. It is just to show what methods (strtol, memcpy, sscanf, isdigit, isalpha) are sometimes used by people to read from input. Also I specify length modifier to scanf (sscanf(..., "%12s") to handle overflows (hopefully). Try to always check return values from scanf and other standard functions (maybe checking snprintf return value is a little too much, but hey, let's be consistent). Be vary, that on some platforms the %n scanf modifier happens not to work. Also this can be build up to use dynamic allocation using malloc, realloc and free, both on line reading (basically it is equal to writing custom version of GNU getline), reading strings from input, reading int's array from input and dynamic allocations of patients.
This was meant as a comment but got too long, so I type it here.
read_from_file() appears overly complex. You might consider revisiting fscanf, reading the photo references as a whole string and then parsing into integers which you can assign to the photo_ref array. (While the code below might compile, I haven't verified that it works. It's just an idea of how one might proceed.)
void read_from_file (Patient reg[], int *pNr_of_patients)
{
FILE *fp;
fp = fopen ("file.txt", "r");
if (fp != NULL)
{
int n;
int i = 0; // position in photo_ref
char refs[30];
*pNr_of_patients = 0;
while (EOF !=
(n =
fscanf (fp, "%s %[^[]%[^]]]", reg[*pNr_of_patients].pers_nr,
reg[*pNr_of_patients].name, refs)))
{
// btw, reg[*pNr_of_patients].name may contain terminating blanks. right trim it. that's easy enough.
if (n > 2)
{ /* found photo refs.Now split the string into integers */
char *s = refs + 1; //skip '['
char *p;
while (*s && i<10){ // scan for the integers, 10 of them
while (*s && *s == ' ')
s++; // skip blanks
p = s; // mark start of number
while (*p && *p != ',')
p++;
if (*p == ',')
*p = 0;
reg[*pNr_of_patients].photo_ref[i++] = atoi (s); //tip: use strtol(3), verify that `i' isnt larger than size of the array
s = p + 1; // skip ','. Must Do: verify that `s' hasnt yet moved past the end of `ref'!!
}
}
(*pNr_of_patients)++;
}
fclose (fp);
}
else
{
printf ("File does not exist\n");
}
}
There are some good answers already, but most of them try to use a single method to parse all elements of the line. I would read whole lines into a buffer first, then use sscanf() to parse the patient number and name, but use strtok() to split the array into its individual components:
void read_from_file(Patient reg[], int *pNr_of_patients) {
FILE *fp = fopen("file.txt", "r");
if (!fp) {
fprintf(stderr, "Error opening file: %s\n", strerror(errno));
*pNr_of_patients = 0;
return;
}
char line[1024];
int i = 0;
while (fgets(line, sizeof line, fp)) {
int offset = 0;
int refs = 0;
sscanf(line, "%11s %29[^[] [%n", ®[i].pers_nr, ®[i].name, &offset);
for (char *tok = strtok(line + offset, ","); tok && refs < 10; tok = strtok(NULL, ",")) {
if (*tok != ']')
reg[i].photo_ref[refs++] = atoi(tok);
}
reg[i].nr_of_ref = refs;
i++;
}
*pNr_of_patients = i;
}
Divide and Conquer
Break this down into steps. Make a function that populates 1 Patient.
The below is untested code. Consider it a starting point. The deign goal is to make a function that reads 1 line into 1 Patient.
Read in 1 entire line
// return 1: success, 0: failure EOF:end-of-file
int read_once_from_file(FILE *stream, Patient *pat_ptr) {
Patient pat = { 0 };
char buffer[100 + 30*13];
if (fgets(buffer, sizeof buffer, stream) == NULL) {
return EOF;
}
Parse the first part. Use "%n" which records the parsing offset. Use width limits on string input.
int n = 0;
if (sscanf(buffer, " %11[^\t] %29[^\t] [ %n", pat.pers_nr, pat.name) != 2) {
return 0; // improper formatted input
}
char *p = buffer + n;
Now look for ']' and photo_ref
if (*p != ']') {
for (pat.nr_of_ref=0; ; pat.nr_of_ref++) {
if (sscanf(p, "%d %n", &pat.photo_ref[i], &n) != 1) {
return 0; // improper formatted input
}
p += n;
if (*p == ']') {
pat.nr_of_ref++;
break;
}
if (*p != ',' || pat.nr_of_ref + 1 == 10) {
return 0; // improper formatted input
}
p++;
}
}
Save result
*pat_ptr = pat;
return 1;
}
Call read_once_from_file() as needed
void read_from_file(Patient reg[],int *pNr_of_patients){
*pNr_of_patients = 0;
FILE *fp = fopen("file.txt","r");
if(fp){
for (int i = 0; i<10000; i++) {
int count = read_once_from_file(fp, ®[i]);
if (count == EOF) {
break;
}
if (count != 1) {
// error
fprintf(stderr, "Input error\n");
break;
}
}
*pNr_of_patients = i;
fclose(fp);
}
}
I need to read data store in end of file and print it.
My input file has many numbers, and I have to read last number,
can any one help me??
int main()
{
FILE *fp;
fp = fopen("f:\\Issuance.csv", "a");
if (!fp)
{
printf("can not open file \n");
getchar();
exit(1);
}
int Size = 30;
char FileInfo[100];
fseek( fp , 0 , SEEK_END);
fread(FileInfo, 1, Size, fp);
printf("%d",FileInfo);
}
fcloseall();
}
You need to use the 2nd parameter of fseek().
fseek(fp, -Size, SEEK_END);
fread(FileInfo, 1, Size, fp);
FileInfo[Size] = '\0'; // NULL terminate FileInfo; or declare as char FileInfo[100] = {0};
printf("%s", FileInfo);
To read the last number in a text file (which may have additional junk after it), starting from the beginning, attempt to read a number. If successful, save it, else toss 1 char. Continue until the end of the file.
// Read last number
int ReadLastNumber(FILE *inf, int default_value) {
int last = default_value;
int num;
int cnt;
rewind(inf);
while ((cnt = fscanf(inf,"%d", &num)) != EOF) {
if (cnt == 1) {
last = num;
} else {
fgetc(inf); // toss non-numeric char
}
}
return last;
}
A more sane solution would fseek() to the end, search backwards for digits. Once some digits are found, continue backwards looking for digits, +or -. Something like the following untested code.
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
// Read last number
int ReadLastNumber2(FILE *inf, int default_value) {
int last = 0;
int place = 1;
bool digit_found = false;
long offset = -1;
while (fseek(inf, offset, SEEK_CUR) == 0) {
int ch = fgetc(inf);
if (ch == EOF) // Likely I/O error
return default_value;
if (isdigit(ch)) {
digit_found = true;
last += (ch - '0')*place;
place *= 10;
offset = -2;
} else if (ch == '-') {
return -last;
} else if (digit_found) {
return last;
}
}
return default_value;
}
Not protected against int overflow.
i am working in file system in that i am counting paragraph from the file but
i am not getting please suggest me how can i do that i tried this but not getting what i want
int main()
{
FILE *fp=fopen("200_content.txt ","r");
int pCount=0;
char c;
while ((c=fgetc(fp))!=EOF)
{
if(c=='\n'){pCount++;}
else{continue;}
}
printf("%d",pCount);
return 0;
}
You should declare c as int instead of char.
Also, remember to fclose(fp); before main() returns.
A paragraph contains two subsequent '\n's, use a variable for counting the two '\n's, like this,
int main()
{
FILE *fp=fopen("200_content.txt ","r");
int pCount=0;
char c;
int newln_cnt=0;
while ((c=fgetc(fp))!=EOF)
{
if(c=='\n')
{
newln_cnt++;
if(newln_cnt==2)
{
pCount++;
newln_cnt=0;
}
}
else{continue;}
}
printf("%d",pCount);
return 0;
}
You code counts the number of newline '\n' characters, not empty line which demarcates the paragraphs. Use fgets to read lines from the file. I suggest this -
#include <stdio.h>
// maximum length a line can have in the file.
// +1 for the terminating null byte added by fgets
#define MAX_LEN 100+1
int main(void) {
char line[MAX_LEN];
FILE *fp = fopen("200_content.txt", "r");
if(fp == NULL) {
printf("error in opening the file\n");
return 1;
}
int pcount = 0;
int temp = 0;
while(fgets(line, sizeof line, fp) != NULL) {
if(line[0] == '\n') {
// if newline is found and temp is 1 then
// this means end of the paragraph. increase
// the paragraph counter pcount and set temp to 0
if(temp == 1)
pcount++;
temp = 0;
}
else {
// if a non-empty line is found, this means
// the start of the paragraph
temp = 1;
}
}
// if the last para doesn't end with empty line(s)
if(temp == 1)
pcount++;
printf("number of para in the file is %d\n", pcount);
return 0;
}
For starters, I assume that you consider a new line to be a new paragraph.
i.e.
This is line 1.
This is line 2.
has 2 paragraphs.
What your code does is neglect the case where there is an EOF and not a newline character (\n) after This is line 2.
One way to fix this is to use an extra char variable.
int main()
{
FILE *fp=fopen("200_content.txt ","r");
int pCount=0;
char c; // char that checks
char last_c; //record of the last character read in the loop
while ((c=fgetc(fp))!=EOF)
{
if(c=='\n'){pCount++;}
last_c = c;
else{continue;} //this line is redundant. You can remove it
}
if (last_c != '\n') pCount++; //if EOF at the end of line and not '\n'
printf("%d",pCount);
return 0;
}
void analyze_file(const char *filename) {
FILE* out_file;
out_file = fopen(filename,"r");
int size;
if(out_file == NULL)
{
printf("Error(analyze_file): Could not open file %s\n",filename);
return;
}
fseek(out_file,0,SEEK_SET);
char ch,ch1;
int alpha_count = 0,num_count = 0,non_alnum =0,charac=0;
int word_count =0,line=0;
int para=0;
while(!feof(out_file))
{
ch = fgetc(out_file);
if (isalpha(ch))
alpha_count++;
else if(isdigit(ch))
num_count++;
else if(!isalnum(ch) && ch!='\n' && !isspace(ch))
++non_alnum;
else if(ch=='\n')
{ line++;
ch1 = fgetc(out_file);// courser moves ahead , as we read
fseek(out_file,-1,SEEK_CUR); // bringing courser back
}
else if(ch == ch1)
{para++; //paragraph counter
word_count--;
}
if(ch==' '||ch=='\n')
{
word_count++;
}
if(ch==EOF)
{
word_count++;line++;para++;
}
}
non_alnum -=1;// EOF character subtracted.
charac = alpha_count + non_alnum + num_count;
fclose(out_file);
printf("#Paragraphs = %d\n",para);
printf("#lines = %d\n",line);
printf("#Words = %d\n",word_count);
printf("#Characters = %d\n",charac);
printf("Alpha = %d\n",alpha_count);
printf("Numerical = %d\n",num_count);
printf("Other = %d\n",non_alnum);
printf("\n");
return;
}