Reading file containing multiple data formats - c

I'm supposed to read a file in C with a structure that looks like this
A:
1
2
3
4
B:
1 1
2 2
3 3
4 4
C:
1 1 1
2 2 2
3 3 3
4 4 4
The file is always separated into three parts and each part starts with an identifier (A:, B:,..).
Identifier is followed by unspecified number of rows containing data. But in each part the format of the data is different. Also it's not just integers but that's not important in this question.
I don't have a problem reading the file. My question is what would be an optimal way to read such a file? It can contain thousands of rows or even more parts than just three. The result should be for example string arrays each containing rows from a different part of the file.
I didn't post any code because I don't need/want you to post any code either. Idea is good enough for me.

You could read the file line by line and check every time if a new section starts. If this is the case, you allocate new memory for a new section and read all the following lines to the data structure for that new section.
For dynamic memory allocation, you will need some counters so you know how many lines per section and how many sections in total you have read.
To illustrate the idea (no complete code):
typedef struct {
int count;
char **lines;
} tSection;
int section_counter = 0;
tSection *sections = NULL;
tSection *current_section = NULL;
char line[MAXLINE];
while (fgets(line, MAXLINE, file)) {
if (isalpha(line[0])) { // if line is identifier, start new section
sections = realloc(sections, sizeof(tSection)*(section_counter+1));
current_section = &sections[section_counter];
current_section->lines = NULL;
current_section->count = 0;
section_counter++;
}
else { // if line contains data, add new line to structure of current section
current_section->lines = realloc(current_section->lines, sizeof(char*)*(current_section->count+1));
current_section->lines[current_section->count] = malloc(sizeof(char)*MAXLINE);
strcpy(current_section->lines[current_section->count], line);
current_section->count++;
}
}

If each section in the file has a fixed format and the section header has a fixed format, you can use fscanf and a state machine based approach. For example in the code below, the function readsec reads a section based on the parameters passed to it. The arguments to readsec depends on which state it is in.
void readsec(FILE* f, const char* fmt, int c, char sec) {
printf("\nReading section %c\n",sec);
int data[3];
int ret=0;
while ((ret=fscanf(f, fmt, &data[0], &data[1], &data[2]))!=EOF){
if (ret!=c) {
return;
}
// processData(sec, c, &data); <-- process read data based on section
}
}
int main() {
FILE * f = fopen("file","r");
int ret = 0;
char sect = 0;
while ((ret=fscanf(f, "%c%*c\n", &sect))!=EOF){
switch (sect) {
case 'A':
readsec(f, "%d", 1, 'A');break;
case 'B':
readsec(f, "%d %d", 2, 'B');break;
case 'C':
readsec(f, "%d %d %d", 3, 'C');break;
default:break;
}
}
return 0;
}

Related

Where am I going wrong in getting this function to do what I would like?

I have written the following function in my C program. The program loads a text file (Les Miserables Vol. I) as well as another text file of 20 of the characters names. The purpose of this function is to scan the entire file, line by line, and count the number of times any of the 20 names appear.
NumOfNames = 20.
Names is an array of the 20 names stored from Names[1] - Names[20].
MaxName is a global integer variable which I would like to store the total number of name appearances throughout the file (It should be in the hundreds or even thousands).
EDIT: After the function is executed, the value of MaxName is 4. I am completely lost as to where I have made a mistake, but it appears that I have made several mistakes throughout the function. One seems to be that it only executed the first iteration of the for loop i.e. it only searches for Name[1], however the first name appears 196 times in the file, so it still isnt even working correctly for just the first name.
void MaxNameAppearances()
{
char LineOfText[85];
char *TempName;
FILE *fpn = fopen(LesMisFilePath, "r+");
for(i = 1; i<=NumOfNames; i++)
{
while(fgets(LineOfText, sizeof(LineOfText), fpn))
{
TempName = strstr(LineOfText, Names[i]);
if(TempName != NULL)
{
MaxName++;
}
}
}
fclose(fpn);
}
I guess that one problem of the code is that it would have to read the file upon every iteration of i. Try to re-order the loops like this:
while(fgets(LineOfText, sizeof(LineOfText), fpn))
{
for(i = 1; i<=NumOfNames; i++)
{
TempName = strstr(LineOfText, Names[i]);
if(TempName != NULL)
{
MaxName++;
}
}
}
This reads a line, checks the occurrances of all names in that line and then goes on to the next line.
If you do it your way, you will be at the end of file for i == 1 already.

