Adi said:
Hello eveyone,
I wanna ask a very simple question here (as it was quite disturbing
me for a long time.)
My problem is to read a file line by line. I've tried following
implementations but still facing problems:
Assume that
FILE* filePointer;
unsigned char lineBuffer[256];
1) Using fscanf: fscanf ( filePointer, "%[^\n]", lineBuffer);
The problem with this code is that fscanf behaviour is quite
unexpected. It sometimes increments the file pointer automatically and
sometime goes on reading the same line forerver.
/* BEGIN pops_device.c */
/*
** If rc equals 0, then an empty line was entered
** and the array contains garbage values.
** If rc equals EOF, then the end of file was reached
** or there is some other problem.
** If rc equals 1, then there is a string in array.
** Up to LENGTH number of characters are read
** from a line of a text file or stream.
** If the line is longer than LENGTH,
** then the extra characters are discarded.
*/
#include <stdio.h>
#define LENGTH 12
#define str(x) # x
#define xstr(x) str(x)
int main(void)
{
int rc;
char array[LENGTH + 1];
puts("The LENGTH macro is " xstr(LENGTH));
fputs("Enter a string with spaces:", stdout);
fflush(stdout);
rc = fscanf(stdin, "%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getchar();
}
while (rc == 1) {
printf("Your string is:%s\n\n"
"Hit the Enter key to end,\nor enter "
"another string to continue:", array);
fflush(stdout);
rc = fscanf(stdin, "%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getchar();
}
if (rc == 0) {
*array = '\0';
}
}
return 0;
}
/* END pops_device.c */
2) Using fgets: fgets ( lineBuffer, 256, filePointer);
The problem here is that fgets also stores the '\n' in the
lineBuffer but what i need is just the complete line delimited by '\0'
before the end-of-line. Reason is that the file i'm reading might be a
DOS file where end-of-line is delimited by '\r\n'. So i want to skip
these end-of-line charater(s) no matter in which environment/OS the
file lies.
/* BEGIN products.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#define ARRAYSIZE 12
#define TAX(S) (0.06 * (S))
#define LIST { \
{"milk" , 2.59}, \
{"candy" , 1.21}, \
{"meat" , 1.69}, \
{"juice" , 1.29}, \
{"fruit" , 2.33} \
}
#define N_PRODUCTS (sizeof items / sizeof *items)
struct product {
char *name;
double price;
};
void Intro(void);
void Prompt(struct product *, size_t);
void Receipt(struct product *, size_t, unsigned *);
size_t OK_input(char *, size_t);
size_t OK_unsigned(long unsigned *);
int main(void)
{
struct product items[] = LIST;
unsigned quantity[N_PRODUCTS];
long unsigned quantity_temp;
size_t index = 0;
Intro();
do {
Prompt(items, index);
if (OK_unsigned(&quantity_temp)) {
quantity[index++] = quantity_temp;
}
} while (index != N_PRODUCTS);
Receipt(items, index, quantity);
return 0;
}
void Intro(void)
{
putchar('\n');
}
void Prompt(struct product *items, size_t index)
{
printf("How much %s would you like to order: ",
items[index].name);
fflush(stdout);
}
void
Receipt(struct product *items,
size_t n_products, unsigned *quantity)
{
size_t index;
double subtotal, increase, tax;
puts("\n"
"Item Price Quantity T-Price\n"
"-----------------------------------------");
for (subtotal = index = 0; index != n_products; ++index) {
increase = quantity[index] * items[index].price;
subtotal += increase;
printf("%-7s%6.2f%12u%16.2f\n",
items[index].name,
items[index].price, quantity[index], increase);
}
tax = TAX(subtotal);
printf("\n"
"%13s%-11s%17.2f\n"
"%13s%-11s%17.2f\n"
"%13s%-11s%17.2f\n",
"", "Subtotal ", subtotal,
"", "Sales Tax ", tax,
"", "Total Sales", subtotal + tax);
}
size_t OK_unsigned(long unsigned *quantity_ptr)
{
char array[ARRAYSIZE] = {'\0'};
size_t length;
length = OK_input(array, sizeof array);
if (length) {
if (strchr(array, '-')){
fputs("\nDon't use - in the number.\n", stderr);
length = 0;
} else {
char *endptr;
errno = 0;
*quantity_ptr = strtoul(array, &endptr, 10);
if (array != endptr - length) {
if (isdigit(endptr[-1])) {
size_t spindex = 0;
while (isspace(endptr[spindex])) {
++spindex;
if (endptr[spindex] == '\n') {
return 1;
}
}
}
fprintf(stderr, "\n"
"Don't use %-2s in the number.\n\n",
isspace(*endptr) ? "\bblank spaces"
: isprint(*endptr) ? endptr[1] = '\0', endptr
: "\bnonprinting characters");
length = 0;
} else {
if (errno || *quantity_ptr > ~0u) {
fprintf(stderr,"\n"
"Enter a number less than or equal "
"to %u.\n\n", ~0u);
length = 0;
}
}
}
}
return length;
}
size_t OK_input(char *array, size_t arraysize)
{
size_t length;
if (!fgets(array, arraysize, stdin) || feof(stdin)) {
if (ferror(stdin)) {
fputs("\n\n\nferror 1\n\n", stderr);
exit(EXIT_FAILURE);
}
if (strlen(array)) {
fputs("\n\n\nfeof 1\n", stderr);
exit(EXIT_FAILURE);
} else {
puts("\n\n\nDon't do that!\n");
clearerr(stdin);
length = 0;
}
} else {
length = strlen(array) - 1;
if (length && (array[length] != '\n')) {
do {
if(!fgets(array, arraysize, stdin) || feof(stdin)){
if (ferror(stdin)) {
fputs("\n\n\nferror 2\n", stderr);
exit(EXIT_FAILURE);
} else {
fputs("\n\n\nfeof 2\n", stderr);
exit(EXIT_FAILURE);
}
} else {
length = strlen(array) - 1;
}
} while (array[length] != '\n');
printf("\n"
"Don't type more than %lu character%s\n"
"before hitting the Enter key.\n\n",
arraysize - 2lu, arraysize == 3 ? "" : "s");
length = 0;
}
}
return length;
}
/* END products.c */
Is there any other way too to avoid this problem??
/* BEGIN line_to_string.c */
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
struct list_node {
struct list_node *next;
void *data;
};
int line_to_string(FILE *fp, char **line, size_t *size);
void list_free(struct list_node *node, void (*free_data)(void *));
void list_fprint(FILE *stream, struct list_node *node);
struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data);
int main(void)
{
struct list_node *head, *tail;
int rc;
char *buff_ptr;
size_t buff_size;
long unsigned line_count;
puts(
"\nThis program makes and prints a list of all the lines\n"
"of text entered from standard input.\n"
"Just hit the Enter key to end,\n"
"or enter any line of characters to continue."
);
tail = head = NULL;
line_count = 0;
buff_size = 0;
buff_ptr = NULL;
while ((rc = line_to_string(stdin, &buff_ptr, &buff_size)) > 1) {
++line_count;
tail = string_node(&head, tail, buff_ptr);
if (tail == NULL) {
break;
}
puts(
"\nJust hit the Enter key to end,\n"
"or enter any other line of characters to continue."
);
}
switch (rc) {
case EOF:
if (buff_ptr != NULL && strlen(buff_ptr) > 0) {
puts("rc equals EOF\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = string_node(&head, tail, buff_ptr);
}
break;
case 0:
puts("realloc returned a null pointer value");
if (buff_size > 1) {
puts("rc equals 0\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = string_node(&head, tail, buff_ptr);
}
break;
default:
break;
}
if (line_count != 0 && tail == NULL) {
puts("Node allocation failed.");
puts("The last line entered didn't make it onto the list:");
puts(buff_ptr);
}
free(buff_ptr);
puts("\nThe line buffer has been freed.\n");
printf("%lu lines of text were entered.\n", line_count);
puts("They are:\n");
list_fprint(stdout, head);
list_free(head, free);
puts("\nThe list has been freed.\n");
return 0;
}
int line_to_string(FILE *fp, char **line, size_t *size)
{
int rc;
void *p;
size_t count;
count = 0;
while ((rc = getc(fp)) != EOF) {
++count;
if (count + 2 > *size) {
p = realloc(*line, count + 2);
if (p == NULL) {
if (*size > count) {
(*line)[count] = '\0';
(*line)[count - 1] = (char)rc;
} else {
ungetc(rc, fp);
}
count = 0;
break;
}
*line = p;
*size = count + 2;
}
if (rc == '\n') {
(*line)[count - 1] = '\0';
break;
}
(*line)[count - 1] = (char)rc;
}
if (rc != EOF) {
rc = count > INT_MAX ? INT_MAX : count;
} else {
if (*size > count) {
(*line)[count] = '\0';
}
}
return rc;
}
void list_free(struct list_node *node, void (*free_data)(void *))
{
struct list_node *next_node;
while (node != NULL) {
next_node = node -> next;
free_data(node -> data);
free(node);
node = next_node;
}
}
void list_fprint(FILE *stream, struct list_node *node)
{
while (node != NULL) {
fputs(node -> data, stream);
putc('\n', stream);
node = node -> next;
}
}
struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data)
{
struct list_node *node;
node = malloc(sizeof *node);
if (node != NULL) {
node -> next = NULL;
node -> data = malloc(strlen(data) + 1);
if (node -> data != NULL) {
if (*head == NULL) {
*head = node;
} else {
tail -> next = node;
}
strcpy(node -> data, data);
} else {
free(node);
node = NULL;
}
}
return node;
}
/* END line_to_string.c */
/* BEGIN type_1.c */
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#define ARGV_0 "type_1"
int line_to_string(FILE *fp, char **line, size_t *size);
int main(int argc, char *argv[])
{
int rc;
FILE *fd;
char *buff_ptr;
size_t buff_size;
buff_size = 0;
buff_ptr = NULL;
if (argc > 1) {
while (*++argv != NULL) {
fd = fopen(*argv, "r");
if (fd != NULL) {
while ((rc = line_to_string
(fd, &buff_ptr, &buff_size)) > 0)
{
switch (rc) {
case EOF:
if (buff_ptr != NULL
&& strlen(buff_ptr) > 0)
{
puts("rc equals EOF\n"
"The string in buff_ptr is:");
puts(buff_ptr);
}
break;
case 0:
puts("realloc returned a null pointer "
"value in line_to_string.");
if (buff_size > 1) {
puts("rc equals 0\n"
"The string in buff_ptr is:");
puts(buff_ptr);
}
break;
default:
puts(buff_ptr);
break;
}
}
fclose(fd);
} else {
fprintf(stderr,
"\nfopen() problem with \"%s\"\n", *argv);
break;
}
}
free(buff_ptr);
} else {
puts(
"Usage:\n>" ARGV_0
" <FILE_0.txt> <FILE_1.txt> <FILE_2.txt> ...\n"
);
}
return 0;
}
int line_to_string(FILE *fp, char **line, size_t *size)
{
int rc;
void *p;
size_t count;
count = 0;
while ((rc = getc(fp)) != EOF) {
++count;
if (count + 2 > *size) {
p = realloc(*line, count + 2);
if (p == NULL) {
if (*size > count) {
(*line)[count] = '\0';
(*line)[count - 1] = (char)rc;
} else {
ungetc(rc, fp);
}
count = 0;
break;
}
*line = p;
*size = count + 2;
}
if (rc == '\n') {
(*line)[count - 1] = '\0';
break;
}
(*line)[count - 1] = (char)rc;
}
if (rc != EOF) {
rc = count > INT_MAX ? INT_MAX : count;
} else {
if (*size > count) {
(*line)[count] = '\0';
}
}
return rc;
}
/* END type_1.c */