C: Too many files open - c

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.

Related

How to read a file with numbers on each line in C?

I'm trying to read a file that contains 10 numbers then adding them to an array so I can sort them later on but I'm having trouble reading them in. Not sure why this isn't working for me, can someone explain what is wrong? There's only a number on each lines.
10.05
11.01
9.03
double nums[10] = {0};
int count;
if ((fptr = fopen("filename", "r")) == NULL){
printf("Error opening file.\n");
}
while ((c = getc(fptr)) != EOF){
if (c != '\n'){
nums[count] = (double)c;
count = count + 1;
}
}
fclose(fptr);
What is wrong:
You are storing only one character.
You are updating count each times on non-newline characters while updating should be on newline characters.
count is used without being initialized.
Casting to double is not for this usage.
Possible fix:
int c;
FILE* fptr;
char line[1024]; // add line buffer and size tracking
int lineCount = 0;
double nums[10] = {0};
int count = 0; // initialize count
if ((fptr = fopen("filename", "r")) == NULL){
printf("Error opening file.\n");
} else { // avoid using NULL to read file
while ((c = getc(fptr)) != EOF){
if (c == '\n'){ // update nums on newline character
line[lineCount] = '\0'; // don't forget to terminate the string
nums[count] = atof(line); // atof() from stdlib.h is useful to convert string to number
count = count + 1;
lineCount = 0; // start to read next line
} else { // read line contents
line[lineCount] = (char)c;
lineCount = lineCount + 1;
}
}
fclose(fptr);
}
Here I go
#include <stdio.h>
#include <stdlib.h>
int main()
{
double values[10];
int count;
FILE *f = fopen("filename", "r");
if (f == NULL)| {
fprintf(stderr, "Some error message");
return EXIT_FAILURE; // We cannot go any further - file is dead
}
// This is basic - you could overcome error problems
// When able to read (including white space) we carry on until the array is full
// This is an area for improvement - error checking etc.
for (count = 0; count < 10 && fscanf(f, " %lf", &values[count]) != 1; count ++);
fclose(f);
return EXIT_SUCCESS;
}

How to edit .csv files in C

