Reading from a file causes an error at the last line - c

The problem that I have is that, once I want to read the file, the program prints out everything, but it doesn't read the last element of the file.
So in the file lets say I have:
admin:pass:1000:off
test:testxx:1000:off
ret:passx:1000:off
It prints out the first 2 lines and then it says "End of the buffer" (its just a printf that I put into the code in order to understand where is the problem).
After printing out "End of the buffer" it prints out the last line..
What am I doing wrong?
#include <stdlib.h> /* exit */
#include <errno.h> /* perror */
#include <stdio.h>
#include <unistd.h> /* write, read, close*/
#include <sys/types.h>
#include <sys/stat.h> /*open */
#include <fcntl.h>
#include <string.h>
#define MAX 500
struct utente{
char username[MAX];
char password[MAX];
int gettoni;
char stato[MAX];
struct utente *next;
};
void menu(int);
struct utente* lettura_file(struct utente *);
struct utente* ins_coda(struct utente *t, char username[], char password[], char gettoni[], char stato[]);
void stampa_lista(struct utente*);
struct utente* elabora(struct utente *top, char buffer[]);
int check(struct utente *, char *);
void aggiornamento_file(struct utente *top);
int main(){
while(1){
char scelta[MAX], stringa[MAX];
int i=0, res=0;
struct utente *TOP=NULL;
printf("1. STAMPA LISTA\nInserisci scelta --> ");
scanf("%s", scelta);
i=atoi(scelta);
switch(i){
case 1:
TOP=lettura_file(TOP);
stampa_lista(TOP);
/*printf("Inserisci stringa da trovare: ");
scanf("%s", stringa);
res=check(TOP, stringa);
if(res==1)
aggiornamento_file(TOP);
*/
break;
}
}
return 0;
}
struct utente* lettura_file(struct utente *top){
int fd, nread;
char buffer[MAX];
fd=open("utenti.txt", O_RDONLY);
if(fd==-1){
perror("ERRORE APERTURA FILE");
return top;
}
while((nread=read(fd, buffer, MAX)>0)){
top=elabora(top,buffer);
}
close(fd);
return top;
}
struct utente* elabora(struct utente *top, char buffer[]){
char username[MAX], password[MAX], gettoni[MAX], stato[MAX];
int i=0;
int j=0; //indice username
int k=0; //indice password
int t=0; //indice gettoni
int x=0; //indice stato
int count=0;
printf("Inzio buffer:\n%sfine buffer\n" ,buffer);
while(buffer[i]!='\0'){
//ELABORO NOME
while(buffer[i]!=':'){
username[j]=buffer[i];
j++;
i++;
}
username[j]='\0';
i++;
//ELABORO COGNOME
while(buffer[i]!=':'){
password[k]=buffer[i];
k++;
i++;
}
password[k]='\0';
i++;
//ELABORO GETTONI
while(buffer[i]!=':'){
gettoni[t]=buffer[i];
t++;
i++;
}
gettoni[t]='\0';
i++;
while(buffer[i]!='\n' && buffer[i]!='\0'){
stato[x]=buffer[i];
x++;
i++;
}
stato[x]='\0';
if(buffer[i]=='\n')
i++;
if(buffer[i]=='\0'){
printf("\nEnd of the buffer %c\n", buffer[i]);
printf("Fine%s %s %s %s\n\n", username, password, gettoni, stato);
top=ins_coda(top, username, password, gettoni, stato);
return top;
}
printf("Utente %d, %s %s %s %s\n\n", count, username, password, gettoni, stato);
top=ins_coda(top, username, password, gettoni, stato);
bzero(username, MAX);
bzero(password, MAX);
bzero(gettoni, MAX);
bzero(stato, MAX);
j=0;
k=0;
t=0;
x=0;
}
return top;
}
struct utente* ins_coda(struct utente *t, char username[], char password[], char gettoni[], char stato[]){
struct utente *p, *top;
int n_gettoni;
p=(struct utente*)malloc(sizeof(struct utente));
strcpy(p->username, username);
strcpy(p->password, password);
n_gettoni=atoi(gettoni);
p->gettoni=n_gettoni;
strcpy(p->stato, stato);
p->next=NULL;
if(t==NULL)
return p;
top=t;
while(t->next!=NULL)
t=t->next;
t->next=p;
return top;
}
void stampa_lista(struct utente *TOP){
int i=1;
while(TOP!=NULL){
printf("Utente %d:\n Username: %s\nPassword: %s\nGEttoni: %d\nStato: %s\n", i, TOP->username, TOP->password, TOP->gettoni, TOP->stato);
TOP=TOP->next;
i++;
}
return;
}
int check(struct utente *top, char stringa[]){
int len=strlen(stringa);
while(top!=NULL){
if(strncmp(top->username, stringa, len)==0){
strcpy(top->stato, "on");
printf("OK\n");
return 1;
}else{
printf("KO\n");
return 0;
}
top=top->next;
}
return 0;
}
void aggiornamento_file(struct utente *top){
char username[MAX], password[MAX], gettoni[MAX], stato[MAX]="";
int fd;
fd = open("utenti.txt", O_WRONLY |O_TRUNC, S_IRUSR | S_IWUSR);
if(fd==-1){
perror("ERROR apertura file utenti!");
exit(1);
}else{
while(top!=NULL){
strcpy(username, top->username);
strcpy(password, top->password);
sprintf(gettoni, "%d", top->gettoni);
strcpy(stato, top->stato);
write(fd, username, strlen(username));
write(fd, ":", 1);
write(fd, password, strlen(password));
write(fd, ":", 1);
write(fd, gettoni, strlen(gettoni));
write(fd, ":", 1);
write(fd, stato, strlen(stato));
write(fd, "\n", 1);
top=top->next;
}
}
close(fd);
return;
}

