How to write CSV fields on a binary file? - c

I have the following code:
typedef struct RegDados{
char removido; // deve ser inicializado com '-'
int encadeamento; // deve ser inicializado com -1
int nroInscricao; // nao aceita valores repetidos nem nulos
double nota;
char data[10]; // checar tipo
char *cidade;
int sizecid;
char tagCampo4;
char *nomeEscola;
int sizesch;
char tagCampo5;
}RegDados;
char * strtokEvenEmpty(char * s, const char * seps){
static char * p = NULL;
if (s != NULL)
p = s;
else if (p == NULL)
return NULL;
else
s = p;
while (*p) {
if (strchr(seps, *p)) {
*p++ = 0;
return s;
}
p += 1;
}
return (*s) ? s : NULL;
}
const char * getfield(char* line, int num){
const char * tok;
for (tok = strtokEvenEmpty(line, ","); tok; tok = strtokEvenEmpty(NULL, ",\n")){
if (!--num)
return tok;
}
return NULL;
}
int main(){
FILE * stream = fopen("trabalho1.csv.csv", "r+");
FILE * ArqBin = fopen("test.bin","wb");
RegDados regdados[5000];
RegCab regcab;
int i = 0;
if(ArqBin == NULL) printf("Error");
if (stream != NULL) {
char line[1024];
while (fgets(line, 1024, stream)) {
regdados[i].nroInscricao = atoi(getfield(line, 1));
fwrite(&regdados[i].nroInscricao, sizeof(int), 1, ArqBin);
regdados[i].nota = atof(getfield(line, 2));
fwrite(&regdados[i].nota, sizeof(double), 1, ArqBin);
strcpy(regdados[i].data, getfield(line, 3));
fwrite(regdados[i].data, sizeof(char), 100, ArqBin);
regdados[i].cidade = getfield(line, 4);
fwrite(regdados[i].cidade, sizeof(char), 100, ArqBin);
regdados[i].nomeEscola = getfield(line, 5);
fwrite(regdados[i].nomeEscola, sizeof(char), 100, ArqBin);
i++;
}
fclose(stream);
fclose(ArqBin);
}
else{
printf("Error");
}
}
It already parses the fields of my file, but I can't write them on a binary file, because when I try to write, I get a lot of null fields, which doesn't happen when I don't write.
My CSV file looks like:
nroInscricao,nota,data,cidade,nomeEscola
13893,353.9,26/11/2016,,FRANCISCO RIBEIRO CARRIL
13595,472.2,,Salgueiro,ALFREDO GUEDES
13894,614.4,28/11/2016,Recife,JOAO DE MOURA GUIMARAES
13880,403.2,29/11/2016,Fortaleza,ANTONIO DIAS PASCHOAL PR
13881,373.7,,Sao Jose da Tapera,DONIZETTI TAVARES DE LIM
13882,394.8,01/12/2016,Sao Bernardo do Cam,JUSTINO GOMES DE CASTRO
How can I write the each field on a binary file?