fgetc not starting at beginning of file - c [duplicate]

This question already has an answer here:
fgetc not starting at beginning of large txt file
(1 answer)
Closed 9 years ago.
Problem solved here:
fgetc not starting at beginning of large txt file
I am working in c and fgetc isn't getting chars from the beginning of the file. It seems to be starting somewhere randomly within the file after a \n. The goal of this function is to modify the array productsPrinted. If "More Data Needed" or "Hidden non listed" is encountered, the position in the array, productsPrinted[newLineCount], will be changed to 0. Any help is appreciated.
Update: It works on smaller files, but doesn't start at the beginning of the larger,617kb, file.
function calls up to category:
findNoPics(image, productsPrinted);
findVisible(visible, productsPrinted);
removeCategories(category, productsPrinted);
example input from fgetc():
Category\n
Diagnostic & Testing /Scan Tools\n
Diagnostic & Testing /Scan Tools\n
Hidden non listed\n
Diagnostic & Testing /Scan Tools\n
Diagnostic & Testing /Scan Tools\n
Hand Tools/Open Stock\n
Hand Tools/Sockets and Drive Sets\n
More Data Needed\n
Hand Tools/Open Stock\n
Hand Tools/Open Stock\n
Hand Tools/Open Stock\n
Shop Supplies & Equip/Tool Storage\n
Hidden non listed\n
Shop Supplies & Equip/Heaters\n
Code:
void removeCategories(FILE *category, int *prodPrinted){
char more[17] = { '\0' }, hidden[18] = { '\0' };
int newLineCount = 0, i, ch = 'a', fix = 0;
while ((ch = fgetc(category)) != EOF){ //if fgetc is outside while, it works//
more[15] = hidden[16] = ch;
printf("%c", ch);
/*shift char in each list <- one*/
for (i = 0; i < 17; i++){
if (i < 17){
hidden[i] = hidden[i + 1];
}
if (i < 16){
more[i] = more[i + 1];
}
}
if (strcmp(more, "More Data Needed") == 0 || strcmp(hidden, "Hidden non listed") == 0){
prodPrinted[newLineCount] = 0;
/*printf("%c", more[0]);*/
}
if (ch == '\n'){
newLineCount++;
}
}
}
Let computers do the counting. You have not null terminated your strings properly. The fixed strings (mdn and hdl are initialized but do not have null terminators, so string comparisons using them are undefined.
Given this sample data:
Example 1
More Data Needed
Hidden non listed
Example 2
Keeping lines short.
But as they get longer, the overwrite is worse...or is it?
Hidden More Data Needed in a longer line.
Lines containing "Hidden non listed" are zapped.
Example 3
This version of the program:
#include <stdio.h>
#include <string.h>
static
void removeCategories(FILE *category, int *prodPrinted)
{
char more[17] = { '0' };
char hidden[18] = { '0' };
char mdn[17] = { "More Data Needed" };
char hnl[18] = { "Hidden non listed" };
int newLineCount = 0, i, ch = '\0';
do
{
/*shift char in each list <- one*/
for (i = 0; i < 18; i++)
{
if (i < 17)
hidden[i] = hidden[i + 1];
if (i < 16)
more[i] = more[i + 1];
}
more[15] = hidden[16] = ch = fgetc(category);
if (ch == EOF)
break;
printf("%c", ch); /*testing here, starts rndmly in file*/
//printf("<<%c>> ", ch); /*testing here, starts rndmly in file*/
//printf("more <<%s>> hidden <<%s>>\n", more, hidden);
if (strcmp(more, mdn) == 0 || strcmp(hidden, hnl) == 0)
{
prodPrinted[newLineCount] = 0;
}
if (ch == '\n')
{
newLineCount++;
}
} while (ch != EOF);
}
int main(void)
{
int prod[10];
for (int i = 0; i < 10; i++)
prod[i] = 37;
removeCategories(stdin, prod);
for (int i = 0; i < 10; i++)
printf("%d: %d\n", i, prod[i]);
return 0;
}
produces this output:
Example 1
More Data Needed
Hidden non listed
Example 2
Keeping lines short.
But as they get longer, the overwrite is worse...or is it?
Hidden More Data Needed in a longer line.
Lines containing "Hidden non listed" are zapped.
Example 3
0: 37
1: 0
2: 0
3: 37
4: 37
5: 37
6: 0
7: 0
8: 37
9: 37
You may check which mode you opened the file, and you may have some error-check to make sure you have got the right return value.
Here you can refer to man fopen to get which mode to cause the stream position.
The fopen() function opens the file whose name is the string pointed to
by path and associates a stream with it.
The argument mode points to a string beginning with one of the follow‐
ing sequences (Additional characters may follow these sequences.):
r Open text file for reading. The stream is positioned at the
beginning of the file.
r+ Open for reading and writing. The stream is positioned at the
beginning of the file.
w Truncate file to zero length or create text file for writing.
The stream is positioned at the beginning of the file.
w+ Open for reading and writing. The file is created if it does
not exist, otherwise it is truncated. The stream is positioned
at the beginning of the file.
a Open for appending (writing at end of file). The file is cre‐
ated if it does not exist. The stream is positioned at the end
of the file.
a+ Open for reading and appending (writing at end of file). The
file is created if it does not exist. The initial file position
for reading is at the beginning of the file, but output is
always appended to the end of the file.
And there is another notice, that the file you operated should not more than 2G, or there maybe problem.
And you can use fseek to set the file position indicator.
And you can use debugger to watch these variables to see why there are random value. I think debug is efficient than trace output.
Maybe you can try rewinding the file pointer at the beginning of your function.
rewind(category);
Most likely another function is reading from the same file. If this solves your problem, it would be better to find which other function (or previous call to this function) is reading from the same file and make sure rewinding the pointer won't break something else.
EDIT:
And just to be sure, maybe you could change the double assignment to two different statements. Based on this post, your problem might as well be caused by a compiler optimization of that line. I haven't checked with the standard, but according to best answer the behavior in c and c++ might be undefined, therefore your strange results. Good luck

Read lines from a file into character arrays

I'm completely new to C and I'm working on a program which has to read in 3 lines from a text file(two numbers and a mathematical symbol) and write out the result. So for example:
The text file looks like:
1
4
*
and my program should be able to read the 3 lines and write out something like "1*4 = 4" or something.
I managed to get to a point where i can read the 3 lines in and show them on screen, so I thought I should put the two numbers in one array and the symbol in another one. The problem is, that I tried to see if the arrays contain the numbers I put in them and my output has some huge numbers in it and I'm not sure why.
Here's the code i wrote:
#include <stdio.h>
#include <io.h>
#include <string.h>
int main(void)
{
int res = 1; /*Creates an integer to hold the result of the check for the file*/
const char *file = "input.txt"; /*String holding the name of the file with the input data*/
res = access(file,R_OK); /*Checks if the file "input.txt" exists*/
if(res == -1)
{ /*IF the file doesn't exist:*/
FILE *input = fopen("input.txt","w"); /*This creates a file called "input.txt" in the directory of the program*/
char write[] = "1\n1\n+"; /*This variable holds the string that's to be written to the file*/
fprintf(input,"%s",write); /*This writes the variable "write" to the file*/
printf("input.txt file created!"); /*Tells you the file is created*/
fclose(input); /*Closes the file after it's done*/
}
else
{ /*IF the file exists:*/
FILE *f = fopen("input.txt","r");
//char line[ 5000 ];
//while ( fgets ( line, sizeof line, f ) != NULL )
//{
// fputs ( line, stdout );
//}
char line[5000];
char nums[2];
char symbol[1];
int i = 0;
while(fgets(line,sizeof line,f)!=NULL)
{
i++;
if(i < 3)
{
fputs(nums,f);
}
else
{
fputs(symbol,f);
}
printf("%d,%d",nums,symbol);
}
printf("\n\n\n");
scanf("\n");
}
return 0;
}
Any help would be greatly appreciated!
Thank you in advance
If you require any more information i will provide it.
This is a self-explanatory algorithm. Also, here is the code that does the operation you are looking for. Generally, the complex operations are accomplished using stack, push and pop method. Once the operators are pushed. One need to apply the BODMAS rule,to evaluate the expression. Since the problem given to you is simple, a simple expression evaluation. This can be simply achieved by FIFO. Here is the algorithm, general explanation. Afterwards, the code is present. This code is well tested.You can extend it to do operations like +,-,division /, %, etc. If you like my answer please appreciate.
#include "stdio.h"
int main(int argc, char *argv[])
{
FILE *fp_op;
int buff[2]; /** assuming a simple operation, thus the buffer size is 3 only, the last one is to store the NULL **/
char operat_buff[2]; /** assuming this operation we can extend it to evaluate an expression **/
fp_op = fopen("calc.txt","rb");
if ( fp_op == 0 )
{
perror("The file doesn't exist to calculate\r\n");
goto error;
}
/** Read the two numbers here **/
fscanf(fp_op,"%d",&(buff[0]));
printf("The buff[1] = %d\r\n",buff[0]);
fscanf(fp_op,"%d",&(buff[1]));
printf("The buff[1] = %d\r\n",buff[1]);
/** read the next line now \n **/
operat_buff[0] = fgetc(fp_op);
/** read the actual character now **/
operat_buff[0] = fgetc(fp_op);
printf("The operat_buff[0] = %d\r\n",operat_buff[0]);
/** Read operation completed **/
/** use switch here **/
switch(operat_buff[0])
{
case '*':
printf("The multiplication result=%d\r\n",buff[0]*buff[1]);
break;
case '+':
printf("The Addition result=%d\r\n",buff[0]+buff[1]);
break;
default:
printf("Add more operations\r\n");
}
return 0;
error:
return -1;
}
I assume that the calc.txt was something like this.
calc.txt
3
5
*
Note: This code is compiled and verified.It compiles with zero warnings. It does the error checking too. You can directly copy and paste it.
What are you reading from the files are simply characters codes: the program has no way of figuring by itself that the character "4" corresponds to the integer number 4. The %d placeholder of printf expects int variables, or it won't work.
If you want just to print the characters you have to save them in char variables (or a char array) and use the placeholder %c in printf. If you want to actually use the numbers and symbols in your program you have more work to do.
Not only in C, but I think in most languages you have to "parse" the characters to numbers.
In C you can use the functions atoi or atol (you have to #include <stdlib.h>) in order to do this conversion.
In order to parse the symbol I'm afraid you will have to use an if or a switch to read the character and perform the operation accordingly.
For example your loop could look like:
while(fgets(line,sizeof line,f)!=NULL)
{
int op1;
int op2;
int res;
char symbol;
i++;
switch (i) {
case 1:
//First line is first operand
op1 = atoi(line);
printf("op1 %d\n",op1);
break;
case 3:
//Second line is second operand
op2 = atoi(line);
printf("op2 %d\n",op2);
break;
//Fifth line is the operator, could be +,-,%./ or anything
case 5:
symbol = line[0];
printf("operand %c\n",symbol);
switch(symbol) {
case '+':
res = op1+op2;
break;
case '-':
res = op1-op2;
break;
default:
//operation not defined, return
return;
}
printf("%d%c%d = %d",op1,symbol,op2,res);
}
}
printf("%d,%d",nums,symbol);
In your code nums and symbol are strings, you can't print them with %d. What you are getting are the addresses of the nums and symbol arrays, respectively - even if that's not the right way of printing an address.
You'll likely want to convert them to integers, using strtol or sscanf and then use those to perform the computation.

Using a function to read in a file

I have the code below which compiles fine in xcode, but when I take it across to Microsoft Visual studio I get a bunch of errors.
void openfile(int mapArray[MAX_HEIGHT][MAX_WIDTH], int *interest, int *dimension1, int *dimension2)
{
int counter = 0;
char buffer;
int rowss, colss;
*interest = 0;
FILE *f;
f = fopen(FILENAME, "r");
if (f==NULL) {
printf("Map file could not be opened");
return 0;
}
// create char array the dimensions of the map
fscanf(f, "%d %d" , dimension1, dimension2 );
// printf("%d %d\n" , dimensions[0], dimensions[1]);
// Reads the spaces at the end of the line till the map starts
buffer=fgetc(f);
while (buffer!='*') {
buffer=fgetc(f);
}
// Read the txt file and print it out while storing it in a char array
while (buffer!=EOF) {
mapArray[rowss][colss]=buffer;
colss++;
// Count up the points of interest
if (((buffer>64)&&(buffer<90))||(buffer=='#') ) {
counter++;
}
// resets column counter to zero after newline
if (buffer=='\n') {
colss=0;
rowss++;
}
buffer=fgetc(f);
}
// Closes the file
fclose(f);
*interest=counter;
}
Which parts are creating all the errors?
I get this list of errors when attempting to compile
Thanks in advance.
I see a few immediate problems. First, you're not initialising rowss or colss before you use them, hence they could contain any value.
Second, fgetc() returns an int so that you can detect end of file. By using a char to hold the return value, you're breaking the contract with the standard library.
Thirdly, you return a 0 if the filename couldn't be opened, despite the fact that the function is specified to return void (ie, nothing).
No doubt those are three of the errors the compiler picked up on, there may be others, and you should probably post the error list with your question for a more exhaustive analysis.

C File Handling / Structure Problem

How does one read in a txt file containing names and marks of students and inputting them
into an array of structures.
maximum allowable records are 7:
e.g. James 45
Mary 70
Rob 100
First, define the structure. The structure describes what a record is; what data it contains. Here you have a student's name and his or her mark.
Second you need to prepare the array to write the objects of the structure into. You already know from the problem description that no more than 7 students are allowed, so you can define the length of the array to that number.
Next, open the text file.
Lastly write a loop that takes as input from the file a string for the student's name and an integer (or a floating-point point number if you so choose) for their mark. In the loop create a structure for each record and insert the structure into the array.
And of course, don't forget to close the file when you're done.
That's all there is to it. If you have any syntax or logic questions then ask in the comments, and we'll gladly help.
Read the man page for fopen: http://linux.die.net/man/3/fopen
This should give you somewhere to start.
Also, the man page for fread and fgets could be helpful. There are many ways to read from a file and the path you choose will depend on numerous things, such as the structure of the file and the amount of security you want in your application.
found this code that is similar enough that should be able to help you get done what you need.
#include <stdio.h>
#include <string.h>
/* Sample data lines
5 0 Wednesday Sunny
6 2 Thursday Wet
*/
int main() {
/* Define a daydata structure */
typedef struct {
int n_adults; int n_kids;
char day[10]; char weather[10];
} daydata ;
daydata record[30];
FILE * filehandle;
char lyne[121];
char *item;
int reccount = 0;
int k;
/* Here comes the actions! */
/* open file */
filehandle = fopen("newstuff.txt","r");
/* Read file line by line */
while (fgets(lyne,120,filehandle)) {
printf("%s",lyne);
item = strtok(lyne," ");
record[reccount].n_adults = atoi(item);
item = strtok(NULL," ");
record[reccount].n_kids = atoi(item);
item = strtok(NULL," ");
strcpy(record[reccount].day,item);
item = strtok(NULL,"\n");
strcpy(record[reccount].weather,item);
printf("%s\n",record[reccount].day);
reccount++;
}
/* Close file */
fclose(filehandle);
/* Loop through and report on data */
printf("Weather Record\n");
for (k=0; k<reccount; k++) {
printf("It is %s\n",record[k].weather);
}
}
http://www.wellho.net/resources/ex.php4?item=c209/lunches.c
Give a holler with code you tried if you have problems changing it to fit your needs.

Resources