Unless your file contains binary data and you actually put a 0x00 at the end of it, it shouldn't contain any zeros for the buffer[i] != '\0' check.
C strings are NULL terminated, but it's not usual to include this terminating zero when writing to text files. Text file editors don't do that either.
One problem is that you are not initializing the buffer variable.
Probably you are getting zeros by chance when the program allocates the 500 bytes for it.
One way you can initialize a buffer with zeros is by setting it's first element to zero at the declaration:
char buffer[MAX] = {0,};
Now, if I understood correctly your doubt, I don't see that the end of the file is happening before the program reads the last entry. What is happening there is that the program is printing the message with the last line after it prints the message of the end of the file. But the program did read the last line before the end of the file and stored it in the variables used by printf.
Confusion typically arises in codes with big functions. elabora function is big, complicated and hard to debug. You can make it simpler if you break it in smaller functions. I believe it gets much better if it reads lines instead of the whole file. I rewrote it quickly as an example:
void get_field(char **src, char dst[MAX])
{
int j = 0;
while (*(*src) && *(*src) != ':' && *(*src) != '\n') {
dst[j++] = *(*src);
(*src)++;
}
}
struct utente* elabora(struct utente *top, char *line, ssize_t line_len, int *count)
{
char username[MAX] = {0,};
char password[MAX] = {0,};
char gettoni[MAX] = {0,};
char stato[MAX] = {0,};
get_field(&line, username);
get_field(&line, password);
get_field(&line, gettoni);
get_field(&line, stato);
printf("Utente %d, %s %s %s %s\n\n", ++(*count), username, password, gettoni, stato);
top = ins_coda(top, username, password, gettoni, stato);
return top;
}
It can now be called from a function that reads the file by lines using geline function:
struct utente* lettura_file(struct utente *top)
{
FILE *file = NULL;
char *buffer = NULL;
size_t buffer_size = 0;
int count = 0;
ssize_t n_read = 0;
if ((file = fopen("utenti.txt", "r")) == NULL) {
perror("ERRORE APERTURA FILE");
return top;
}
while ((n_read = getline(&buffer, &buffer_size, file)) > 0) {
top = elabora(top, buffer, n_read, &count);
}
if (buffer != NULL)
free(buffer);
if (file != NULL)
fclose(file);
return top;
}
These functions are still not safe, but I believe they have better readability and tend to be easier do debug. strtok would be better than parsing tokens this way. Using other functions for opening and reading the file, as suggested by the questions' comments, would also improve the code.

Related

Can anyone help? I am trying to read data from a file but its just spitting out garbage

