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);
Related
So i have to write a function that reads a file (.txt) and searches for a specific string, and then make it return the whole line that the string was on.
For example, one line is composed by student number, name, date of birth, address and course. I need to search for a name, and i need it to return the whole line, containing number, name, etc.
Ex :
Input : Search for student name : HUGO
Output :
61892 HUGOABC 12-02-2001 Address123 ETD
81029 HUGOBCA 09-09-2000 Address123 EAC
Here's my code :
fp = fopen("database_Alunos.txt", "r+");
if (fp == NULL) {
printf("Error! File is NULL...");
system("pause");
main();
}
else {
char nome[50];
printf("Choose name to search : ");
scanf("%[^\n]s", &nome);
/* ??? */
fclose(fp);
system("pause");
main();
fp is a FILE *fp, declared on top of main();
I need to know what kind of functions I can use in the space in the code with the question marks to make it scan for 'nome' and return the whole line
You'll need to loop through a getline using your fp, then you can use for example strstr to check if the name is present in this string. Note that you'll have to handle the case when the name is present in the address.
char *line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, fp)) != -1) {
if (strstr(line, nome) != NULL) {
printf("%s\n", line);
}
}
Ok i figured it out, this is a function to read the line
int readline(char *buff, int len, FILE *fp) {
buff[0] = '\0';
buff[len - 1] = '\0';
char *tmp;
if (fgets(buff, len, fp) == NULL) {
*buff = '\0';
return 0;
}
else {
if ((tmp = strrchr(buff, '\n')) != NULL) {
*tmp = '\0';
}
}
return 1;
}
Func to read name and return lines :
scanf("%[^\n]s", fgets(nome, sizeof(nome), stdin));
char line[1024];
while (readline(line, 1024, fp)) {
char *aux = strdup(line);
char *numero = strtok(aux, "\t");
char *nomeAluno = strtok(NULL, "\t");
if (strstr(nomeAluno, nome) != NULL) {
printf("%s\n", line);
}
}
}
I have a csv file having values
1,A,X
2,B,Y
3,C,Z
I have to read the CSV file line by line and keep it in a Structure array.
The values are going fine each time in the for loop. But at the end when I am printing the Array, only the last value is being printed.
Somebody please tell me where am I doing the logical error?
struct proc
{
char *x;
char *y;
};
void main()
{
fflush(stdin);
fflush(stdout);
const char s[2] = ",";
char *token;
int rows=0,i,tokenVal=0,rowCount=0;
FILE *fpCount = fopen("data.csv","r");
if(fpCount != NULL)
{
char lineCount[20];
while(fgets(lineCount, sizeof lineCount, fpCount))
rows++;
}
struct proc *pi[rows];
for(i=0;i<rows;i++)
pi[i] = (struct proc*) malloc(sizeof(struct proc));
FILE *fp = fopen("data.csv", "r");
if(fp != NULL)
{
char line[20];
while(fgets(line, sizeof line, fp) != NULL)
{
printf("Start rowCount = %d\t",rowCount);
token = strtok(line, s);
while(token!=NULL)
{
if(tokenVal==0)
{
pi[rowCount]->Id =token;
}
if(tokenVal==1)
{
pi[rowCount]->act = token;
}
printf("\n");
tokenVal++;
token = strtok(NULL,s);
}
tokenVal = 0;
printf("end rowCount = %d\t",rowCount);
rowCount++;
}
fclose(fp);
} else {
perror("data.csv");
}
printf("total %d",rowCount);
int k=0;
for(k=0;k<rowCount;k++)
{
printf(" %d = %s----%s",k,pi[k]->Id,pi[k]->act);
}
}
Diagnosis
The fundamental problem you face is that you are saving pointers to the variable line in your structures, but each new line overwrites what was previously in line, so at the end, only data from the last line is present. It is fortuitous that your lines of data are all the same 'shape'; if the fields were of different lengths, you'd have more interesting, but equally erroneous, results.
Consequently, you need to save a copy of each field, not simply a pointer to the field. The simple way to do that is with POSIX function strdup(). If you don't have the function, you can create it:
char *strdup(const char *str)
{
size_t len = strlen(str) + 1;
char *rv = malloc(len);
if (rv != 0)
memmove(rv, str, len); // or memcpy
return rv;
}
Your code doesn't compile; your data structure has elements x and y but your code uses elements Id and act. You use a VLA of pointers to your struct proc, but it would be sensible to allocate an array of the structure, either as a VLA or via malloc() et al. You should check memory allocations — there isn't a way to check VLAs, though (one reason to use dynamic allocation instead). You could rewind the file instead of reopening it. (It's a good idea to use a variable to hold the file name, even if you only open it once; it makes error reporting better. Also, errors should stop the program, in general, though you did use perror() if the reopen operation failed — but not if the open failed.) You don't need two arrays into which to read the lines. It's a good idea to use far longer buffers for input lines. You should free dynamically allocated memory. Also, see What should main() return in C and C++?; the answer is int and not void (unless perhaps you are on Windows).
Here are three variants of your code, with various aspects of the issues outlined above more or less fixed.
VLA of pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int rows = 0, rowCount = 0;
FILE *fpCount = fopen(datafile, "r");
if (fpCount == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char lineCount[2000];
while (fgets(lineCount, sizeof(lineCount), fpCount))
rows++;
fclose(fpCount);
printf("Read %d rows from '%s'\n", rows, datafile);
struct proc *pi[rows];
for (int i = 0; i < rows; i++)
pi[i] = (struct proc *)malloc(sizeof(struct proc));
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to reopen '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount]->x = strdup(token);
}
else if (tokenVal == 1)
{
pi[rowCount]->y = strdup(token);
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k]->x, pi[k]->y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k]->x);
free(pi[k]->y);
free(pi[k]);
}
return 0;
}
VLA of structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int rows = 0, rowCount = 0;
FILE *fpCount = fopen(datafile, "r");
if (fpCount == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char lineCount[2000];
while (fgets(lineCount, sizeof(lineCount), fpCount))
rows++;
fclose(fpCount);
printf("Read %d rows from '%s'\n", rows, datafile);
struct proc pi[rows];
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to reopen '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount].x = strdup(token);
}
else if (tokenVal == 1)
{
pi[rowCount].y = strdup(token);
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k].x, pi[k].y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k].x);
free(pi[k].y);
}
return 0;
}
Dynamic array of structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int num_rows = 0, rowCount = 0;
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp))
num_rows++;
rewind(fp);
printf("Read %d rows from '%s'\n", num_rows, datafile);
struct proc *pi = calloc(num_rows, sizeof(*pi));
if (pi == 0)
{
fprintf(stderr, "Failed to allocate %zu bytes of memory\n", num_rows * sizeof(*pi));
exit(EXIT_FAILURE);
}
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount].x = strdup(token);
// null check
}
else if (tokenVal == 1)
{
pi[rowCount].y = strdup(token);
// null check
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k].x, pi[k].y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k].x);
free(pi[k].y);
}
free(pi);
return 0;
}
Given a data file:
1,A,X
2,B,Y
3,C,Z
3192-2146-9913,Abelone,Zoophyte
all three programs produce the same output:
Read 4 rows from 'data.csv'
Start rowCount = 0 [1][A][X] end rowCount = 0
Start rowCount = 1 [2][B][Y] end rowCount = 1
Start rowCount = 2 [3][C][Z] end rowCount = 2
Start rowCount = 3 [3192-2146-9913][Abelone][Zoophyte] end rowCount = 3
total 4
0 = [1]----[A]
1 = [2]----[B]
2 = [3]----[C]
3 = [3192-2146-9913]----[Abelone]
In the printf(" %d = %s----%s----%s",k,pi[k]->Id,pi[k]->act);
There are four data
%d
%s
%s
%s
but you set only three
k
pi[k]->Id
pi[k]->act
Struggling to move tokens to a 2D array .
The idea is that I am reading a file with multiple lines , get the number of lines and then based on that create a 2D array to use memory wisely(I dont want to create a 100 x 3 array for no reason).
I think I got the 2D array initialized in a separate funtion but when I try to enter data read from strtok() , I am getting error :
error: 'arr' undeclared (first use in this function)
strcpy(&arr[s2][c2],token);
Here is my code :
#include <stdio.h>
#include <string.h>
int ch, lines;
int no_of_lines(char* fp)
{
while(!feof(fp)) {
ch = fgetc(fp);
if(ch == '\n') {
lines++;
}
}
lines++;
return lines;
}
void declare_space_array(int size)
{
char* arr = (char*)malloc(size * 3 * sizeof(char));
return;
}
int main(void)
{
int c2 = 0;
int s2 = 0;
int len;
// char data[10][4];
static const char filename[] = "C:\\Users\\PC\\Documents\\Assignments\\stringops\\test.txt";
FILE* file = fopen(filename, "r");
no_of_lines(file);
printf("No of lines in file = %d", lines);
printf("\n");
// Closing file because it was read once till the end of file
fclose(file);
// Opening file again to read for parsing
file = fopen(filename, "r");
declare_space_array(lines);
char* token;
if(file != NULL) {
char line[128];
while(fgets(line, sizeof line, file) != NULL)
{
len = strlen(line);
printf("%d %s", len - 1, line);
const char s = ",";
token = strtok(line, ",");
while(token != NULL) {
strcpy(arr[s2][c2], token);
// printf( "%s\n", token );
token = strtok(NULL, ",");
c2++;
}
s2++;
}
fclose(file);
} else {
perror(filename); /* why didn't the file open? */
}
for(r1 = 0; r1 < lines; r1++) {
for(c1 = 0; c1 < 3; c1++) {
printf("%s", &arr[r1][c1]);
}
}
return 0;
}
file is something like this:
A1,B1,C1
A2,B2,C2
A3,B3,C3
EXPECTED OUTPUT TO SOMETHIGN LIKE THIS:
A1
B1
C1
A2
B2
C2
A3
B3
C3
After discussion in chat, etc, you could end up with code like this. This uses a global variable arr that's a pointer to an array of arrays of 3 char * values.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int lines = 0;
static char *(*arr)[3] = 0; // global definition.
static int no_of_lines(FILE *fp)
{
lines = 0;
int ch;
while ((ch = fgetc(fp)) != EOF)
{
if (ch == '\n')
lines++;
}
return ++lines; // Allow for last line possibly not having a newline
}
static void declare_space_array(int size)
{
arr = calloc(size, 3 * sizeof(char *)); // zeroed memory allocation
if (arr == 0)
{
fprintf(stderr, "Failed to allocate memory\n");
exit(1);
}
}
int main(void)
{
int c2 = 0;
int s2 = 0;
int len;
// char data[10][4];
// static const char filename[] = "C:\\Users\\PC\\Documents\\Assignments\\stringops\\test.txt";
const char *filename = "data";
FILE *file = fopen(filename, "r");
if (file == 0)
{
fprintf(stderr, "Failed to open file '%s' for reading\n", filename);
exit(1);
}
no_of_lines(file);
printf("No of lines in file = %d\n", lines);
rewind(file);
declare_space_array(lines);
const char delims[] = ",\n";
char line[128];
while (fgets(line, sizeof line, file) != NULL)
{
char *token;
c2 = 0;
len = strlen(line);
printf("%d [%.*s]\n", len - 1, len - 1, line);
token = strtok(line, delims);
while (token != NULL)
{
arr[s2][c2] = strdup(token); // copy token (from strtok) into newly allocated string.
token = strtok(NULL, delims);
c2++;
}
s2++;
}
fclose(file);
for (int r1 = 0; r1 < lines; r1++)
{
if (arr[r1][0] != 0)
{
for (int c1 = 0; c1 < 3; c1++)
printf(" %-10s", arr[r1][c1]);
putchar('\n');
}
}
return 0;
}
It doesn't release the memory that's allocated — I got lazy.
Sample data (note that the names are longer than 2 characters and are of variable length):
server1,Phoenix,Windows
server2,Dallas,Linux
server-99,London,z/OS
Sample output:
No of lines in file = 4
23 [server1,Phoenix,Windows]
20 [server2,Dallas,Linux]
21 [server-99,London,z/OS]
server1 Phoenix Windows
server2 Dallas Linux
server-99 London z/OS
The 'number of lines in file = 4' allows for the possibility that there isn't a newline at the end of the last line. The code in the printing loop allows for the possibility that there was a newline at the end and therefore the count is an over-estimate. It would spot a memory allocation from strdup() as long as the failure was on the first field of a line. It might crash if it was the second or third field that was not successfully copied.
I'm just getting into C and I figured this would be a good exercise. I've been putsing around with fgets trying to read from a file and I am just doing something wrong. I would like to enter the file name to read, enter the file name to output to, create that file, sort it (just a list of words), and then dump the sorted list into the created file. I know I should be doing something like:
char strIn[25];
printf("Enter a source filename: ");
fgets(strIn, 25, stdin);
printf("You entered: %s \n", strIn);
FILE *infile;
infile = fopen(strIn, "r");
if (infile == NULL){
printf("Unable to open file.");
}
char strOut[25];
printf("Enter a destination filename: ");
fgets(strOut, 25, stdin);
printf("You entered: %s \n", strOut);
FILE *outfile;
Any help is appreciated! Thank you
fgets puts the newline character \n at the end of your buffer. So you need to remove it.
int length = strlen(strIn);
if ( length > 0 && strIn[length-1] == '\n' )
strIn[length-1] = '\0';
You are on the right track. qsort will do what you want. The approach used here is not scalable; everything is held in memory and static allocation will make things HUGE very quickly, but it works as a toy example. At the moment it will break once there are more than 1000 lines in the input file.
# include <stdio.h>
# include <string.h>
#define MAXNAMELEN 100
#define MAXLINELEN 100
#define MAXITEMS 1000
int main(int argc, char ** argv) {
FILE * infile, * outfile;
// Statically allocated -- dastardly!
char name[MAXNAMELEN];
char line[MAXLINELEN];
char lines[MAXITEMS][MAXLINELEN];
int i, items = 0;
printf("Enter a source filename: ");
fgets(name, sizeof(name), stdin);
name[strlen(name)-1] = '\0'; // strip newline
// No error checking -- ANYWHERE -- dastardly!
infile = fopen(name, "r");
while (fgets(line, sizeof(line), infile)) {
strcpy(lines[items], line);
items++;
}
qsort(lines, items, MAXLINELEN, strcmp);
printf("Enter a destination filename: ");
fgets(name, sizeof(name), stdin);
name[strlen(name)-1] = '\0'; // strip newline
outfile = fopen(name, "w");
for (i=0; i<items; i++) {
fputs(lines[i], outfile);
}
fclose(infile);
fclose(outfile);
}
Now with dynamic allocation and error-checking (possibly an improvement on than the version above). Sorts /usr/share/dict/words (99171 lines) no problem at all. Still requires that the entire array be held in memory. See External Sorting for a way around that.
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
#define CHUNKLEN 100
#define INITITEMS 1000
/* Reads a string from stream into buffer until a newline or EOF.
buffer is dynamically allocated (and reallocated if necessary)
to ensure the string fits. Returns the number of characters put
into the buffer (zero if EOF and no characters read), or -1 on error. */
int unlimited_read(char ** buffer, FILE * stream) {
int bufl = CHUNKLEN;
int strl = 0;
char * chunk = (char *)malloc(CHUNKLEN);
if ( (*buffer = (char *) malloc(CHUNKLEN)) == NULL ) {
perror("memory error (malloc)");
return -1;
}
while (fgets(chunk, CHUNKLEN, stream) != NULL) {
strcpy(*buffer + strl, chunk);
strl += strlen(chunk);
if ( (strl == bufl - 1 ) && *(*buffer + strl - 1) != '\n') {
// lengthen buffer
bufl += CHUNKLEN - 1;
if ( (*buffer = realloc(*buffer, bufl)) == NULL ) {
perror("memory error (realloc)");
return -1;
}
} else {
// This shouldn't fail -- we're only ever making it smaller
*buffer = realloc(*buffer, strl);
return strl;
}
} // while
// If fgets returned NULL and we are not at EOF, it didn't work
return feof(stream) ? strl : -1;
}
/* Compare two strings given pointers to those strings.
Routine is courtesy of the qsort man page */
int cmpstringp(const void *p1, const void *p2) {
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
/* Sort lines in a file. File must end with a newline. */
int main(int argc, char ** argv) {
FILE * infile, * outfile;
char * inname, * outname, *tmpstr;
char ** lines;
int ret, tmp, nlines, i, items = 0;
if (argc != 3) {
printf("Usage: %s file_to_sort output_file\n", argv[0]);
exit(EXIT_FAILURE);
}
inname = argv[1];
outname = argv[2];
if ( (lines = malloc(INITITEMS * sizeof(char *))) == NULL) {
perror("memory error (malloc)");
exit(EXIT_FAILURE);
}
nlines = INITITEMS;
infile = fopen(inname, "r");
while ((ret = unlimited_read(&lines[items], infile)) > 0) {
items++;
if (items == nlines) {
nlines += INITITEMS;
lines = realloc(lines, (nlines * sizeof(char *)));
}
}
if (ret < 0) {
printf("WARNING: possibly truncated file\n");
}
tmpstr = lines[items - 1]; // Final line in file
tmp = strlen(tmpstr);
if (tmpstr[tmp - 1] != '\n') {
printf("Error: input file does not end with newline\n");
exit(EXIT_FAILURE);
}
qsort(lines, items, sizeof(char *), cmpstringp);
outfile = fopen(outname, "w");
for (i = 0; i < items; i++) {
fputs(lines[i], outfile);
free(lines[i]);
}
free(lines);
fclose(infile);
fclose(outfile);
}
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.