I'm trying to read from a text file to a struct that has a pointer to another struct.
The text file has the following format:
279288151 1 John Doe
002 1 30 04 2018
23189842 0 Jane Doe
0
282676381 1 Mark Examp
001 0 28 03 2018 03 04 2018
243897574 1 Joe Soap
003 2 14 04 2018 21 04 2018
This is my .h file:
#ifndef Clientes_h
#define Clientes_h
#include <stdio.h>
#include <stdlib.h>
#include "Alugueres.h"
#define ST_TAM 50
typedef struct info_cliente Cliente;
struct info_cliente{
char nome[ST_TAM];
long nif;
int n_alugueres;
int n_hist;
pAluga aluguer;
};
typedef struct aluga Aluguer, *pAluga;
typedef struct data DataIn, *pDataIn;
typedef struct data DaraEn, *pDataEn;
struct aluga{
int id_unico;
int estado;
pDataIn dataIn;
pDataEn dataEn;
pAluga prox;
};
struct data{
int dia;
int mes;
int ano;
};
Cliente* le_fich(char *nome, int *n);
#endif /* Clientes_h */
And my read_file func is as follows:
#include "Clientes.h"
Cliente* le_fich(char *nome, int *n){
FILE *f = fopen(nome, "r");
Cliente *aux;
int conta = 0;
if(!f){
printf("Error\n");
return NULL;
}
while(getc(f) != EOF){
aux = (Cliente*)malloc(sizeof(Cliente));
fscanf(f, "%ld %d %49[^\n]", &aux[conta].nif, &aux[conta].n_alugueres, aux[conta].nome);
if(aux[conta].n_alugueres != 0){
fscanf(f, "%d %d %d %d %d", &aux[conta].aluguer->id_unico,
&aux[conta].aluguer->estado, &aux[conta].aluguer->dataIn->dia,
&aux[conta].aluguer->dataIn->mes, &aux[conta].aluguer->dataIn->ano);
}
conta++;
}
return aux;
}
It gives me a bad_access error when trying to run the fscanf after the if is successful (when accessing the pointer of the struct for my date). If anyone could help me out, would really appreciate it.
Right now you allocating memory for aux in the loop and then try to access an element using an index which will not work. Instead you need to allocate memory for all Cliente records. If you know the number of records in the file you could simply do aux = (Cliente*)malloc(size * sizeof(Cliente));. You might also check on how you can use realloc() in the actual loop.
It gives me a bad_access error when trying to run the fscanf after the if is successful
2 problems:
As #Rishikesh Raje, #Cyclonecode pointed out, the allocation is only good for aux = aux[0] as well as other missing allocations.
The usual suspect to scanning problems are that the fscanf() did not scan what was expected and code lacked checks of the return value. (Hint: when line is only "0\n", the 2nd fscanf() reads more than OP expected.)
ret = fscanf(f, "%ld %d %49[^\n]", ...);
if((ret ==3) && (aux[conta].n_alugueres != 0)){
fscanf(f, "%d %d %d %d %d", ...
// code errantly does not check the return value of `fscanf()`.
}
else {
break; // This code missing, what to do when `ret != 3`
}
A simply solution to both problems to (re)allocate as needed and check success of reading 2 lines and scanning them.
I recommend to not allocate data for the new pair of lines until the input have been validated.
Cliente *aux = NULL; // Initialize to NULL
size_t n = 0; // Keep count of record count
char buf1[150]; // Use generous buffers. Suggest 2x maximum expected need
char buf2[100];
// while 2 lines successfully read
while (fgets(buf1, sizeof buf1, f) && fgets(buf2, sizeof buf2, f)) {
// Form objects to be scanned into with default values.
struct info_cliente cli = { 0 };
struct aluga alu = { 0 };
struct data dat = { 0 };
if (sscanf(buf1, "%ld %d %49[^\n]", &cli.nif, &cli.n_alugueres, cli.nome) != 3) {
perror("Unexpected 1st line");
break;
}
if (cli.n_alugueres == 0) {
if (sscanf(buf2, "%d", &alu.id_unico) != 1 || alu.id_unico != 0)) {
perror("Unexpected 2nd line 0");
break;
}
}
else if (cli.n_alugueres == 1) {
if (sscanf(buf2, "%d %d %d %d %d", &alu.id_unico, &alu.estado, &dat.dia,
&dat.mes, &dat.ano) != 5) {
perror("Unexpected 2nd line");
break;
}
alu.dataIn = malloc(sizeof *alu.dataIn);
*alu.dataIn = dat;
cli.aluguer = malloc(sizeof *cli.aluguer);
*cli.aluguer = alu;
} else {
perror("Unexpected 2nd line n_alugueres");
break;
}
Cliente *tmp = realloc(aux, sizeof *aux * (n+1));
aux = tmp;
aux[n] = cli;
n++;
}
Cliente *tmp = realloc(aux, sizeof *aux * (n+1));
aux = tmp;
aux[n] = NULL; // NULL terminate the list
Note Error checking on malloc()/realloc() omitted for brevity on the above sample code.
You are using both getc and fscanf to access the file.
If you want to use fscanf, you should not use getc.
Use the return value of fscanf to let you know when the file has ended. fscanf will return the number of items matched. In your case if successful it should return 3.
int ret;
do{
aux = (Cliente*)malloc(sizeof(Cliente));
ret = fscanf(f, "%ld %d %49[^\n]", &aux[conta].nif, &aux[conta].n_alugueres, aux[conta].nome);
if((ret ==3) && (aux[conta].n_alugueres != 0)){
fscanf(f, "%d %d %d %d %d", &aux[conta].aluguer->id_unico,
&aux[conta].aluguer->estado, &aux[conta].aluguer->dataIn->dia,
&aux[conta].aluguer->dataIn->mes, &aux[conta].aluguer->dataIn->ano);
}
conta++;
}while (ret == 3);
Related
I'm having problems while reading a file -again.
Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
struct camion {
int data[3];
int numeropacchi;
char *hubprovenienza[100];
_Bool iseccezionale;
};
int main(void) {
struct camion **listacamion = malloc(sizeof(struct camion * ));
listacamion[0] = calloc(1, sizeof(struct camion));
int numerocamion = 0;
//importazione
FILE *input = fopen("info.txt", "r");
if (input != NULL) {
char *temp = calloc(100, sizeof(char));
for (int i = 0; true; i++) {
//1, 2, 3
fscanf(input, "%d - %d - %d",
&listacamion[i]->data[0],
&listacamion[i]->data[1],
&listacamion[i]->data[2]);
printf("ORA %d-%d-%d\n",
listacamion[i]->data[0],
listacamion[i]->data[1],
listacamion[i]->data[2]);
fscanf(input, "%d", &listacamion[i]->numeropacchi);
printf("NUMERO PACCHI %d\n", listacamion[i]->numeropacchi);
if (fscanf(input, "%s", listacamion[i]->hubprovenienza) == EOF) {
listacamion[i]->iseccezionale = false;
break;
}
printf("HUB PROVENIENZA %s\n", listacamion[i]->hubprovenienza);
//4
char eccezionale;
long i_senoneccezionale = ftell(input);
if (fscanf(input, "%s", temp) == EOF)
break;
else { //if not EOF alloc mem for the next truck
listacamion = realloc(listacamion, sizeof(struct camion *) * (i + 1));
listacamion[i + 1] = calloc(1, sizeof(struct camion))
numerocamion++;
printf("RIALLOCATA!\n");
}
if (temp[0] == 'E') //if 'E' is there, bool is true
listacamion[i]->iseccezionale = true;
else {
//otherwise go back to the saved pointer
listacamion[i]->iseccezionale = false;
fseek(input, i_senoneccezionale, SEEK_SET);
}
//set temp to zero;
free(temp);
temp = calloc(100, sizeof(char));
//---
printf("YES OR NO %d\n\n", listacamion[i]->iseccezionale);
}
}
//---
return 0;
}
Here's the input file I use:
22 - 03 - 2020
39
Bergamo
E
30 - 05 - 2021
90
Napoli
19 - 02 - 2022
132
Bari
21 - 03 - 2022
1721
Modena
E
I've got 3 or 4 lines per camion (truck) in my file, depending on whether the load is oversized or not (if it is, then an 'E' is put, otherwise the line in the file doesn't exist). So the end of the file could be detected either at the third fscanf or the fourth, but I'm not sure fscanf works this way... The issue is that the loop is correctly executed 3 times out of 4 (I've got the info of 4 trucks), then I get a segmentation fault, so I assumed that the problem might be an inadequate detection of the end of the file. I've tried other options - feof for example - but they weren't useful, and I can only find examples of a fscanf used as a condition, while my condition is just a true because I have different fscanfs...
Here's my output:
ORA 22-3-2020
NUMERO PACCHI 39
HUB PROVENIENZA Bergamo
RIALLOCATA!
YES OR NO 1
ORA 30-5-2021
NUMERO PACCHI 90
HUB PROVENIENZA Napoli
RIALLOCATA!
YES OR NO 0
ORA 19-2-2022
NUMERO PACCHI 132
HUB PROVENIENZA Bari
RIALLOCATA!
--------------------------------
Process exited after 0.4117 seconds with return value 3221226356
You should test the return value of every call to fscanf(): fscanf returns the number of successful conversions. A return value different from the number of conversions requested is an indication of invalid or missing input. Both must be handled to avoid undefined behavior.
fscanf(input, "%s", temp) is risky: a word longer than 99 bytes will cause a buffer overflow. You should write fscanf(input, "%99s", temp)
Here is a modified version using an array of structures instead of an array of pointers:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
struct camion {
int data[3];
int numeropacchi;
char *hubprovenienza[100];
_Bool iseccezionale;
};
int main() {
struct camion *listacamion = NULL;
struct camion cam;
char ebuf[2];
int numerocamion = 0;
//importazione
FILE *input = fopen("info.txt", "r");
if (input != NULL) {
while (fscanf(input, "%d - %d - %d\n%d\n%99s",
&cam.data[0], &cam.data[1], &cam.data[2],
&cam.numeropacchi,
cam.hubprovenienza) == 5) {
cam.iseccezionale = false;
if (fscanf(input, " %1[E]", ebuf) == 1)
cam.iseccezionale = true;
printf("ORA %d-%d-%d\n"
"NUMERO PACCHI %d\n"
"HUB PROVENIENZA %s\n"
"YES OR NO %d\n\n",
cam.data[0], cam.data[1], cam.data[2]);
cam.numeropacchi,
cam.hubprovenienza,
cam.iseccezionale);
// if new truck, append to array
listacamion = realloc(listacamion, sizeof(*listacamion) * (numerocamion + 1));
listacamion[numerocamion] = cam;
numerocamion++;
printf("RIALLOCATA!\n");
}
}
return 0;
}
Check the return value of every call to scanf. You can refactor your code to make it simpler. Try something like:
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
struct camion {
int data[3];
int numeropacchi;
char hubprovenienza[100];
_Bool iseccezionale;
};
int
get_record(FILE *fp, struct camion *c)
{
/* Read up to one record from the file. Return 1 on success. */
if( 3 != fscanf(fp, "%d -%d -%d", c->data, c->data + 1, c->data + 2) ){
return 0;
}
if( 1 != fscanf(fp, "%d", &c->numeropacchi) ){
return 0;
}
if( 1 != fscanf(fp, "%99s", c->hubprovenienza) ){
return 0;
}
int k;
while( ( k = fgetc(fp)) != EOF && isspace(k) ){
;
}
if( k == 'E' ){
c->iseccezionale = 1;
} else {
c->iseccezionale = 0;
ungetc(k, fp);
}
return 1;
}
int
main(int argc, char **argv)
{
struct camion c;
while( get_record(stdin, &c) ){
printf(
"%s is %soversized\n",
c.hubprovenienza,
c.iseccezionale ? "" : "not "
);
}
return 0;
}
Okay, this morning I've tried again and now it's working. Thanks to your comments I've paid more attention to the fscanf return value; I've also realized that I could get an EOF from either the 1st line per truck or the 4th one, not 3rd and 4th.
I didn't know whether to delete this question or what... so I've decided to post the code that worked for me just in case it might turn useful to some C beginners like me in the future because it contains a couple of things I wish I knew before. I'm also quite sure there's a faster way to do so, but who knows...
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
struct camion {
int data[3];
int numeropacchi;
char* hubprovenienza[100];
_Bool iseccezionale;
};
int main(void) {
struct camion** listacamion=malloc(sizeof(struct camion*));
listacamion[0]=calloc(1, sizeof(struct camion));
int numerocamion=0;
//importazione
FILE* input=fopen("info.txt", "r");
if(input!=NULL) {
char* temp=calloc(100, sizeof(char));
_Bool primavolta=true;
for(int i=0; true; i++) {
int fs1, fs4;
int temp1, temp2, temp3;
//1
if((fs1=fscanf(input, "%d - %d - %d", &temp1, &temp2, &temp3) == EOF))
break;
else {
numerocamion++;
if(primavolta) {
primavolta=false;
listacamion[i]->data[0]=temp1;
listacamion[i]->data[1]=temp2;
listacamion[i]->data[2]=temp3;
}
else {
//EVENTUALE RIALLOCAZIONE
listacamion=realloc(listacamion, sizeof(struct camion*)*(i+1));
listacamion[i]=calloc(1, sizeof(struct camion));
//---
listacamion[i]->data[0]=temp1;
listacamion[i]->data[1]=temp2;
listacamion[i]->data[2]=temp3;
}
}
//2
fscanf(input, "%d", &listacamion[i]->numeropacchi);
//3
fscanf(input, "%s", listacamion[i]->hubprovenienza);
//4
char eccezionale;
long i_senoneccezionale=ftell(input);
if((fs4=fscanf(input, "%s", temp)) == EOF) {//se è la fine del file...
listacamion[i]->iseccezionale=false;
free(temp);
break;
}
else {
if(temp[0]=='E') //se esiste il carattere 'E' - e quindi la quarta riga ESISTE - assegna il valore positivo alla booleana
listacamion[i]->iseccezionale=true;
else { /*se invece NON esiste il carattere 'E', ripristina il puntatore al file alla stessa riga che avrebbe dovuto contenere la 'E',
così che questo non vada perso per la prossima chiamata a scanf per la prima riga di ciascun camion, quella contenente la data*/
listacamion[i]->iseccezionale=false;
fseek(input, i_senoneccezionale, SEEK_SET);
}
}
//ripristina memoria per temp;
free(temp);
temp=calloc(100, sizeof(char));
//---
}
}
//---
//output
for(int i=0; i<numerocamion; i++) {
printf("%d-%d-%d %d %s %d\n", listacamion[i]->data[0], listacamion[i]->data[1], listacamion[i]->data[2], listacamion[i]->numeropacchi, listacamion[i]->hubprovenienza, listacamion[i]->iseccezionale);
}
return 0;
}
I am having trouble with an assignment, it involves reading from files into structures and I'm confused on how to do it here is the function with the parameters I HAVE to use.
// This function will read in size struct players from filename and add these
// the players array. The function will use index to know where to start
// writing the players to in the array.
// Parameters
//
// filename – The name of the input file
// players – a pointer to the array of player structures
// index – The index of the array to start placing players into
// size – The number of players in the input file
// Return - Nothing
void read_from_file(char* filename, Player* players, int index, int size);
This is the function I have to use to read in data from 3 DIFFERENT files that look as such:
Andrew Jackson 129 33 38 30 506
Jeremy Warden 25 24 3 9 493
Jared Welch 130 1 43 27 422
Brandon Splitter 138 38 40 7 587
Joe Gwilliams 150 23 30 25 498
Ali Mohammed 119 43 13 6 598
Dheeraj Johnson 124 79 59 36 506
Bill Clinton 121 65 12 26 449
Jesse James 87 58 8 5 464
John Doe 129 100 0 12 548
I have to read in 3 files that all have 10 players in them for a total of 30 I need to read into the structures. I have not gotten very far I know but I am very confused on what to do and how to approach this, any help would be very much appreciated! Below I have down what I have already done. Please help!! Thanks
//Brady Webb
//lab D
//HW1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct player
{
char Fname[25];
char Lname[25];
int Singles;
int Doubles;
int Triples;
int Homeruns;
int At_Bats;
float Slugging_Percentage;
} Player;
void read_from_file(char* filename, Player* players, int index, int size);
int main(int argc, char* argv[])
{
int size= atoi(*(argv+1));
char* file1 = *(argv+2);
char* file2 = *(argv+3);
char* file3 = *(argv+4);
if (argc<6 || argc>6)
{
printf("Incorrect command line arguments\n");
return 0;
}
return 0;
}
void read_from_file(char*filename, Player* players, int index, int size)
{
FILE *ptr;
int i=0;
if ((ptr=fopen(filename, "r")) == NULL)
{
return 0;
}
while (ptr != EOF)
{
}
}
The simplest approach to read file with regular structure is to use fscanf with complex format string.
fscanf("%s %s %d %d %d %d %d", Player.Fname, Player.Lname,
&Player.Singles, &Player.Doubles,
&Player.Triples, &Player.Homeruns, &Player.At_Bats);
You should make a loop to read till the and of file, and you can add the check of reading the data in the correct format, e.g.:
int check = fscanf("%s %s %d %d %d %d %d", Player.Fname, Player.Lname,
&Player.Singles, &Player.Doubles,
&Player.Triples, &Player.Homeruns, &Player.At_Bats);
if( check != 7 )
{
// stop reading and report on wrong file format
}
UPDATE:
I propose the following code as possible solution:
// This function will read in size struct players from filename and add these
// the players array. The function will use index to know where to start
// writing the players to in the array.
// Parameters
//
// filename – The name of the input file
// players – a pointer to the array of player structures
// index – The index of the array to start placing players into
// size – The number of players in the input file
// Return - number of read players (positive number)
// or error code (negarive number)
int read_from_file(char * filename, Player* players, int index, int size)
{
struct player ptmp;
FILE *fptr;
// open the file
if ((fptr = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "File %s cannot be oppened\n",filename);
return -1; // error code for "File cannot be oppened"
}
// reading from file
int position = index;
int cnt = 0;
while (!ferror(fptr) && cnt < size)
{
int check = fscanf(fptr, "%24s %24s %d %d %d %d %d", ptmp.Fname, ptmp.Lname,
&ptmp.Singles, &ptmp.Doubles, &ptmp.Triples, &ptmp.Homeruns, &ptmp.At_Bats);
if (feof(fptr) && check != 7)
{
break;
}
if (check != 7)
{
fclose(fptr);
fprintf(stderr,"Wrong data format in line %d of file %s\n", cnt+1, filename);
return -2; // error code for "File has wrong data format"
}
// copy data to players
players[index++] = ptmp;
cnt++;
}
// close the file
fclose(fptr);
return cnt;
}
Pay attention at changed type of function read_from_file - I described my idea concerning return value in the comments.
And main in my understanding should be like:
int main(int argc, char* argv[])
{
Player players[30]; // memory is allocated for particular number of data items
// check the command line arguments
if (argc < 3)
{
printf("Please run the program in the format:\n");
printf(" %s 2 firstFile.txt secondFile.txt\n", argv[0]);
printf(" where 2 is number of files given after 2 with data to be read\n");
return 0;
}
int fileNumber = 0;
if (!sscanf(argv[1], "%d", &fileNumber) || fileNumber <= 0)
{
printf("The first command line argument nust be positive number.\n");
printf("Run program without parameters to see details\n");
return 0;
}
if (fileNumber != (argc - 2))
{
printf("Command line arguments are inconsistent\n");
printf("Run program without parameters to see details\n");
return 0;
}
// file processing
int i = 0;
int total = 0;
int max = 30;
for (i = 0; i < fileNumber; i++)
{
printf("Reading from %s...\n", argv[i + 2]);
int res = read_from_file(argv[i + 2], players, total, max);
if (res > 0)
{
total += res;
max -= res;
}
}
// check data
for (i = 0; i < total; i++)
{
printf("%s %s : %d %d %d %d %d\n", players[i].Fname, players[i].Lname, players[i].Singles, players[i].Doubles, players[i].Triples, players[i].Homeruns, players[i].At_Bats);
}
return 0;
}
It is assumed that 30 players can be read any number of files, and not necessarily to 10 of each file.
The loop can be return to read one line at a time and each line can be passed to a helper function like getPlayer
char *line;
char buffer[256]; // assuming that's the longest line size
while ((line = fgets(buffer, sizeof(buffer), ptr)) != NULL) {
Player *pl = getPlayer(line);
//...
}
The above code should replace loop in the code posted as while (ptr != EOF). The getPlayer can be something along the following lines (the player can then be added to list/array etc. and make sure you free up accordingly when you are done)
Player *getPlayer(char *line) {
Player *iPlayer = (Player *)malloc(sizeof(Player));
sscanf(line, "%s %s %d %d %d %d %d %f",
iPlayer->Fname,
iPlayer->Lname,
&iPlayer->Singles,
&iPlayer->Doubles,
&iPlayer->Triples,
&iPlayer->Homeruns,
&iPlayer->At_Bats,
&iPlayer->Slugging_Percentage);
return iPlayer;
}
struct Player PlayerArr[10];
void read_from_file(char*filename, Player* players, int index, int size)
{
FILE *ptr;
int i=0;
char content[50];
memset(content, '\0', 50);
if ((ptr=fopen(filename, "r")) == NULL)
{
return 0;
}
while (ptr != EOF)
{
getline(infile,line);
memset(content,'\0',sizeof(content));
strcpy(content,line.c_str());
ReadNextConfRecord(content);
}
return 0;
}
int ReadNextConfRecord(char *content)
{
char str[50];
const char delimiter[2] = " ";
char *token;
int count =0;
int i =0;
memset(str, '\0', 50);
strcpy(str, (char*)content);
token = strtok(str, delimiter);
while( token != NULL )
{
count++;
if(count == 1)
{
strcpy(PlayerArr[i].Fname, token);
}
else if(count == 2)
{
strcpy(PlayerArr[i].Lname, token);
}
else if(count == 3)
{
PlayerArr[i].Singles= atoi(token);
}
else if(count == 4)
{
PlayerArr[i].Doubles= atoi(token);
}
else if(count == 5)
{
PlayerArr[i].Triples= atoi(token);
}
else if(count == 6)
{
PlayerArr[i].Homeruns= atoi(token);
}
else if(count == 7)
{
PlayerArr[i].At_Bats= atoi(token);
}
else if(count == 8)
{
PlayerArr[i].Slugging_Percentage= atof(token);
}
i++;
token = strtok(NULL, delimiter);
}
return 0;
}
Using tokenizer can also solve above problem
I didn't have any error when running the codes below. But on the command console it seems to be stuck on Processing filename xxxxx as below.
May I ask how do I use if condition to filter out number of data characters of less than 50 in line 67, I try to use strlen(array[count].data) >= 1 && strlen(array[count].data) <= 50 but doesn't seem to work ?
Enter filename: testdata.txt
Processing filename testdata.txt ...
Sample Source txt file:
9001:0002:9003:0021:CLS
0001:0010:0003:0021:CLS
8001:0002:8002:0080:<HTML>
0001:4002:0002:0080:<BODY>
0004:0002:0002:0080:JHJKJBKHJBIUHBKBKHBKHHBKJBKJNKJKHHKUHKJLHBKHBKHBHBHBKHBHBHBHBBHHBHBJKJHKJHKJHKUHIUJ
Source code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
//struct definition
struct record{
int src;
int dest;
int type;
int port;
char data[100];
};
int main()
{
struct record *array;
FILE* inFile; //file handle or pointer
FILE* outFile;
FILE* errorFile;
char filename[100];
int count = 0; //keep track of number of records in memory
int i = 0;
int test;
int n1 = 0; //keep track number of correct records
int n2 = 0; //keep track number of error records
array = (struct record *) malloc(sizeof(struct record));
//User to enter filename
printf("Enter filename: ");
scanf("%s", filename);
printf("Processing filename %s ...\n", filename);
inFile = fopen(filename, "r");
if (inFile == NULL) //check if file handler is invalid
{
printf("Could not open file %s\n", filename);
exit(1); //error status code
}
test = fscanf(inFile, "%d:%d:%d:%d",
&array[count].src, &array[count].dest, &array[count].type, &array[count].port);
fgets(array[count].data, 100, inFile);
while (test != EOF){
count++;
array = (struct record *) realloc(array, (count + 1)*sizeof(struct record));
test = fscanf(inFile, "%d:%d:%d:%d:%s",
&array[count].src, &array[count].dest, &array[count].type, &array[count].port, &array[count].data);
}
fclose(inFile); //must always close file once done
outFile = fopen("data_1.txt", "wt");
errorFile = fopen("data_error.txt", "wt");
if (outFile == NULL) //check if file handler is invalid
{
printf("Could not write to file \n", filename);
exit(1);
}
if (count > 0){
printf("Viewing all records: ", count);
for (i = 0; i < count; i++){
if (array[count].src >= 1 && array[count].src <= 1024 && array[count].dest >= 1 && array[count].dest <= 1024 && array[count].type >= 1 && array[count].type <= 10 && array[count].port >= 1 && array[count].port <= 1024)
n1++;
fprintf(outFile, "%d %d %d %d",
(i + 1),
array[count].src,
array[count].dest,
array[count].type,
array[count].port
);
}
}
else
{
n2++;
fprintf(errorFile, "%d %d %d %d",
(i + 1),
array[count].src,
array[count].dest,
array[count].type,
array[count].port
);
}
fclose(errorFile);
fclose(outFile);
return 0;
}
There are many common errors in this. Which book are you reading?
array = (struct record *) malloc(sizeof(struct record));
test = fscanf(inFile, "%d:%d:%d:%d",
&array[count].src, &array[count].dest, &array[count].type, &array[count].port);
fgets(array[count].data, 100, inFile);
while (test != EOF){
count++;
array = (struct record *) realloc(array, (count + 1)*sizeof(struct record));
test = fscanf(inFile, "%d:%d:%d:%d:%s", &array[count].src, &array[count].dest
, &array[count].type, &array[count].port
, &array[count].data);
}
Forgive me. I took the liberty to clean up a bit of your code and condense some of the logic.
You shouldn't cast the return value of malloc or realloc.
While I'm on the topic of types, isn't your compiler warning you about the type of &array[count].data being incompatible with the type that corresponds to %s? Don't cast it. Just remove the & and recognise how the expression array[count].data that denotes an array is converted to a pointer of the appropriate type.
x = realloc(x, ...); where x is whatever expression is (almost) always incorrect. You need to use a temporary variable so you can handle errors correctly without leaking memory (example below)... While I'm on the topic of realloc usage, the first argument can be NULL and realloc will act like malloc in that case, so technically that first malloc is unnecessary bloat...
struct record *array = NULL;
void *temp = realloc(array, (count + 1) * sizeof *array);
if (!temp) {
puts("ERROR ALLOCATING MEMORY!");
exit(EXIT_FAILURE);
}
array = temp;
Have you considered the difference between fgets and fscanf("%s", ...)? Are you aware that the former is for reading lines, while the latter is for reading words? This would explain your problem; fscanf is halting when it sees a space, leaving everything after the space to be processed by the next fscanf call. The result? An infinite loop of fscanf fails.
Have you considered that not all fscanf fails return EOF? If you ask fscanf to read an int (using %d) and it immediately encounters non-decimal digits it can't proceed any further; a conversion failure occurs.
If you want to ensure that fscanf reads all 5 fields successfully, you need to compare the return value to 5, not to EOF.
I think you most likely meant to read the remainder of the line following your four colon-separated ints, and here's how I'd naively do that:
array = NULL;
struct record record;
while (fscanf(inFile, "%d:%d:%d:%d:%99[^\n]", &record.src, &record.dest
, &record.type, &record.port
, record.data) == 5) {
/* It's possible that a user might enter more than *
* 99 chars for the last field, so when that happens *
* we should discard the remainder... */
if (getchar() != '\n') {
puts("WARNING: Maximum field length for data is 99; user input truncated");
fscanf(inFile, "%*[^\n]");
}
void *temp = realloc(array, (count + 1) * sizeof *array);
if (!temp) {
puts("ERROR ALLOCATING MEMORY!");
exit(EXIT_FAILURE);
}
array = temp;
array[count++] = record;
}
I am trying to write a C program to parse an input file so that individual lines are parsed, and in each line, the individual characters are then parsed further and stored in different variables in a struct. Here is my code so far (I have managed to parse individual characters without considering which line they are on):
/* create struct instances */
/* file open code */
...
int currentChar = fscanf(fp, "%s", storageArray);
while (currentChar != EOF) {
printf("%s\n", storageArray);
currentChar = fscanf(fp, "%s", storageArray);
}
...
/* file close code */
How can I adapt my code so that, instead of having each individual character print to the screen, I get behaviour like the following: (Note: in my program I assume the user input will have three characters to a line.)
INPUT FILE:
a b c
f e d
LINE STRUCT 1:
char1 = a
char2 = b
char3 = c
LINE STRUCT 2:
char1 = f
char2 = e
char3 = d
I feel like the solution might involve nested loops similar to the while I have written, where the outer one keeps track of lines and the inner one keeps track of characters.
Or try this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define READ_OPTIONS "r"
struct line {
char char1;
char char2;
char char3;
struct line* next;
};
struct line* g_lines = NULL;
int main(int argc, char** argv) {
char buf[8] = {0};
struct line* newline, *iter, *head;
int counter = 1;
FILE* fp = fopen("file.txt", READ_OPTIONS);
if(NULL != fp) {
while(fgets(buf, 8, fp)) {
newline = malloc(sizeof(struct line));
if(NULL != newline) {
memset(newline, 0, sizeof(struct line));
sscanf(buf, "%c %c %c",
&newline->char1,
&newline->char2,
&newline->char3);
if(NULL != g_lines) {
for(iter = g_lines;
NULL != iter->next;
iter = iter->next);
iter->next = newline;
} else g_lines = newline;
}
}
fclose(fp);
} else return -1;
/* print the contents */
for(iter = g_lines;
NULL != iter;
iter = iter->next,
++counter)
printf("Line %d: char1=%c char2=%c char3=%c\n",
counter, iter->char1, iter->char2,
iter->char3);
/*
now to free memory before returning
control to the operating system
*/
for(iter = g_lines;
NULL != iter;)
{
head = iter->next;
free(iter);
iter = head;
}
return 0;
}
Try This
int i=1,j-1;
printf("Line STRUCT 1: ");
while( ( ch = fgetc(fp) ) != EOF ){
if(ch=='\n'){
printf("Line STRUCT %d \n",++i);
j=1;
}
printf("Char %d = %c \n",ch,j++);
}
Do this:
for (int i=0; !feof(fp); i++)
fscanf(fp,"%c %c %c ",
&storageArray[i].char1,
&storageArray[i].char2,
&storageArray[i].char3);
I have a text file with data in the form:
Lee AUS 2 103 2 62 TRUE
Check AUS 4 48 0 23 FALSE
Mills AUS 8 236 0 69 FALSE
I need to each line into a struct like, however I'd like to avoid using fixed length arrays (the problem with fgets as far as I can tell):
struct Data
{
char *sname;
char *country;
int *a;
int *b;
int *c;
int *d;
char *hsisno;
};
I am very new to C. Should I use fscanf, or fgets?
fscanf stands for "file scan formatted" and user data is about as unformatted as you can get.
You should never use naked "%s" format strings on data where you don't have absolute control over what can be read.
The best solution is to use fgets to read a line since this allows you to prevent buffer overflow.
Then, once you know the size of your line, that's the maximum size of each string that you will require. Use sscanf to your heart's content to get the actual fields.
One final thing. It's probably a bit wasteful having int* types for the integers, since you know they have a specific maximum size already. I'd use the non-pointer variant, something like:
struct Data {
char *sname; char *country;
int a; int b; int c; int d;
char *hsisno;
};
By way of example, here's some safe code:
#include <stdio.h>
#include <string.h>
// Here's all the stuff for a linked list of your nodes.
typedef struct sData {
char *sname; char *country; char *hsisno;
int a; int b; int c; int d;
struct sData *next;
} Data;
Data *first = NULL; Data *last = NULL;
#define MAXSZ 100
int main (void) {
char line[MAXSZ], sname[MAXSZ], country[MAXSZ], hsisno[MAXSZ];
int a, b, c, d;
FILE *fIn;
Data *node;
// Open the input file.
fIn = fopen ("file.in", "r");
if (fIn == NULL) {
printf ("Cannot open file\n");
return 1;
}
// Process every line.
while (fgets (line, sizeof(line), fIn) != NULL) {
// Check line for various problems (too short, too long).
if (line[0] == '\0') {
printf ("Line too short\n");
return 1;
}
if (line[strlen (line)-1] != '\n') {
printf ("Line starting with '%s' is too long\n", line);
return 1;
}
line[strlen (line)-1] = '\0';
// Scan the individual fields.
if (sscanf (line, "%s %s %d %d %d %d %s",
sname, country, &a, &b, &c, &d, hsisno) != 7)
{
printf ("Line '%s' didn't scan properly\n", line);
return 1;
}
// Allocate a new node to hold data.
node = malloc (sizeof (Data));
if (node == NULL) {
printf ("Ran out of memory\n");
return 1;
}
node->sname = strdup (sname);
node->country = strdup (country);
node->a = a;
node->b = b;
node->c = c;
node->d = d;
node->hsisno = strdup (hsisno);
node->next = NULL;
if (first != NULL) {
last->next = node;
last = node;
} else {
first = node;
last = node;
}
}
fclose (fIn);
// Output the list for debugging.
node = first;
while (node != NULL) {
printf ("'%s' '%s' %d %d %d %d '%s'\n",
node->sname, node->country, node->a, node->b,
node->c, node->d, node->hsisno);
node = node->next;
}
return 0;
}
which reads in your file and stores it in a linked list. It outputs:
'Lee' 'AUS' 2 103 2 62 'TRUE'
'Check' 'AUS' 4 48 0 23 'FALSE'
'Mills' 'AUS' 8 236 0 69 'FALSE'
at the end, as expected.
I've done a whole series of answers on the pitfalls of using *scanf functions on non-controlled data (enter user:14860 fgets into the search box above), some of which (here, here and here, for example) include a perennial favourite function of mine, getLine, for safer user input.