I am trying to read from file hw4.data and see if it has a name. The user inputs the name via a command line argument. Everything works fine but I can't get the file to be passed between the functions correctly. The assignment requires that I define the file in main and pass it between SCAN and LOAD.
#include <stdio.h>
#include <stdlib.h>
struct _data {
char name[20];
long number;
};
int SCAN(FILE *(*stream)) { // skim through the file and find how many entries there are
int size = 0;
char s_temp[100];
long l_temp;
while (1) {
fscanf(*stream, "%s %ld", s_temp, &l_temp);
if (feof(*stream)) break;
size++;
}
return size;
}
struct _data* LOAD(FILE *stream, int size) { // loop through the file and load the entries into the main data array
struct _data* d = malloc(size * sizeof(struct _data));
int i;
for (i = 0; i < size; i++) {
fscanf(stream, "%s %ld", d[i].name, &d[i].number);
}
return d;
}
void SEARCH(struct _data *BlackBox, char* name, int size) { // loop through the array and search for the right name
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\nThe name was found at the %d entry.\n*******************************************\n", i);
found = 1;
break;
}
}
if (found == 0) {
printf("*******************************************\nThe name was NOT found.\n*******************************************\n");
}
}
void FREE(struct _data* BlackBox, int size) { // free up the dynamic array
free(BlackBox);
}
int main(int argv, char* argc[]) {
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE* file = fopen("./hw4.data", "r");
int size = SCAN(&file);
struct _data* data = LOAD(&file, size);
SEARCH(data, argc[1], size);
fclose(file);
return 0;
} else {
printf("*******************************************\n* You must include a name to search for.*\n*******************************************\n");
return 0;
}
}
Here's the format of hw4.data
ron 7774013
jon 7774014
tom 7774015
won 7774016
A few issues:
In SCAN, remove the feof. Replace with: if (fscanf(*stream, "%s %ld", s_temp, &l_temp) != 2) break;
Note that after calling SCAN, you should do: rewind(file);. Otherwise, LOAD will only see [immediate] EOF.
And, as others have mentioned, just pass file to SCAN/LOAD and not &file.
Add a check for null return from fopen (e.g.) if (file == NULL) { perror("fopen"); exit(1); }
Stylistically:
If you have a comment describing a function, put it on the line above the function.
Try to keep lines within 80 chars
Here is the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _data {
char name[20];
long number;
};
// skim through the file and find how many entries there are
int
SCAN(FILE *stream)
{
int size = 0;
char s_temp[100];
long l_temp;
while (1) {
if (fscanf(stream, "%s %ld", s_temp, &l_temp) != 2)
break;
size++;
}
return size;
}
// loop through the file and load the entries into the main data array
struct _data *
LOAD(FILE *stream, int size)
{
struct _data *d = malloc(size * sizeof(struct _data));
int i;
for (i = 0; i < size; i++) {
fscanf(stream, "%s %ld", d[i].name, &d[i].number);
}
return d;
}
// loop through the array and search for the right name
void
SEARCH(struct _data *BlackBox, char *name, int size)
{
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\n");
printf("The name was found at the %d entry.\n", i);
printf("*******************************************\n");
found = 1;
break;
}
}
if (found == 0)
printf("*******************************************\n"
"The name was NOT found.\n"
"*******************************************\n");
}
// free up the dynamic array
void
FREE(struct _data *BlackBox, int size)
{
free(BlackBox);
}
int
main(int argv, char *argc[])
{
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE *file = fopen("./hw4.data", "r");
if (file == NULL) {
perror("fopen");
exit(1);
}
int size = SCAN(file);
rewind(file);
struct _data *data = LOAD(file, size);
SEARCH(data, argc[1], size);
fclose(file);
}
else
printf("*******************************************\n"
"* You must include a name to search for.*\n"
"*******************************************\n");
return 0;
}
Using realloc, we can combine SCAN and LOAD into a single function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _data {
char name[20];
long number;
};
// loop through the file and load the entries into the main data array
struct _data *
LOAD(FILE *stream, int *sizep)
{
struct _data *all = NULL;
struct _data *d;
int size = 0;
int capacity = 0;
while (1) {
if (size >= capacity) {
capacity += 10;
all = realloc(all,sizeof(*all) * capacity);
if (all == NULL) {
perror("realloc");
exit(1);
}
}
d = &all[size++];
if (fscanf(stream, "%s %ld", d->name, &d->number) != 2)
break;
}
// trim to size actually used
all = realloc(all,sizeof(*all) * size);
*sizep = size;
return all;
}
// loop through the array and search for the right name
void
SEARCH(struct _data *BlackBox, char *name, int size)
{
int i;
int found = 0;
for (i = 0; i < size; i++) {
printf("%s %s\n", BlackBox[i].name, name);
if (strcmp(BlackBox[i].name, name) == 0) {
printf("*******************************************\n");
printf("The name was found at the %d entry.\n", i);
printf("*******************************************\n");
found = 1;
break;
}
}
if (found == 0)
printf("*******************************************\n"
"The name was NOT found.\n"
"*******************************************\n");
}
// free up the dynamic array
void
FREE(struct _data *BlackBox, int size)
{
free(BlackBox);
}
int
main(int argv, char *argc[])
{
if (argv == 2) {
printf("The argument supplied is %s\n", argc[1]);
FILE *file = fopen("./hw4.data", "r");
if (file == NULL) {
perror("fopen");
exit(1);
}
int size;
struct _data *data = LOAD(file, &size);
SEARCH(data, argc[1], size);
fclose(file);
}
else
printf("*******************************************\n"
"* You must include a name to search for.*\n"
"*******************************************\n");
return 0;
}
I had to use rewind() in order to reset the file so that LOAD() would read from the start of the file and give good data.