when I try to write, I get a lot of null fields, which doesn't happen when I don't write.
this is normal, the internal representation of number can contains several 0, for instance doing :
fwrite(&regdados[i].nroInscricao, sizeof(int), 1, ArqBin);
if regdados[i].nroInscricao values 7 and your int are on 32bits that will write 3 times 0 and 1 time 7 (the order depends if you are on little/big endian).
Of course it is the same with the strings you write with a fixed size, so the padding characters can have any value including 0 (there are not initialized)
Your way to extract the fields with getfield is expensive because you extract the first token, then to get the second you have to bypass the first token, then to get the third you have to bypass the 2 first tokens etc.
A better way is to do getfield(line, 1) then getfield(NULL, 1) to get the second token, then to do getfield(NULL, 1) to get the third etc, so in fact the second argument is always 1 and you can remove its management
You try to open trabalho1.csv.csv, probably you want to open trabalho1.csv
In
if(ArqBin == NULL) printf("Error");
if (stream != NULL) {
it is not enough to print error, you must not continue, can be
if(ArqBin == NULL)
printf("Error");
else if (stream != NULL) {
or better replace
FILE * stream = fopen("trabalho1.csv.csv", "r+");
FILE * ArqBin = fopen("test.bin","wb");
...
if(ArqBin == NULL) printf("Error");
if (stream != NULL) {
...
}
else{
printf("Error");
}
by something like
FILE * stream = fopen("trabalho1.csv.csv", "r+");
if (stream == NULL) {
fprintf(stderr, "cannot open input file rabalho1.csv.csv");
return -1;
}
FILE * ArqBin = fopen("test.bin","wb");
if (ArqBin == NULL) {
fprintf(stderr, "cannot open output file test.bin");
fclose(stream); /* really useful if you do not stop execution */
return -1;
}
...

Related

Reading dynamic length comma separated values using fscanf

I have a txt file which contains patient details separated by commas
I want to read each value store that in a structure. But, the problem is that some of the entries contain 3 values and the others contain 4.
ENTRIES IN TXT FILE are:
1032,Pugsley Yanson,CELL,3048005191
1048,Banjo Codi,TBD,
1056,Lettuce Peas,WORK,7934346809
My Code looks like :
`struct Phone
{
char description[PHONE_DESC_LEN];
char number[PHONE_LEN];
};
// Data type: Patient
struct Patient
{
int patientNumber;
char name[NAME_LEN];
struct Phone phone;
};
void importPatients(const char* datafile, struct Patient patients[], int max){
FILE *fp = fopen(datafile, "r");
int i = 0;
int read = 0;
while (!feof(fp) && i < max){
read = fscanf(fp,"%d,%14[^,],%4[^,],%10[^,]\n",&patients[i].patientNumber,patients[i].name,patients[i].phone.description,patients[i].phone.number);
if(read == 0 && !feof(fp)){
fclose(fp);
return;
}
i++;
}
fclose(fp);
}
This code works perfectly when reading entries with 4 values but fails as soon as it encounters an entry with 3 values like: 1048,Banjo Codi,TBD,
How can this be fixed or is there a better approach to solve this problem?
At least these issues
Inconsistent ,
Sometimes a line of data ends with a final field, sometimes not.
1032,Pugsley Yanson,CELL,3048005191
1048,Banjo Codi,TBD,
Avoid line ending problems: read the line with fgets() and then parse.
Why is “while( !feof(file) )” always wrong?
Be sure char buffers are big enough
#define NAME_LEN (14 + 1)
#define PHONE_DESC_LEN ( 4 + 1)
#define PHONE_LEN (10 + 1)
Weak test
Do not test against 1 possible undesired return value. read could be other than 0 or 4. Test against desired return value.
// if(read == 0
if(read != 4
[Needs re-work, re-work done below]
Alternate:
char buf[100];
while (i < max && fgets(buf, sizeof buf, fp)){
int read = sscanf(buf,"%d , %14[^,], %4[^,], %10[^,]",
&patients[i].patientNumber, patients[i].name,
patients[i].phone.description, patients[i].phone.number);
if (read != 4) {
report_error();
} else {
i++;
}
}
fclose(fp);
[Update]
Untested sample code to better handle empty fields. Likely deserves more testing - later.
// Return patient count. -1 implies error
int importPatients(const char *datafile, struct Patient patients[], int max) {
FILE *fp = fopen(datafile, "r");
if (fp == NULL) {
return -1;
}
char buf[100];
int i = 0;
while (i < max && fgets(buf, sizeof buf, fp)) {
const char *token = strtok(buf, ',');
if (token == NULL) {
return -1;
}
patients[i].patientNumber = aoti(token); // Better code would use strtol()
token = strtok(buf, ',');
if (token == NULL) {
return -1;
}
snprintf(patients[i].name, sizeof patients[i].name, "%s", token); // TBD, check return value to buffer fit.
token = strtok(buf, ',');
if (token == NULL) {
return -1;
}
snprintf(patients[i].phone.description,
sizeof patients[i].phone.description, "%s", token);
token = strtok(buf, '\n');
if (token == NULL) {
return -1;
}
snprintf(patients[i].phone.number, sizeof patients[i].phone.number, "%s",
token);
i++;
}
fclose(fp);
return i;
}

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);

Copy content of img file to another img file

I am trying to make a function that opens 2 files- one for reading and the other one for writing and then copies the first file into the other one binary and for some reason its just does not work. I was trying to debug the program few times but i could not recognize the problem.
void myCopyBinary(char * * argv) {
FILE * srcFile;
FILE * dstFile;
int yesNo = 0;
char temp = ' ';
int i = 0;
int size = 0;
char * buffer;
int resultFread = 0;
int resultFwrite = 0;
srcFile = fopen(argv[1], "rb");
if (srcFile != NULL) {
dstFile = fopen(argv[2], "rb");
if (dstFile != NULL) {
printf("Would you like to overwrite it? (Every Number -YES, 0-NO): ");
scanf("%d", & yesNo);
if (yesNo == 0) {
fclose(dstFile);
exit(0);
return 1;
}
}
dstFile = fopen(argv[2], "wb");
if (dstFile != NULL) {
fseek(srcFile, 0, SEEK_END); // non-portable
size = ftell(srcFile);
buffer = (char * ) malloc(sizeof(char) * size);
if (buffer == NULL) {
printf("Error with the buffer!\n");
exit(1);
}
do {
resultFread = fread(buffer, 1, sizeof(buffer), srcFile);
resultFwrite = fwrite(buffer, 1, resultFread, dstFile);
} while (resultFread > 0);
}
}
One a side note, before reopening the dstFile for writing you should close the previous descriptor.
Anyways, you called fseek(srcFile, 0, SEEK_END);, however you didn't seek back to the beginning, thus when you attempt to read you hit EOF. I guess this is the root of your problem, if not(or rather after fixing this it still doesn't work) then please give more details how it does not work.

How to copy specific contents of a file onto another file in C?

I've been at this for hours now and I feel like I'm not getting some fundamental techniques in C. I've been trying to read in a specific line and store it into 2 separate files from one file. The source file (temp) is the one I'm reading in and the 2 files, data and text are the 2 files I'm trying to store.
TL;DR, how do I split up contents from a file into two separate file?
For example, the input file is:
.data
A: .word 32
B: .word 16
.text
main:
la $s0 A
lw $s0 0($s0)
la $s1 B
lw $s1 0($s1)
This function will take in the temp file and process the .data and .text segments into separate files.
void process_file(FILE * temp)
{
FILE * data, * text;
char * token;
char * nextLine;
char line[256];
char * str_token = malloc(256 * sizeof(char));
char * str = malloc(256 * sizeof(char));
int i;
data = fopen("data.asm", "w");
text = fopen("text.asm", "w");
if (temp == NULL || data == NULL || text == NULL)
{
printf("UNABLE TO OPEN OR LOCATE FILES. EXITING.\n");
exit(1);
}
while (!feof(temp))
{
if ((nextLine = fgets(line, 256, temp)) != NULL)
{
token = strtok(nextLine, "\n");
//printf("%s", nextLine);
if (token != NULL)
{
strcat(str_token, token);
token = strtok(NULL, "\n");
if ((strstr(str_token, ".data")) != NULL)
{
strcat(str, str_token);
fprintf(data, "%s", str); //ADDING THIS CAUSES A SEG FAULT :(
}
else if ((strstr(token, ".text")) != NULL)
{
fprintf(text, "%s", token);
token = strtok(NULL, "\n");
}
}
}
}
}
Thanks!
void process_file(FILE *temp){
FILE *data, *text, *fp;
char line[256], str[256];
char *token;
data = fopen("data.asm", "w");
text = fopen("text.asm", "w");
if (temp == NULL || data == NULL || text == NULL) {
printf("UNABLE TO OPEN OR LOCATE FILES. EXITING.\n");
exit(1);
}
fp = data;//default for not start with .data and .text
while(fgets(line, sizeof line, temp)){
strcpy(str, line);
if((token=strtok(str, " \t\n"))!=NULL){//not blank line
if(strcmp(token, ".data")==0)
fp = data;
else if(strcmp(token, ".text")==0)
fp = text;
}
fprintf(fp, "%s", line);
}
fclose(data);
fclose(text);
}
This is what I used to get your answer, using the code you provided anyway
void process_file(FILE * temp)
{
//You may use NULL versus 0
FILE * data = 0, * text = 0;
char * token = 0;
char * nextLine = 0;
char line[256];
int i = 0;
data = fopen("data.asm", "w");
text = fopen("text.asm", "w");
if ( 0 == temp || 0 == data || 0 == text )
{
printf("UNABLE TO OPEN OR LOCATE FILES. EXITING.\n");
exit(1);
}
while (!feof(temp))
{
if ((nextLine = fgets(line, 256, temp)) != NULL)
{
token = strtok(nextLine, "\n");
//printf("%s", nextLine);
if (0 != token){
if(strcmp(token, ".data") == 0 ){
//Make use of that i
i = 1;
}
else if(strcmp(token, ".text") == 0){
i = 2;
}
switch(i){
case 1:
fprintf(data, "%s\r\n", token);
break;
case 2:
fprintf(text, "%s\r\n", token);
break;
default:
printf("Invalid!");
break;
}
}
}
}
fclose(data);
fclose(text);
fclose(temp);
}
#include <string.h>
#include <stdio.h>
void process_file(FILE * temp)
{
FILE * data, * text, *write = NULL;
char * token;
char * nextLine;
char line[256];
char * str_token = malloc(256 * sizeof(char));
char * str = malloc(256 * sizeof(char));
char val [] = "safasfsdf";
int i;
data = fopen("data.asm", "w");
text = fopen("text.asm", "w");
if (temp == NULL || data == NULL || text == NULL)
{
printf("UNABLE TO OPEN OR LOCATE FILES. EXITING.\n");
exit(1);
}
while (!feof(temp))
{
if ((nextLine = fgets(line, 256, temp)) != NULL)
{
if ((strstr(line, ".data")) != NULL)
{
write = data;
}
else if ((strstr(line, ".text")) != NULL)
{
write = text;
}
if(line[0]!= '\n'){
printf ("%p %s",write, line);
fprintf(write, "%s", line);
}
}
}
fclose (data);
fclose (text);
}
pattern is identified first. Based on the pattern file pointer is selected. I have removed the few unwanted line from the original source and make it simple. Hope this will be helpful to you.

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.

Resources