I'm new at programming, and I need help in my C project. I have to search for a city, confirm it exists in the first file (city.csv), and take its id from there. Then I have to match that id with the corresponding one in the second file (meteo.csv), and then edit its weather information, that is in that second file. However, I don't know how I can take the city id from the first file, and then how to edit the second file after obtaining all the new weather informations. Here is the code:
void addInfo() {
FILE * fp;
char id_city[100];
char city[100];
char humidity[100];
char temp_max[100];
char temp_min[100];
char pressure[100];
char date[100];
printf("Name of the city: ");
scanf("%s", city);
// I think it's here that I have to write the code for take the city's id from the first file
if (id_city != NULL) {
printf("Maximun temperature: ");
scanf("%s", temp_max);
printf("Minimun temperature: ");
scanf("%s", temp_min);
printf("Humidity: ");
scanf("%s", humidity);
printf("Pressure: ");
scanf("%s", pressure);
printf("Date, in the format YYYY-MM-DD: ");
scanf("%s", date);
fp = fopen ("meteo.csv", "a");
fprintf(fp, "%s, %s, %s, %s, %s \n", temp_max, temp_min, humidity, pressure, date); //I think there's something wrong here too...
fclose(fp);
printf("Information edited successfully");
}
The file city.csv has 152 lines and 4 columns:
(id_city,city,county,district)
such as
(56,Lisbon,Lisbon,Lisbon)
The file meteo.csv has 152 lines and 7 columns:
(id_meteo_city,id_city,temp_max,temp_min,humidity,pressure,date)
such as
(56,56,14,5,62,1025,2018-02-12)
The first thing I would do is encapsulate the data in a struct, that makes it
easier to map a line of a CSV file into an object representing a line.
If both files city.csv and meteo.csv have different columns, I'd create a
different struct for each file. If both files have the same columns, you could
use the struct. I assume that both files are different and that city has the
format meteo_id,city_id,name.
typedef struct city_t {
int meteo_id;
int city_id;
char name[100]; // no city should have
// longer than 100 chars
} city_t;
typedef struct meteo_t {
int meteo_id;
int city_id;
int tempt_max;
int tempt_mix;
double humidity;
double preassure;
char date[11];
} meteo_t;
Let's assume that both files are well formatted, otherwise you would have to
write code that checks for errors and handles them, that would be the next step
in the exercise, so I'm going to write only the basic version with basic error
recognition.
#include <stdio.h>
#include <string.h>
#include <errno.h>
// takes 2 params, the filename and a pointer
// to size_t where the number of cities is stored
city_t *read_cities(const char *filename, size_t *len)
{
if(filename == NULL || len == NULL)
return NULL;
FILE *fp = fopen(filename, "r");
if(fp == NULL)
{
fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
return NULL;
}
city_t *arr = NULL, *tmp;
*len = 0;
// assuming that no line will be longer than 1023 chars long
char line[1024];
while(fgets(line, sizeof line, fp))
{
tmp = realloc(arr, (*len + 1) * sizeof *arr);
if(tmp == NULL)
{
fprintf(stderr, "could not parse the whole file %s\n", filename);
// returning all parsed cities so far
if(*len == 0)
{
free(arr);
arr = NULL;
}
return arr;
}
arr = tmp;
// %99[^\n] is to read up to 99 characters until the end of the line
if(sscanf(line, "%d,%d,%99[^\n]", &(arr[*len].meteo_id),
&(arr[*len].city_id), arr[*len].name) != 3)
{
fprintf(stderr, "Invalid line format (skipping line):\n%s\n", line);
// skip this line, and decrement *len
(*len)--;
continue;
}
// incrementing only when parsing of line was OK
(*len)++;
}
fclose(fp);
// file is empty or
// all lines have wrong format
if(*len == 0)
{
free(arr);
arr = NULL;
}
return arr;
}
void print_cities(city_t *cities, size_t len, FILE *fp)
{
if(cities == NULL || fp == NULL)
return;
for(size_t i = 0; i < len; ++i)
fprintf(fp, "%d,%d,%s\n", cities[i].meteo_id, cities[i].citiy_id,
cities[i].name);
}
Now I've written the read and write functions for the file citiy.csv assuming the
format meteo_id;city_id;name. The print_cities allows you to print the CSV
content on the screen (passing stdout as the last argument) or to a file
(passing a FILE object as the last argument).
You can use these functions as templates for reading and writing meteo.csv, the
idea is the same.
You can use these function as follows:
int main(void)
{
size_t cities_len;
city_t *cities = read_cities("city.csv", &cities_len);
// error
if(cities == NULL)
return 1;
do_something_with_cities(cities, cities_len);
// update csv
FILE *fp = fopen("city.csv", "w");
if(fp == NULL)
{
fprintf(stderr, "Could not open city.csv for reading: %s\n",
strerror(errno));
free(cities);
return 1;
}
print_cities(cities, cities_len, fp);
fclose(fp);
free(cities);
return 0;
}
Now for your exercise: write a similar function that parses meteo.csv (using
my function as a template shouldn't be that difficult) and parse both files. Now
that you've got them in memory, it's easy to manipulate the data (insert,
update, delete). Then write the files like I did in the example and that's it.
One last hint: how to search for a city:
// returns the index in the array or -1 on error or when not found
int search_for_city_by_name(city_t *cities, size_t len, const char *name)
{
if(cities == NULL || name == NULL)
return -1;
for(size_t i = 0; i < len; ++i)
if(strcmp(name, cities[i].name) == 0)
return i;
// not found
return -1;
}
Now I have given you almost all parts of the assignment, all you have to do is
stick them together and write the same functions for the meteo.csv file.
To edit one field:
void _ERR(char a) {
if (a == "f") printf("\n\tError File !!\n\n");
if (a == "m") printf("\n\tError Memory !!\n\n");
exit(1); }
char* stmm(const char* src) {
char* dst = malloc(strlen(src) + 1);
if (dst == NULL) return NULL;
strcpy(dst, src);
return dst; }
const char* getfield(char* line, int num) {
const char* tok;
for (tok = strtok(line, ",");
tok && *tok;
tok = strtok(NULL, ",\n"))
{
if (!--num)
return tok;
}
return NULL; }
void edit_file(char* FName, char* NewValue, int row, int col) {
int i, r = 0, c;
char line[1024];
FILE* fr, * fw;
fr = fopen(FName, "r");
fw = fopen(FName, "r+");
if (fr == NULL|| fw == NULL) _ERR("f");
while (fgets(line, 1024, fr))
{
char* tmp = stmm(line);
if (tmp == NULL) _ERR("m");
for (i = 0, c = 1; i < strlen(tmp); i++) {
if (tmp[i] == 44) c++;
}
for (i = 0; i < c; i++) {
if (r == row && i+1 == col) {
fprintf(fw,"%s", NewValue);
} else {
free(tmp);
tmp = stmm(line);
if (tmp == NULL) _ERR("m");
fprintf(fw,"%s", getfield(tmp, i + 1));
}
(i < c - 1) ? fprintf(fw,",") : fprintf(fw,"\n");
}
free(tmp);
r++;
}
fclose(fr);
fclose(fw); }
edit_file(".\FileName.csv","NewValue",Row,Column);

C Programming - Difficulty with Parsing a Temp Folder

I'm having some trouble with parsing a temp.txt folder which contains the basenames of various files in a directory. I'd like to sort through this file, figure out what each of the file types are line by line, and then delete them from the temp file. I have been able to do carry out the parsing of the file names to discover their type, but when I call my delete function... it only works for some of the files and leaves behind some junk occasionally.
The input file looks like this:
temp.txt input
The output file after running looks like this:
temp.txt output
#include < stdio.h >
#include < string.h >
#include < unistd.h >
#include < stdlib.h >
/* This program module parses through the temp.txt file and finds .gif, .png, and .bmp files
and prints to standard output their file type in the order in which they're found.
It also takes all the files that aren't of those three types and puts them in a junk file.
*/
int deleteline(int delete_line);
int main(int argc, char * argv[]) {
FILE * file = fopen("temp.txt", "r"); /* should check the result */
FILE * myfile = fopen("junkfiles.txt", "w");
char line[4069];
char * gif = ".gif";
char * png = ".png";
char * bmp = ".bmp";
int i = 0;
int j = 0;
// if tempfile cannot be created, error handle
if (!file) {
puts("Some kind of file error!");
return 1;
}
// if junkfile cannot be created, error handle
if (!myfile) {
puts("Some kind of file error!");
return 1;
}
while (fgets(line, sizeof(line), file)) {
i++;
if (strstr(line, gif) != NULL) {
j = deleteline(i);
fflush(NULL);
if (j == 0) {
printf("File on line %d is: ", i);
puts(line);
printf("This is a .gif\n\n");
} else {
printf("Some error on line %d\n", i);
}
} else if (strstr(line, png) != NULL) {
j = deleteline(i);
if (j == 0) {
printf("File on line %d is: ", i);
puts(line);
printf("This is a .png\n\n");
} else {
printf("Some error on line %d\n", i);
}
} else if (strstr(line, bmp) != NULL) {
j = deleteline(i);
if (j == 0) {
printf("File on line %d is: ", i);
puts(line);
printf("This is a .bmp\n\n");
} else {
printf("Some error on line %d\n", i);
}
} else {
j = deleteline(i);
if (j == 0) {
printf("The file on line %d is junk.\n\n", i);
} else {
printf("Some error on line %d\n\n", i);
}
fprintf(myfile, "%s", line);
}
}
/* may check feof here to make a difference between eof and io failure -- network
timeout for instance */
fclose(file);
fclose(myfile);
fflush(NULL);
return 0;
}
int deleteline(int delete_line) {
FILE * fileptr1, * fileptr2;
char * filename = "temp.txt";
char ch;
int temp = 1;
//open file in read mode
fileptr1 = fopen(filename, "r");
ch = getc(fileptr1);
//rewind
rewind(fileptr1);
//open new file in write mode
fileptr2 = fopen("replica.txt", "w");
while (ch != EOF) {
ch = getc(fileptr1);
if (ch == '\n')
temp++;
//except the line to be deleted
if (temp != delete_line) {
//copy all lines in file replica.c
putc(ch, fileptr2);
}
}
fclose(fileptr1);
fclose(fileptr2);
remove(filename);
//rename the file replica.c to original name
rename("replica.txt", filename);
return 0;
}

unable to clear string during/after a while loop in c

I have a code that scans all the files in a directory for targeted words, and prints them out into a new file. The problem right now is after the while loop reads a file and stores a variable into the string (ex. customer), if the next file being read does not have the targeted word, it still displays the result stored in the string from the previous file. My goal is to make it display "N/A" if the current file does not have the target word.
I have tried a few ways to clear the string at the end or beginning of the while loop, but none of them work most of them just gives me a coredump error. Running out of ideas, any help would be much appreciated!
Code (shortened for easier reading):
int main(int argc, char** argv)
{
char directory[100];
char buff[100];
char delims[] = " :=";
char* result = NULL;
char* customer;
char* device;
char* buffer;
int i = 0;
DIR* FD;
struct dirent* in_file;
int c = 0;
printf("Enter directory:");
scanf("%s",directory);
FILE* ft = fopen("workorderlist.csv", "w"); /* Open file to write to*/
if (ft == NULL)
{
puts("Cannot open target file");
exit(1);
}
fprintf (ft, "Work Order,Customer,Device,Test_Prog,Software,DUT_board_id,Corl box\n");
/* Open Directory */
if (NULL == (FD = opendir(directory)))
{
puts("Cannot open directory");
return 1;
}
while ((in_file = readdir(FD)))
{
if (!strcmp (in_file->d_name, "."))
{
continue;
}
if (!strcmp (in_file->d_name, ".."))
{
continue;
}
/* Open files to read from */
buffer = (char*)malloc(100);
sprintf(buffer, "%s/%s", directory, in_file->d_name);
size_t len = strlen(buffer);
if (len >= 4 && memcmp(buffer + len - 4, ".wor", 4) == 0) /* checks if file ends with .wor */
{
FILE* fs = fopen(buffer, "r"); /* open file to read */
if (fs == NULL)
{
puts("Cannot open source file");
return 1;
}
/* Scanning each file for targeted words: */
while (fgets(buff, 100, fs) != NULL)
{
result = strtok( buff, delims );
while (result != NULL)
{
if ((strcmp(result, "Customer") == 0))
{
result = strtok(NULL,delims);
customer = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(customer, result);
for (i = 0; i < strlen(customer) + 1; i++)
{
if (customer[i] == '\n')
{
break;
}
}
customer[i] = ' ';
}
if (strcmp(result, "device") == 0)
{
result = strtok(NULL, delims);
device = (char*)malloc((strlen(result) + 1) * sizeof(char));
strcpy(device, result);
for (i = 0; i < strlen(device) + 1; i++)
{
if(device[i] == '\n')
{
break;
}
}
device[i] = ' ';
}
result = strtok(NULL,delims);
}
}
if (customer == '\0')
{
customer = "N/A";
}
if (device == '\0')
{
device = "N/A";
}
fprintf(ft, "%s,%s,%s,%s,%s,%s,%s\n",
in_file->d_name, customer, device, testprog,
software, dutboardid, corlbox);
printf(in_file->d_name);
printf("\n");
fclose (fs) ;
c++;
}
}
printf("Total Workorders Found: %d (Info saved to workorderlist.csv)\n", c);
fclose(ft);
return 0;
}
First at all, customer/device are strings. You should not be doing == for it comparison. You can, for example, compare the first char of the string: device[0] == '\0';
You should do string initialization before the loop starts.
You can achieve this by using strcpy with a known value or any other string manipulation function. The value that you use to initialize the string before the loop is the one you gonna test with strcmp or similar later.
Is like with ints or any other C data type, but you need manipulation functions instead.
By the way, haven't you posted your read file loop in a question here too?
Hope this helps.

Reading from a file based on space delimiter

I have text file that contains 4 lines of data. Each line is as follows
Candies 2
Cookies -4
Soda 5
Milk 8
I have to read the the values (2,-4,5,8) in a C function and store them in a variable.
I have written the following code and I'm stuck at the point where I've indicated by XXXXXX. I'm not sure what exactly goes in there and after that. Appreciate your help.
void function()
{
int count=0,value[4],length=0;
FILE *fp;
fp = fopen("file.txt","r");
if (fp == NULL)
{
fprintf(stderr, "Can't open file !\n");
exit(1);
}
char line[100];
for (count = 0; count < 4; count++)
{
if (fgets(line,sizeof(line),fp)==NULL)
break;
else
{
while(fp!="" && length<strlen(line))
{
fp++;length++
}
if(fp == "")
value[count]= XXXXXXXX;
}
}
Please don't increment fp. It does not do what you think it does.
If you want to do this with minimal help from existing C functions, simply search through your line for a space (okay, I used isspace and atoi here):
int pos = 0;
while( line[pos] != 0 && !isspace(line[pos]) ) pos++;
if( line[pos] != 0 ) {
line[pos++] = 0;
value[count] = atoi( &line[pos] );
printf( "Key: '%s' / Value: %d\n", line, value[count] );
}
Or you could use sscanf...
int nread = sscanf( line, "%*s %d", &value[count] );
Or:
char key[100];
int nread = sscanf( line, "%s %d", key, &value[count] );
void function()
{
int count=0,value[4],length=0;
FILE *fp;
fp = fopen("file.txt","r");
if (fp == NULL)
{
fprintf(stderr, "Can't open file !\n");
exit(1);
}
char line[100];
for (count = 0; count < 4; count++)
{
if (fgets(line,sizeof(line),fp)==NULL)
break;
else
{
sscanf(line,"%*s %d",&value[count]);
}
}
I have modify the code ,you can try it.

Resources