INI file reader function is addressing wrong values to my structure, How do I solve this?

This is for a project for university (small replica of a Catan game) and I'm struggling a bit with this part, we have the read an INI file with fairly simple formatting, it only has some comments starting with ';' and then it's just tags with a value in front:
xdim=4
ydim=5
N=D
S=L2
E=S10
W=D
etc...
I have this function to read from an INI file and address the read values to the correct struct element. But it seems like it doesn't even read the file, the struct is a simple struct with xdim and ydim, after I call the func xdim is '&d&d&d&d etc...' and ydim is 0
I've tried placing in some printf's just to see if the values from the INI file itself where being read wrong, but nothing is printed.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 128
typedef struct UNIT { /**struct used in an array for the rest of the INI values*/
char N[4];
char S[4];
char W[4];
char E[4];
char Building;
}UNIT;
typedef struct{ /**This is declared in main and passed to the functions*/
UNIT *grid;
unsigned int xdim;
unsigned int ydim;
} MAP_CONFIG;
void set_config_val(MAP_CONFIG *config, const char *key, int val) {
if (config == NULL)
return;
if (strcmp(key, "xdim") == 0){
printf("here");
config->xdim = val;
}
else if (strcmp(key, "ydim") == 0){
printf("here");
config->ydim = val;
}
else{
;
}
}
void read_config(MAP_CONFIG *config,FILE *f) {
char str[MAX];
char *token;
const char *delim = "=\n";
while (1) {
fgets(str, MAX, f);
if(feof(f)!= 0) break;
puts(str);
if (strchr(str, '=')!=NULL) {
char varname[MAX];
int value;
token = strtok(str, delim);
strcpy(varname, token);
token = strtok(NULL, delim);
value = atoi(token);
printf("&d", token);
set_config_val(config, varname, value);
}
}
config = malloc(sizeof(MAP_CONFIG));
config->grid = calloc(config->xdim * config->ydim, sizeof(UNIT));
close(f);
return;
}
open file function:
FILE *openFile(char *nome, char *mode) {
FILE *f;
printf("Opening file %s\n", nome);
f = fopen(nome, mode);
if (f == NULL) {
fprintf(stderr, "*** It was not possible to open the file %s.", nome);
exit(1);
}
return f;
}
test main im using:
int main(int argc, char **argv) {
MAP_CONFIG map;
MAP_CONFIG *mapa = &map;
FILE *f;
char *filename;
for (int i = 0; i < argc; i++)
printf("Parametro %d: %s\n", i, argv[i]);
if (argc >= 2) {
filename = argv[1];
}
else {
printf("Opening base map file..\n");
filename = "mapa.ini";
}
f = openFile(filename, "r");
read_config(mapa, f);
printf("%d %d", map.xdim, map.ydim);
return 0;
}
I just want it to read the xdim and ydim, and then repeat the process to an array of structs for each struct to get the correct value of the N,S,E,W present in the INI file... Help!

Reading lines from file

