I'm writing a practice program to read integers from a file and sort them. I'm a little confused about file IO in C. What i have so far is below, I was hoping someone could take a look at it and offer any corrections/suggestions if they have any...
// TODO: Open input file and do same as above
char *mode = "r";
FILE *fp = fopen(inputFile, mode);
if(fp == NULL){
fprintf(stderr, "Can't open input file!");
exit(1);
}
// Load the numbers into a buffer and get a count
int buffer[100];
int count = 0;
while(fscanf(fp, "%d", &buffer[count]) == 1) {
count++;
}
// Initialize the array with the proper size
integers = (int*)malloc(sizeof(count*sizeof(int)));
// Load the integers into the array
rewind(fp);
for(int i = 0; i < count; i++){
if(fscanf(fp, "%d", &integers[count] != 1)){
fprintf(stderr, "Error loading integers into array");
exit(1);
}
}
fscanf() returns number of elements successfully read so check against the required number of elements to be read and in your case you can just read values to your array and increment index. Later use the value of index to allocate memory.
int *temp;
integers = malloc(sizeof(int)));
while(fscanf(fp, "%d", &integers[index]) == 1)
{
index++;
temp = realloc(integers,sizeof(int) * (index+1));
if(temp != NULL)
integers = temp;
}
Related
I need help to read the numbers of a .txt file and put them in an array. But only from the second line onwards. I'm stuck and don't know where to go from the code that i built.
Example of the .txt file:
10 20
45000000
48000000
56000000
#define MAX 50
int main (void){
FILE *file;
int primNum;
int secNum;
int listOfNumers[50];
int numberOfLines = MAX;
int i = 0;
file = fopen("file.txt", "rt");
if (file == NULL)
{
printf("Error\n");
return 1;
}
fscanf(file, "%d %d\n", &primNum, &secNum);
printf("\n1st Number: %d",primNum);
printf("\n2nd Number: %d",secNum);
printf("List of Numbers");
for(i=0;i<numberOfLines;i++){
//Count the number from the second line onwards
}
fclose(file);
return 0;
}
You just need a loop to keep reading ints from file and populate the listOfNumers array until reading an int fails.
Since you don't know how many ints there are in the file, you could also allocate the memory dynamically. Example:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE* file = fopen("file.txt", "rt");
if(file == NULL) {
perror("file.txt");
return 1;
}
int primNum;
int secNum;
if(fscanf(file, "%d %d", &primNum, &secNum) != 2) {
fprintf(stderr, "failed reading primNum and secNum\n");
return 1;
}
unsigned numberOfLines = 0;
// allocate space for one `int`
int* listOfNumers = malloc((numberOfLines + 1) * sizeof *listOfNumers);
// the above could just be:
// int* listOfNumers = malloc(sizeof *listOfNumers);
while(fscanf(file, "%d", listOfNumers + numberOfLines) == 1) {
++numberOfLines;
// increase the allocated space by the sizeof 1 int
int* np = realloc(listOfNumers, (numberOfLines + 1) * sizeof *np);
if(np == NULL) break; // if allocating more space failed, break out
listOfNumers = np; // save the new pointer
}
fclose(file);
puts("List of Numbers:");
for(unsigned i = 0; i < numberOfLines; ++i) {
printf("%d\n", listOfNumers[i]);
}
free(listOfNumers); // free the dynamically allocated space
}
There are a few ways to approach this; if you know the size of the first line, you should be able to use fseek to move the position of the file than use getline to get each line of the file:
int fseek(FILE *stream, long offset, int whence);
The whence parameter can be:
SEEK_SET : the Beginning
SEEK_CUR : the current position
SEEK_END : the End
The other option would to encapsulate the entire file read in a while loop:
char *line = NULL;
size_t linecap = 0;
ssize_t linelen;
int counter = 0;
while((linelen = getline(&line, &linecap, file)) != -1){
if counter == 0{
sscanf(line, "%d %d\n", &primNum, &secNum);
}else{
//Process your line
}
counter++; //This would give you your total line length
}
For example, I have a txt file with all integers. I want to count how many integers there are in order to allocate an array.
int array[0];
int count = 0;
FILE *file = fopen("file1.txt","r");
while(fscanf(file,"%d",&array[count])==1){
count++;
}
printf("%d",count);
Currently there are error messages and won't go through. Is this the way fscanf work?
You cannot create an array of size 0. If you want to only count the number of integers, don't use an array, but only a temporary variable.
It is a good idea to check whether you opened the file correctly and to close the file too.
#include <stdio.h>
int main(){
int temp;
int count = 0;
FILE *file = fopen("file1.txt","r");
if(file == NULL){
printf("Could not open specified file");
return -1;
}
while(fscanf(file,"%d",&temp)==1){
count++;
}
fclose(file);
printf("%d",count);
}
return 0;
}
If you also want to store the values for later use, you could for example read through the file twice, first time counting the amount of integers, then using this amount to declare the needed array. An important thing before the second run is to rewind the file pointer, to read the file from the start.
#include <stdio.h>
int main(){
int temp;
int count = 0;
FILE *file = fopen("file1.txt","r");
if(file == NULL){
printf("Could not open specified file");
return -1;
}
while(fscanf(file,"%d",&temp)==1){
count++;
}
printf("%d",count);
if(count == 0){ //do not create array of size 0
fclose(file);
}
else{
//second run
int array[count];
rewind(file);
for(int i=0; i<count; i++){
fscanf(file,"%d",&array[count]);
}
fclose(file);
//continue using array...
}
return 0;
}
int array[0];
and
fscanf(file,"%d",&array[count])
will lead to a segmentation fault because you're accessing the array out of bounds.
If you need flexible array you need
int *array
a place-holder for storing each number from fscanf
realloc array every time you find a new number and add the number to array.
#include <stdio.h>
#define isDigit(c) ('0' <= (c) && (c) <= '9') ? 1 : 0
int main() {
FILE *fd;
int counter, c, tmp;
if ((fd = fopen("PathToFile", "r")) != NULL){
do{
c = getc(fd);
if (isDigit(c)) tmp = 1;
else if (tmp == 1 && !isDigit(c)){
counter++, tmp = 0;
}
}while (c != EOF);
}else{
printf("Couldn't find File!");
return 1;
}
fclose(fd);
printf("%i", counter);
return 0;
}
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'm trying to read the number of a txt file like this:
input=20
output=10
hidden=5
....
I tried with this code:
char line[30];
char values[100][20];
int i = 0;
FILE *fp;
fp = fopen("myFile.txt", "r");
if(fp == NULL)
{
printf("cannot open file\n");
return 0;
}
while(fgets(line, sizeof(line), fp) != NULL)
{
sscanf(line, "%[^=]", values[i])
printf("%s\n", values[i]);
i++;
}
fclose(fp);
But I obtain only the first word and never the number after the =.
I get
input
output
etc
instead of
20
10
5
etc
How can I get the number??
This line
sscanf(line, "%[^=]", values[i]);
means "read everything up to, but not including, the = sign into values[i]".
If you are interested in the numeric part after the equal sign, change the call as follows:
sscanf(line, "%*[^=]=%19s", values[i]);
This format line means "read and ignore (because of the asterisk) everything up to, and including, the equal sign. Then read a string of length of up to 19 characters into values[i]".
Demo.
Don't use sscanf() for that, redeclare values to store the integers like
int values[LARGE_CONSTANT_NUMBER];
and after fgets() just use strchr
char *number;
number = strchr(line, '=');
if (number == NULL)
continue;
number += 1;
values[i] = strtol(number, NULL, 10);
you could also use malloc() and realloc() if you wish, to make the values array dynamic.
Try it if you like
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char line[100];
int values[100];
int i;
FILE *fp;
size_t maxIntegers;
fp = fopen("myFile.txt", "r");
if (fp == NULL)
{
perror("cannot open file\n");
return 0;
}
i = 0;
maxIntegers = sizeof(values) / sizeof(values[0]);
while ((fgets(line, sizeof(line), fp) != NULL) && (i < maxIntegers))
{
char *number;
number = strchr(line, '=');
if (number == NULL) /* this line does not contain a `=' */
continue;
values[i++] = strtol(number + 1, NULL, 10);
printf("%d\n", values[i - 1]);
}
fclose(fp);
return 0;
}
with this technique you avoid unecessarily storing the number as a string.
This code opens a directory, and for every file in the directory it loops through every line of data inside the file, and then parses it to do some calculations and outputs the resulting data into a new file.
The problem is that I can only output a maximum of around 1021 files. I'm closing all of the fopens after outputting all the data, so I'm not sure what I'm doing wrong.
Shouldn't fclose() be closing the open files therefore this not happening?
int main(int argc, char *argv[])
{
//sample data values
double lat;
double lon;
double convergence;
double pt_scale;
int zone = 54;
double major_axis = 6378137.0000;
double flattening = (1/298.2572);
double zoneWidth = 6;
double centMeridian = -177;
double falseEast = FALSE_EASTING;
double falseNorth = FALSE_NORTHING;
double scale = SCALE_FACTOR;
int max_size = 128;
int current_size = max_size;
char *pathStr = malloc(max_size);
char *outPathStr = malloc(max_size);
char coData[100]; //max length of line;
long firstTerm, secondTerm; //terms we will split the line into, lat, lon, elevation.
int counter = 0; //pos counter
int d = EOF; //end of file ASCII
char strIn[200];
char* elevation;
char strOut[200];
char dirOut[200]; //sprintf must use a actual defined buffer otherwise there will be a buffer overflow.
char* cchr;
int j;
_setmaxstdio(2048);
printf("Please enter the path of the files: \n");
getUserInput(pathStr, current_size, max_size);
printf("Please enter the output path of the files: \n");
getUserInput(outPathStr, current_size, max_size);
//loop through each file in the directory. Open the file, convert, then close it.
//we will use dirent.h as it is cross platform so we wont have to worry about sharing issues
DIR *dir; //new directory
struct dirent *ent;
dir = opendir(pathStr); //allcate it a path
if(opendir(pathStr) == NULL)
{ printf("Error: %d (%s)\n", errno, strerror(errno));}
int k;
if(dir != NULL)
{
while((ent = readdir(dir)) != NULL) //loop through each file in the directory.
{
//open the file and loop through each line converting it then outputing it into a new file
if((!strcmp(ent->d_name,"..") || !strcmp(ent->d_name,".")) == 1)
{
//dont want these directories
continue;
}
else
{
sprintf(strIn,"%s%s",pathStr,ent->d_name); //get the file n
FILE *fp = fopen(strIn, "r");
if(fopen(strIn, "r") == NULL) //for inputting file
{ printf("Error: %d (%s)\n", errno, strerror(errno));
getchar();
break; }
sprintf(dirOut,"%s%d%s",outPathStr,counter,".geo");
printf("%s \n",dirOut);
FILE *fp2 = fopen(dirOut, "w"); //for outputting file
if(fopen(dirOut, "w") == NULL)
{ printf("Error: %d (%s)\n", errno, strerror(errno));
getchar();
break; }
while(fgets(coData, 100, fp) != NULL)//loop through line by line, allocate into 2 doubles and a string, pass the two coordinates and convert
{
//extract terms from coData
char * pch; //pointer to array pos
char * pend;
pch = strtok(coData," ");
j = 0;
while(j <= 2) //We only want to split the first three parameters.
{
//convert char array to double for co-oridinate conversion
if(j == 0)
{
firstTerm = atof(pch); //latitude;
j++;
continue;
}
if(j == 1)
{
pch = strtok(NULL, " ");
secondTerm = atof(pch); //longitude
j++;
continue;
}
if(j == 2)
{
pch = strtok(NULL," ");
elevation = pch; //elevation doesnt need to be converted because it isnt used in the coordinate conversion.
break;
}
}
grid2spheroid(&lat,&lon,&convergence,&pt_scale,firstTerm,secondTerm,zone,0, major_axis,flattening,zoneWidth,centMeridian,falseEast,falseNorth,scale);
sprintf(strOut,"%f %f %s",lat,lon,elevation);
//printf("%d %d", lat, lon);
fputs(strOut,fp2);
} //end of while
fclose(fp2);
fclose(fp);
counter++;
}
}
closedir(dir);
}
free(pathStr); //finished using the path string so we can finish the
free(outPathStr);
getchar();
return 0;
}
void getUserInput(char *pathStr, int current_size, int max_size)
{
unsigned int i = 0;
if(pathStr != NULL)
{
int c = EOF;
//get the user input and reallocate the memory size if the input it too large.
while((c = getchar()) != '\n' && c != EOF) //WHILE NOT END OF FILE OR NEW LINE (USER PRESSED ENTER)
{
pathStr[i++] = (char)c;
if(i == current_size)
{
current_size = i+max_size;
pathStr = realloc(pathStr, current_size);
}
}
}
}
You aren't closing all the files ;-)
FILE *fp = fopen(strIn, "r");
if(fopen(strIn, "r") == NULL) //for inputting file
Same applies to your output.
I think you meant something more like:
FILE *fp = fopen(strIn, "r");
if(fp == NULL) //for inputting file
{
// error handling.
No, no! You're opening every file twice (and only closing once)!
/* Bad! */
dir = opendir(pathStr); //allcate it a path
if(opendir(pathStr) == NULL)
{ printf("Error: %d (%s)\n", errno, strerror(errno));}
int k;
/* Correct */
dir = opendir(pathStr); //allocate it a path
if(!dir) {
printf("Error: %d (%s)\n", errno, strerror(errno));
return;
}
You're also doing the same thing with fopen(). In both places :)
Just check the pointer; don't call "fopen()" a second time; don't call "opendir()" a second time!
Also: please don't put code on the same line as your opening brace. OK?
dir = opendir(pathStr); //allcate it a path
if(opendir(pathStr) == NULL)
(...)
FILE *fp2 = fopen(dirOut, "w"); //for outputting file
if(fopen(dirOut, "w") == NULL)
(...)
FILE *fp = fopen(strIn, "r");
if(fopen(strIn, "r") == NULL) //for inputting file
Here you open the file twice but only store the pointer once. Change these to:
FILE *fp = fopen(strIn, "r");
if(fp == NULL) //for inputting file
and the other one in the same way.