I am trying to read strings and integers from a simple text file to my array. But the problem is that I get some random characters in a line in the middle of my list. It probably has to do with a newline problem, but I am not sure. The text file looks like this:
4
Mr Tambourine Man
Bob Dylan
1965
Dead Ringer for Love
Meat Loaf
1981
Euphoria
Loreen
2012
Love Me Now
John Legend
2016
The first number (4), indicates how many songs there are in the list. I have made a struct which will be able to hold the songs and dynamically allocate memory for each pointer.
Struct:
typedef struct Song {
char *song;
char *artist;
int *year;
} Song;
Allocated:
Song *arr;
arr = (Song*)malloc(sizeof(Song));
Function:
int loadFile(char fileName[], Song *arr, int nrOf) {
FILE *input = fopen(fileName, "r");
if (input == NULL) {
printf("Error, the file could not load!\n");
} else {
int i = 0;
fscanf(input, "%d\n", &nrOf);
for (int i = 0; i < nrOf; i++) {
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
arr[i].year = (int*)malloc(sizeof(int));
fgets(arr[i].song, 100, input);
fgets(arr[i].artist, 100, input);
fscanf(input, "%d\n", arr[i].year);
}
printf("The file is now ready.\n");
fclose(input);
}
return nrOf;
}
Are you able to find the problem? Or do you have a better solution?
This is wrong:
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
You are only allocating buffers of size 1, there's no scaling. This gives you undefined behavior when you overrun the buffers by loading more data into them than they can hold.
I would expect those to read:
arr[i].song = malloc(100);
and so on. Note that no cast is necessary, and sizeof (char) is always 1.
Also, this:
arr[i].year = (int*)malloc(sizeof(int));
is super-strange. There's absolutely no reason to dynamically allocate a single integer, just make the field an int and store the value there directly.
First Issue:
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
Are only allocating 1 byte for your char* pointers, song and artist. You can allocate a size for this:
arr[i].song = (char*)malloc(100 * sizeof(char)); /* or malloc(100) */
arr[i].artist = (char*)malloc(100 * sizeof(char));
Or you can simply malloc() enough space from you buffer:
char buffer[100];
fgets(buffer, 100, input);
/* check for failure, remove newline */
arr[i].song = malloc(strlen(buffer)+1);
/* check error from malloc */
strcpy(arr[i].song, buffer);
Or even use strdup():
arr[i].song = strdup(buffer);
Which is a substitute for malloc()/strcpy().
Note: You can also read Do I cast the result of malloc?.
Second Issue:
Your current struct:
typedef struct Song {
char *song;
char *artist;
int *year;
} Song;
Can be simplified to:
typedef struct {
char *song;
char *artist;
int year;
} Song;
Because year does not need to be a pointer. Easier to manage if its just an int. This avoids having to do allocations like:
arr[i].year = (int*)malloc(sizeof(int));
Other Recommendations:
You should check the return of fscanf() and fgets() as its safe to do this. It helps just incase your file will have incorrect data. This goes the same for malloc(), which can return NULL is unsuccessfully allocated on the heap.
Here is some code with the above considerations in mind:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100
typedef struct {
char *song;
char *artist;
int year;
} Song;
Song *create_array(FILE *input, int *nrof);
void load_data(Song *arr, FILE *input, int nrof);
void print_free_data(Song *arr, int nrof);
void get_buffer(char buffer[], FILE *input);
int main(void) {
FILE *input;
Song *arr;
int nrof;
input = fopen("artist.txt", "r");
if (input == NULL) {
fprintf(stderr, "Error reading file\n");
exit(EXIT_FAILURE);
}
arr = create_array(input, &nrof);
load_data(arr, input, nrof);
print_free_data(arr, nrof);
fclose(input);
return 0;
}
Song *create_array(FILE *input, int *nrof) {
Song *arr;
if (fscanf(input, "%d ", nrof) != 1) {
fprintf(stderr, "Cannot find number of songs\n");
exit(EXIT_FAILURE);
}
arr = malloc(*nrof * sizeof(*arr));
if (arr == NULL) {
fprintf(stderr, "Cannot allocate %d spaces for array\n", *nrof);
exit(EXIT_FAILURE);
}
return arr;
}
void load_data(Song *arr, FILE *input, int nrof) {
char buffer[SIZE];
for (int i = 0; i < nrof; i++) {
get_buffer(buffer, input);
arr[i].song = malloc(strlen(buffer)+1);
if (arr[i].song == NULL) {
fprintf(stderr, "Cannot allocate song\n");
exit(EXIT_FAILURE);
}
strcpy(arr[i].song, buffer);
get_buffer(buffer, input);
arr[i].artist = malloc(strlen(buffer)+1);
if (arr[i].artist == NULL) {
fprintf(stderr, "Cannot allocate artist\n");
exit(EXIT_FAILURE);
}
strcpy(arr[i].artist, buffer);
if (fscanf(input, "%d ", &arr[i].year) != 1) {
fprintf(stderr, "Cannot find year for Song: %s Album: %s\n",
arr[i].song, arr[i].artist);
exit(EXIT_FAILURE);
}
}
}
void get_buffer(char buffer[], FILE *input) {
size_t slen;
if (fgets(buffer, SIZE, input) == NULL) {
fprintf(stderr, "Error from fgets(), line not read\n");
exit(EXIT_FAILURE);
}
slen = strlen(buffer);
if (slen > 0 && buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
fprintf(stderr, "Too many characters entered\n");
exit(EXIT_FAILURE);
}
}
void print_free_data(Song *arr, int nrof) {
for (int i = 0; i < nrof; i++) {
printf("%s\n%s\n%d\n\n", arr[i].song, arr[i].artist, arr[i].year);
free(arr[i].song);
arr[i].song = NULL;
free(arr[i].artist);
arr[i].artist = NULL;
}
free(arr);
arr = NULL;
}
Which Outputs correct data:
Mr Tambourine Man
Bob Dylan
1965
Dead Ringer for Love
Meat Loaf
1981
Euphoria
Loreen
2012
Love Me Now
John Legend
2016
Your memory allocation is incorrect. The structure should have char arrays for the song and artist names and an int for the year, and you should modify your API to return the array and its size to the caller:
int loadFile(const char *fileName, Song **arr, int *numberp);
Here is a corrected and simplified of your program:
#include <stdio.h>
#include <stdlib.h>
typedef struct Song {
char song[100];
char artist[100];
int year;
} Song;
/* call as
if (loadFile(fileName, &songs, &songs_size) < 0) {
// deal with error...
}
*/
int loadFile(const char *fileName, Song **arrp, int *numberp) {
FILE *input;
Song *arr;
int i, nrOf;
input = fopen(fileName, "r");
if (input == NULL) {
fprintf(stderr, "Cannot open file %s\n", filename);
return -1;
} else {
if (fscanf(input, "%d\n", &nrOf) != 1) {
fprintf(stderr, "%s: missing number of items\n", filename);
fclose(intput);
return -1;
}
arr = calloc(sizeof(*arr), nrOf);
if (arr == NULL) {
fprintf(stderr, "cannot allocate memory for %d items\n", nrOf);
fclose(intput);
return -1;
}
for (int i = 0; i < nrOf; i++) {
char cc;
if (fscanf(input, "%99[^\n]%*c%99[^\n]%*c%d%c",
sarr[i].song, arr[i].artist,
&arr[i].year, &cc) != 4 || cc != '\n') {
fprintf(stderr, "%s: invalid format for item %d\n",
filename, i);
break;
}
}
printf("The file is now ready.\n");
fclose(input);
*arrp = arr;
*numberp = i;
return i;
}
}

Read from a text file and use each line to compare if they are anagrams

I must modify my program to accept input from
a file called anagrams.txt.This file should have two strings per line, separated by the # character. My program should read
each pair of strings and report back if each pair of strings is an anagram. For example consider the following content of anagrams.txt:
hello#elloh
man#nam
Astro#Oastrrasd
Your program should print out the following:
hello#elloh - Anagrams!
man#nam - Anagrams!
Astro#Oastrrasd- Not anagrams!
I should compile in g++
Here is the code to read from text:
int main()
{
char input[30];
if(access( "anagrams.txt", F_OK ) != -1) {
FILE *ptr_file;
char buf[1000];
ptr_file =fopen("anagrams.txt","r"); if (!ptr_file)
return 1;
while (fgets(buf,1000, ptr_file)!=NULL)
printf("%s",buf);
fclose(ptr_file);
printf("\n");
}
else{ //if file does not exist
printf("\nFile not found!\n");
}
return 0;
}
Code to find if the text are anagrams:
#include <stdio.h>
int find_anagram(char [], char []);
int main()
{
char array1[100], array2[100];
int flag;
printf("Enter the string\n");
gets(array1);
printf("Enter another string\n");
gets(array2);
flag = find_anagram(array1, array2);
if (flag == 1)
printf(" %s and %s are anagrams.\n", array1, array2);
else
printf("%s and %s are not anagrams.\n", array1, array2);
return 0;
}
int find_anagram(char array1[], char array2[])
{
int num1[26] = {0}, num2[26] = {0}, i = 0;
while (array1[i] != '\0')
{
num1[array1[i] - 'a']++;
i++;
}
i = 0;
while (array2[i] != '\0')
{
num2[array2[i] -'a']++;
i++;
}
for (i = 0; i < 26; i++)
{
if (num1[i] != num2[i])
return 0;
}
return 1;
}
You can try something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXLINE 1000
#define MAXLETTER 256
int is_anagram(char *word1, char *word2);
void check_lines(FILE *filename);
int cmpfunc(const void *a, const void *b);
void convert_to_lowercase(char *word);
int
main(int argc, char const *argv[]) {
FILE *filename;
if ((filename = fopen("anagram.txt", "r")) == NULL) {
fprintf(stderr, "Error opening file\n");
exit(EXIT_FAILURE);
}
check_lines(filename);
fclose(filename);
return 0;
}
void
check_lines(FILE *filename) {
char line[MAXLINE];
char *word1, *word2, *copy1, *copy2;
while (fgets(line, MAXLINE, filename) != NULL) {
word1 = strtok(line, "#");
word2 = strtok(NULL, "\n");
copy1 = strdup(word1);
copy2 = strdup(word2);
convert_to_lowercase(copy1);
convert_to_lowercase(copy2);
if (is_anagram(copy1, copy2)) {
printf("%s#%s - Anagrams!\n", word1, word2);
} else {
printf("%s#%s - Not Anagrams!\n", word1, word2);
}
}
}
void
convert_to_lowercase(char *word) {
int i;
for (i = 0; word[i] != '\0'; i++) {
word[i] = tolower(word[i]);
}
}
int
is_anagram(char *word1, char *word2) {
qsort(word1, strlen(word1), sizeof(*word1), cmpfunc);
qsort(word2, strlen(word2), sizeof(*word2), cmpfunc);
if (strcmp(word1, word2) == 0) {
return 1;
}
return 0;
}
int
cmpfunc(const void *a, const void *b) {
if ((*(char*)a) < (*(char*)b)) {
return -1;
}
if ((*(char*)a) > (*(char*)b)) {
return +1;
}
return 0;
}
Since this looks like a University question, I won't provide a full solution, only a hint.
All you have to do is replace the stdin input part of the anagram-finding file with the code you wrote to read from a file: it's as simple as changing
printf("Enter the string\n");
gets(array1);
printf("Enter another string\n");
gets(array2);
to
// before program:
#define SIZE 1000
// inside main
if (access("anagrams.txt", F_OK) == -1){
printf("\nFile not found!\n");
return 1; // Abort the program early if we can't find the file
}
FILE *ptr_file;
char buf[1000];
ptr_file = fopen("anagrams.txt","r");
if (!ptr_file)
return 1;
char array1[SIZE], array2[SIZE];
while (fgets(buf, 1000, ptr_file)!=NULL){
// do all your anagram stuff here!
// there is currently one line of the input file stored in buf
// Hint: You need to split buf into array_1 and array_2 using '#' to separate it.
}
fclose(ptr_file);
printf("\n");
Additional comments:
Don't ever ever ever use gets. gets doesn't check that the string it writes to can hold the data, which will cause your program to crash if it gets input bigger than the array size. Use fgets(buf, BUF_SIZE, stdin) instead.
Beautiful code is good code. People are more likely to help if they can read your code easily. (fix your brackets)
Just for interest, a more efficient algorithm for checking anagrams is to use qsort to sort both arrays, then a simple string matcher to compare them. This will have cost O(mnlog(m+n)), as opposed to O(m^2 n^2), awith the current algorithm
You need to split every line you read by fgets (as you did) in to two strings, and pass them to your find_anagram function. You can do that using strtok:
int main()
{
int flag;
char buf[1000];
FILE *ptr_file;
//Check file existence
//Open the file for reading
while (fgets (buf, 1000, ptr_file) != NULL)
{
char *array1 = strtok(buf, "#");
char *array2 = strtok(NULL, "\n");
flag = find_anagram (array1, array2);
//Check flag value to print your message
}
return 0;
}
//put your find_anagram function
Don't forget to #include <string.h> to use strtok().

C - Opening differents files using same pointer

I'm trying to retrieve informations by many plain-text files, which will be then stored in a proper struct. To do so, I'm using a function that takes member of the struct to populate and source of the plain-text file where the informations are stored.
Posting my "test" code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct _elem
{
const char *title;
int ok;
int almost;
int nope;
int hits;
float last_rank;
};
typedef struct _elem Chapter;
Chapter *generate_array(const char *source, int *elems);
int engine_start(Chapter *elem, char *source);
int main()
{
const char path_f[100];
int elements = 0;
int i = 0;
Chapter *dict;
printf("Insert the name of the source:\n");
scanf("%s", path_f);
printf("\nGenerating dictionary, please wait...\n");
dict = generate_array(path_f, &elements);
if (dict == NULL)
{
printf("Aborting.\n");
exit(1);
}
while (i < elements)
{
printf("Element %d:\n", (i + 1));
printf("\nTitle: %s\n", dict[i].title);
printf("Ok: %10d\n", dict[i].ok);
printf("Almost: %5d\n", dict[i].almost);
printf("Nope: %8d\n", dict[i].nope);
printf("Hits: %8d\n", dict[i].hits);
printf("Rank: %8.2f\n", dict[i].last_rank);
printf("\n");
i++;
}
return EXIT_SUCCESS;
}
Chapter *generate_array(const char *source, int *elems)
{
FILE *src;
int sources;
int i = 0;
char **srcs;
Chapter *generated;
src = fopen(source, "r");
if (src == NULL)
{
printf("[!!] Error while reading file!\n");
return NULL;
}
fscanf(src, "%d", &sources);
if (sources <= 0)
{
printf("[!!] Wrong number of sources, exiting.\n");
return NULL;
}
srcs = (char **) malloc(sizeof(char *) * sources);
while (i < sources && !feof(src))
{
srcs[i] = (char *) malloc(sizeof(char) * 100);
fscanf(src, "%s", srcs[i++]);
}
fclose(src);
generated = (Chapter *) malloc(sizeof(Chapter) * i);
*elems = i;
i = 0;
while (i < *elems)
{
if(engine_start( &generated[i], srcs[i] )) i++;
else
{
printf("[!!] Error in file %s, aborting.\n", srcs[i]);
return NULL;
}
}
return generated;
}
int engine_start(Chapter *elem, char *source)
{
FILE *parser;
int done = 0;
parser = fopen(source, "r");
if (parser == NULL) printf("[!!] Error while opening %s, aborting.\n", source);
else
{
fgets(elem->title, 100, parser);
fscanf(parser, "%d %d %d %d %f", &(elem->ok), &(elem->almost),
&(elem->nope), &(elem->hits),
&(elem->last_rank) );
fclose(parser);
done = 1;
}
return done;
}
Now this is the main file where are stored paths to the other plain-text files:
lol.dat
5
lold/lol1.dat
lold/lol2.dat
lold/lol3.dat
lold/lol4.dat
lold/lol5.dat
And one example of lolX.dat:
Qual'รจ la vittoria di cristo?
3 4 5 12 44.9
I'm getting SIGSEGV after the first iteration of "engine_start", probably due to FILE *parser (but I can be totally wrong, I don't know at this point).
Someone can guide me through this problem? Thank you.
Make the following changes and try-
struct _elem
{
char *title; // allocate the memory for this.
int ok;
int almost;
int nope;
int hits;
float last_rank;
};
You need to allocate memory for element title before assigning something to it.
int engine_start(Chapter *elem, char *source)
{
FILE *parser;
int done = 0;
parser = fopen(source, "r");
if (parser == NULL) printf("[!!] Error while opening %s, aborting.\n", source);
else
{
elem->title=(char *)malloc(100); // include this line.
fgets(elem->title, 100, parser);
fscanf(parser, "%d %d %d %d %f", &(elem->ok), &(elem->almost),
&(elem->nope), &(elem->hits),
&(elem->last_rank) );
fclose(parser);
done = 1;
}
return done;
}

Resources