c Fatfs CSV writing bug - c

I have an issue with a CSV writing function, some times it prints a blank line to the file. How do I check for this or remove it as I dont want to write a blank line to the CSV.
My CSV output is:
As you can see the first 2 lines are correct the third is an error
After some time it recovers and the extra lines are removed.
Here is my Write function to the CSV:
void Write_block_to_sd_card()
{
while (fno.fname[0]);
ff_result = f_open(&file, FILE_NAME, FA_READ | FA_WRITE | FA_OPEN_APPEND);
if(ff_result != FR_OK)//Not passing if the file is missing
{
if (ff_result != FR_OK)
{
NRF_LOG_INFO("Unable to open or create file: " FILE_NAME ".");
SD_CARD_PRESENT = 0;
return;
}
}
else//File was openned fine
{
NRF_LOG_RAW_INFO("");
NRF_LOG_INFO("Writing to file as a block " FILE_NAME "...");
SD_CARD_PRESENT = 1;
//File is opened
for(int i = 0; i<MAXIMUM_BUFFER_STORAGE;i ++)
{
char buffer[50];//Buffer to hold output
char stT1 [MAX_BUFFER_SIZE];//T1
char stT2 [MAX_BUFFER_SIZE];//T2
char stT3 [MAX_BUFFER_SIZE];//T3
char stT4 [MAX_BUFFER_SIZE];//T4
char stPd [MAX_BUFFER_SIZE];//Pdiff
char stUT [MAX_BUFFER_SIZE];//Unix time
sprintf(stT1, "%d", SD_CARD_T1[i]);
sprintf(stT2, "%d", SD_CARD_T2[i]);
sprintf(stT3, "%d", SD_CARD_T3[i]);
sprintf(stT4, "%d", SD_CARD_T4[i]);
sprintf(stPd, "%f", SD_CARD_Pdiff[i]);
sprintf(stUT, "%d", SD_CARD_UNIX_TIME[i]);
//Do not write the ID to the SD card only chuck over the Unix time
snprintf(buffer, sizeof buffer, "%s,%s,%s,%s,%s,%s\r\n",stUT,stT1,stT2,stT3,stT4,stPd);//Convert to a string
if (strcmp(buffer, "") != 0)//Not null
{
if((SD_CARD_T1[i] != 0) && (SD_CARD_T2[i] != 0) && (SD_CARD_T3[i] != 0) && (SD_CARD_T4[i] != 0))//Null check
//Need to always write the correct length of data
ff_result = f_write(&file, buffer, sizeof(buffer), (UINT *) &bytes_written);//Add the input data to the CSV file
if (ff_result != FR_OK)
{
NRF_LOG_INFO("Write failed\r\n.");
}
else
{
}
}
ID = ID + 1;//Increment the ID counter
}
NRF_LOG_INFO("Write new entry %d bytes written.", (bytes_written*(MAXIMUM_BUFFER_STORAGE)));
}
(void) f_close(&file);
Time_stamp_id = ID;//Set the time stamp id to the latest value.
return;
}
I have a set of internal char arrays which store the data values to write to the CSV, I simply loop them and store to the SD card. But, I need to not create the blank lines.

Related

File for loop C

I'm trying to make a loop where my "soft" variable is taking the value of my other variable.
I'm trying to put each words of my file text (country-name, an espace, capital-of-country) in table.
But I can't find how to do it...
There's my program (partly) :
int main(){
FILE *Africa;
FILE *America;
FILE *Asia;
FILE *Europe;
FILE *Oceania;
int countryIndex = 0;
int capitalIndex;
char countryTable[15][30];
char capitalTable[15][30];
//FILE country[] = {Africa, America, Asia, Europe, Oceania};
Africa = fopen("Africa.txt", "r");
America = fopen("America.txt", "r");
Asia = fopen("Asia.txt", "r");
Europe = fopen("Europe.txt", "r");
Oceania = fopen("Oceania.txt", "r");
if (Africa == NULL || America == NULL || Asia == NULL || Europe == NULL || Oceania == NULL){
printf("Error, failed when trying to open the continent file.\n");
return 1;
}
for (FILE *country = {Africa, America, Asia, Europe, Oceania}){
while (fscanf(country, "%s", countryTable[countryIndex]) != EOF){
fscanf(country,"%s", capitalTable[countryIndex]);
countryIndex++;
}
for (capitalIndex = 0; capitalIndex < countryIndex; capitalIndex++){
printf("Country : %s - ", countryTable[capitalIndex]);
printf("Capital : %s\n", capitalTable[capitalIndex]);
}
}
/*while (fscanf(Africa, "%s", countryTable[countryIndex]) != EOF){
fscanf(Africa,"%s", capitalTable[countryIndex]);
countryIndex++;
}
for (capitalIndex = 0; capitalIndex < countryIndex; capitalIndex++){
printf("Country : %s - ", countryTable[capitalIndex]);
printf("Capital : %s\n", capitalTable[capitalIndex]);
}*/
fclose(Africa);
fclose(America);
fclose(Asia);
fclose(Europe);
fclose(Oceania);
return 0;
}
If you need additional information do not hesitate.
I found a solution, even if it's not the way I wanted to do it. By modifying my code like this :
int main(){
char *continentName[] = {"Africa.txt", "America.txt", "Asia.txt", "Europe.txt", "Oceania.txt"};
int nbrContinent = sizeof continentName / sizeof continentName[0];
int countryIndex = 0;
int capitalIndex;
char countryTable[15][30];
char capitalTable[15][30];
for (int i = 0; i < nbrContinent; i++){
FILE *continent = fopen(continentName[i], "r");
if (continent == NULL){
printf("Error, failed when trying to open the continent file.\n");
return 1;
}
while (fscanf(continent, "%s", countryTable[countryIndex]) != EOF){
fscanf(continent,"%s", capitalTable[countryIndex]);
countryIndex++;
}
fclose(continent);
}
for (capitalIndex = 0; capitalIndex < countryIndex; capitalIndex++){
printf("Country : %s - ", countryTable[capitalIndex]);
printf("Capital : %s\n", capitalTable[capitalIndex]);
}
return 0;
}
This way it's easier and I get around the problem haha.
By doing so, each capital name is associated with its country in the tables. e.g. tabContinent[1] -> tabCountry[1]
I just put the retrieval of each word in my files in the initial for loop so that I don't have to redo one. To do this, I changed the type of my "FILE" variables into an array of chars that directly contains the names of the files in question. Then you have to be careful to close the files at the end of the loop but inside (otherwise the function doesn't end correctly and can cause problems when calling another function). And finally, add a for loop outside the first one (otherwise the values of the arrays are displayed each time the files are read) in order to enter the data retrieved in the arrays.

C - Parse file and write to separate files based on the last field

The software intends to read from a csv styled file (it's delimited by space not by a comma) and split the initial file into two new ones. The two files are determined by the last field, which is a binary value. As it stands, it currently reads the file character by character. I want it to recognize the space, and only run the individual character check on the very last field. To my understanding, strtok() will come in handy, but I'm struggling to find a way to incorporate that into the existing software.
Any help would be greatly appreciated :)
/*
* C program to parse a file, and split it into two based on the final line of input
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Function declarations */
int isMale(signed char _line);
int isFemale(signed char _line);
int isMale(signed char _line)
{
}
int isFemale(signed char _line)
{
}
int main()
{
/* File pointer to hold reference to different files */
FILE * fPtrIn, // Input file
* fPtrMale, // Males of school age
* fPtrFemale, // Females of school age
* fPtrMisc; // Data not within the given parameters
// current_char is the current character being read
// success stores the read status
char current_char;
int success;
// Open all files to perform read/write.
fPtrIn = fopen("data/example.txt", "r");
fPtrMale = fopen("data/males.txt" , "w");
fPtrFemale = fopen("data/females.txt" , "w");
fPtrMisc = fopen("data/erroneus.txt", "w");
// fopen() return NULL if unable to open file in given mode.
if(fPtrIn == NULL || fPtrMale == NULL || fPtrFemale == NULL || fPtrMisc == NULL)
{
// Unable to open file, exit software
printf("Unable to open file.\n");
printf("Please check whether file exists and you have read/write privilege.\n");
exit(EXIT_FAILURE);
}
// File open success message
printf("File opened successfully. \n\n");
// Read an integer and store read status in success.
while (fscanf(fPtrIn, "%d", &current_char) != -1)
{
// Write each one to separate file
if (isMale(current_char))
fprintf(fPtMale, "%d\n", current_char);
else if (isFemale(current_char))
fprintf(fPtrFemale, "%d\n", current_char);
else
fprintf(fPtrMisc, "%d\n", current_char);
}
// Done with all files, hence close all.
fclose(fPtrIn);
fclose(fPtrMale);
fclose(fPtrFemale);
fclose(fPtrMisc);
printf("Data written to files successfully.");
return 0;
}
You should
Read lines via fgets()
Copy the line read because strtok() will modify original buffer.
parse fields via strtok()
Judge and output according to the parsed field.
// hoping that too long lines won't come
char line[102400], line_parse[102400];
// Read an integer and store read status in success.
while (fgets(line, sizeof(line), fPtrIn) != NULL)
{
char *last_field, *ret;
// Copy the line for parsing
strcpy(line_parse, line);
// Separate the line into tokens
last_field = ret = strtok(line_parse, " ");
while (ret != NULL)
{
last_field = ret;
ret = strtok(NULL, " ");
}
// Get the first character of the last field
if (last_field == NULL) current_char = '\0'; else current_char = last_field[0];
// Write each one to separate file
if (isMale(current_char))
fputs(line, fPtrMale);
else if (isFemale(current_char))
fputs(line, fPtrFemale);
else
fputs(line, fPtrMisc);
}

How to store values after reading a word in a file in c

I am trying to read a file in C and then store values after a certain word is read. For example, in my input.txt file, the following are the contents:
GREETINGS
Hello 13
Namaste 24
Hola 36
FLAVORS
Vanilla 23
Chocolate 78
I want to read past GREETINGS and then store Hello and its value 13 then Namaste and its value 24, etc. And then read past FLAVORS and store Vanilla and its value 23, etc. And so on...
I know how to open a file but not sure how to proceed from there.
void readInput() {
char input_file[100];
FILE *fp;
printf("Please enter the name of the file you would like to read: \n");
scanf("%s", input_file);
printf("You entered: %s\n", input_file);
fp = fopen(input_file, "r");
if (fp == NULL) {
printf("File does not exist.\n");
exit(1);
}
else
printf("This file exists!\n");
}
From your sample data it is not clear whether the lines with data have the always the same format. So I
suggest that you store in a variable the current mode (that means GREETINGS, FLAVOR, etc) and you call
a specific parsing function for every mode depending of the value of the current
mode.
I'd create this enum first
typedef enum {
MODE_GREETINGS = 0, // <-- important that this value is 0
MODE_FLAVORS,
...
MODE_INVALID
} Modes;
and a variable (this can be a global variable) with all modes:
const char *modes_lookup[] = {
"GREETINGS",
"FLAVORS",
...
};
Then I'd write a function that returns the mode:
Modes get_mode(const char *src)
{
if(src == NULL)
return MODE_INVALID;
for(size_t i = 0; i < sizeof modes_lookup / sizeof modes_lookup[0]; ++i)
{
if(strcmp(src, modes_lookup[i]) == 0)
return i;
}
return MODE_INVALID;
}
Now you can parse the file like this:
char line[1024];
Modes mode = MODE_INVALID;
size_t lineno = 0;
while(fgets(line, sizeof line, fp))
{
lineno++;
// removing newline
line[strcspn(line, "\n")] = 0;
Modes newmode = get_mode(line);
// check if line is a GREETINGS, FLAVOR, etc line
if(newmode != MODE_INVALID)
{
// sets the new mode and continue with the next line
mode = newmode;
continue;
}
// parsing depending on the current mode
if(mode == MODE_GREETINGS)
{
if(parse_greetings(line) == 0)
{
fprintf(stderr, "Cannot parse data on line %zu\n", lineno);
break;
}
continue;
}
if(mode == MODE_FLAVORS) {
if(parse_flavor(line) == 0)
{
fprintf(stderr, "Cannot parse data on line %zu\n", lineno);
break;
}
continue;
}
// more (if mode == MODE_xxx) cases as needed
if(mode == MODE_INVALID)
{
// mode == MODE_INVALID
fprintf(stderr, "File does not start with a mode line.\n");
break;
}
}
Then you've got to write the parse_greetings, parse_flavor, parse_xxx functions. Like I said
before, it's not clear from your sample whether the format will be always the
same, so it depends on the format whether you use strtok or sscanf. Also
your description of how and where you want to store the values is very vague, so
I don't know what would be the best for your.
The easiest would be sscanf and if the data line gets more complex, you can then
use strtok.
For example with sscanf
int parse_greetings(const char *line)
{
if(line == NULL)
return 0;
char name[20];
int val;
if(sscanf(line, "%19s %d", name, val) != 2)
return 0;
// do whatever you need with 'name' and 'val'
return 1;
}
And with strtok:
int parse_greetings(char *line)
{
if(line == NULL)
return 0;
char name[20];
int val;
const char *delim = " \t\r\n";
char *token = strtok(line, NULL);
// line has only delimiters
if(token == NULL)
return 0;
strncpy(name, token, sizeof name / sizeof name[0]);
name[(sizeof name / sizeof name[0]) - 1] = 0;
token = strtok(NULL, delim);
if(token == NULL)
return 0;
char *end;
val = strtol(token, &end, 0);
// check if token is a number
if(*end != '\0')
return 0;
// do whatever you need with 'name' and 'val'
return 1;
}
I'm not sure what exact criteria you're using to choose which lines to skip, but the next step for reading lines is to use fscanf or fgets. This tutorial should help you get started with reading files in C.
Edit, to make this more relevant after your comment:
fgets may be a better choice. You'll need to read each line, then loop through each character, to check if they are all uppercase.
If they are not uppercase, you can use fscanf, using the format %s %d, to scan the data line.

how to read specific lines of a text document and write them to another text | C

I have created a function that takes as a parameter the name of a source file, the name of a destination file and the beginning and end lines of the source file lines that will be copied to the destination file, like the example below. All I want to do is to input the lines that I want to copy to the other text file like the example below:
The code I show you just "reads" the content of the one text file and "writes" another one. I want to "write" specific lines that the user gives, not the whole text file
Inputs by the user:
Source_file.txt //the file that the destination file will read from
destination_file.txt //the new file that the program has written
2 3 // the lines that it will print to the destination file: 2-3
Source_file.txt:
1
2
3
4
5
6
destination_file.txt
2
3
code:
#include <stdio.h>
#include <stdlib.h>
void cp(char source_file[], char destination_file[], int lines_copy) {
char ch;
FILE *source, *destination;
source = fopen(source_file, "r");
if (source == NULL) {
printf("File name not found, make sure the source file exists and is ending at .txt\n");
exit(EXIT_FAILURE);
}
destination = fopen(destination_file, "w");
if (destination == NULL) {
fclose(source);
printf("Press any key to exit...\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(source)) != EOF)
fputc(ch, destination);
printf("Copied lines %d from %s to %s \n",
lines_copy, source_file, destination_file, ".txt");
fclose(source);
fclose(destination);
}
int main() {
char s[20];
char d[20];
int lines;
printf("-Enter the name of the source file ending in .txt\n"
"-Enter the name of the destination file ending in .txt\n"
"-Enter the number of lines you want to copy\n\n");
printf(">subcopy.o ");
gets(s);
printf("destination file-> ");
gets(d);
printf("Lines: ");
scanf("%d", &lines);
cp(s, d, lines);
return 0;
}
In cp(), in order to select the lines to keep, you have to know their position in the input-file. Thus, you need to count lines.
Using fgets instead of fgetc will allow you to count the lines.
On the other hand, if I wanted to select lines 3 and 7 to 12 in a file, I'd use:
sed -n -e "3p;7,12p" < input.txt > output.txt
this is a very simple solution, let's say you know that the maximun length of a line will be 100 characters for simplicity (if a line is longer than 100 characters only the first 100 will be taken)
at the top (outside main) you can write
#ifndef MAX_LINE_SIZE
#define MAX_LINE_SIZE 100
#endif
i know many people don't like this but i think in this case it makes the code more elegant and easier to change if you need to modify the maximum line size.
to print only the wanted lines you can do something like this
char line[MAX_LINE_SIZE];
int count = 0;
while (fgets(line, MAX_LINE_SIZE, source)){
count++;
if (3 <= count && count <= 5){
fputs(line, destination);
}
}
The while loop will end when EOF is reched because fgets returns NULL.
P.S. there could be some slight errors here and there since i wrote it pretty fast and going by memory but in general it should work.
There are some problems in your program:
Do not use gets(), it may cause buffer overflows.
Always use type int to store the return value of fgetc() in order to distinguish EOF from regular byte values.
You pass an extra argument ".txt" to printf(). It will be ignored but should be removed nonetheless.
To copy a range of lines from source to destination, you can just modify your function this way:
#include <stdio.h>
#include <string.h>
#include <errno.h>
void cp(char source_file[], char destination_file[], int start_line, int end_line) {
int ch;
int line = 1, lines_copied;
FILE *source, *destination;
source = fopen(source_file, "r");
if (source == NULL) {
printf("Cannot open input file %s: %s\n",
source_file, strerror(errno));
exit(EXIT_FAILURE);
}
destination = fopen(destination_file, "w");
if (destination == NULL) {
printf("Cannot open output file %s: %s\n",
destination_file, strerror(errno));
fclose(source);
exit(EXIT_FAILURE);
}
while ((ch = fgetc(source)) != EOF) {
if (line >= start_line && line <= end_line) {
fputc(ch, destination);
}
if (ch == '\n') {
line++;
}
}
lines_copied = 0;
if (line > start_line) {
if (line >= end_line) {
lines_copied = end_line - start_line + 1;
} else {
lines_copied = line - start_line + 1;
}
}
printf("Copied lines %d from %s to %s\n",
lines_copy, source_file, destination_file);
fclose(source);
fclose(destination);
}
int main() {
char source_file[80];
char destination_file[80];
int start_line, end_line;
printf("-Enter the name of the source file ending in .txt\n"
"-Enter the name of the destination file ending in .txt\n"
"-Enter the start and end line\n\n");
printf(">subcopy.o ");
if (scanf("%79s", source_file) != 1) {
return 1;
}
printf("destination file-> ");
if (scanf("%79s", destination_file) != 1) {
return 1;
}
printf("Start and end lines: ");
if (scanf("%d %d", &start_line, &end_line) != 2) {
return 1;
}
cp(source_file, destination_file, start_line, end_line);
return 0;
}

Trying to read text file into array without repeats in C

This is for a beginner's C programming unit. I'm trying to read a text file containing MAC addresses and the data they received, separate out the relevant data (address and number of packets), copy the addresses to an array without repeating any of them and sum the associated number of packets if an identical address is encountered.
I can read the file in just fine, and get the bits of each line I want without issue, but when I try to check each address read against those already in the array I hit a problem. Depending on the location of the integer counting the number of full lines, the program either fails to recognise identical strings and prints them all as they are in the file, or prints them over one another in addresses[0], leaving me with only the last address. I'm stumped and need some fresh eyes on this - any suggestions would be greatly appreciated.
My code follows:
static void readadds(char filename[])
{
FILE* packetfile = fopen(filename, "r");
FILE* datafile = fopen("packdata.txt", "w+");
// Open file from input; create temporary file to store sorted data.
char line[100];
char addresses[500][18];
int datasize[500];
int addressno = 0;
// Create storage for lines read from text file, addresses and related data.
if(packetfile != NULL)
{
while(fgets(line, sizeof line, packetfile) != NULL)
{
int linenum = 0;
char thisadd[18];
int thisdata;
//Create arrays to temp store data from each line
sscanf(line, "%*s %*s %s %i", thisadd, &thisdata);
for(int i = 0; i < 500; i++)
{
if(strcmp(thisadd, addresses[i]) == 0)
{ //check if the address is already in the array
int x = datasize[i];
datasize[i] = x + thisdata; //sum packet data if address already exists
printf("Match!\n");
break;
}
else
{
strcpy(addresses[linenum], thisadd); //initialize new address
datasize[linenum] = thisdata; //initialize assoc. data
linenum++;
addressno++;
printf("Started!\n");
break;
}
}
}
for(int i = 0; i <= addressno; i++)
{
printf("%s %i\n", addresses[i], datasize[i]);
fprintf(datafile,"%s %i\n", addresses[i], datasize[i]);
}
}
fclose(packetfile);
fclose(datafile);
}
This version prints over addresses[0]. If linenum is replaced by addressno in the for() loop, identical strings are not recognised. My dataset is arranged like this:
1378251369.691375 84:1b:5e:a8:bf:7f 68:94:23:4b:e8:35 100
1378251374.195670 00:8e:f2:c0:13:cc 00:11:d9:20:aa:4e 397
1378251374.205047 00:8e:f2:c0:13:cc 00:11:d9:20:aa:4e 397
1378251374.551604 00:8e:f2:c0:13:cc 00:11:d9:20:aa:4e 157
1378251375.551618 84:1b:5e:a8:bf:7c cc:3a:61:df:4b:61 37
1378251375.552697 84:1b:5e:a8:bf:7c cc:3a:61:df:4b:61 37
1378251375.553957 84:1b:5e:a8:bf:7c cc:3a:61:df:4b:61 37
1378251375.555332 84:1b:5e:a8:bf:7c cc:3a:61:df:4b:61 37
I'm almost certain this is what you're trying to do. The logic to add a new entry was incorrect. You only add one if you have exhausted searching all the current ones, which means you need to finish the current for-search before the add.
Note: Not tested for compilation, but hopefully you get the idea.
static void readadds(char filename[])
{
// Open file from input; create temporary file to store sorted data.
FILE* packetfile = fopen(filename, "r");
FILE* datafile = fopen("packdata.txt", "w+");
// Create storage for lines read from text file, addresses and related data.
char addresses[500][18];
int datasize[500];
int addressno = 0;
if (packetfile != NULL)
{
char line[100];
while(fgets(line, sizeof line, packetfile) != NULL)
{
char thisadd[18];
int thisdata = 0;
//Create arrays to temp store data from each line
if (sscanf(line, "%*s %*s %s %i", thisadd, &thisdata) == 2)
{
// try to find matching address
for(int i = 0; i < addressno; i++)
{
if(strcmp(thisadd, addresses[i]) == 0)
{
//check if the address is already in the array
datasize[i] += thisdata;;
printf("Match!\n");
break;
}
}
// reaching addressno means no match. so add it.
if (i == addressno)
{
printf("Started!\n");
strcpy(addresses[addressno], thisadd); //initialize new address
datasize[addressno++] = thisdata; //initialize assoc. data
}
}
else
{ // failed to parse input parameters.
break;
}
}
for(int i = 0; i <= addressno; i++)
{
printf("%s %i\n", addresses[i], datasize[i]);
fprintf(datafile,"%s %i\n", addresses[i], datasize[i]);
}
}
fclose(packetfile);
fclose(datafile);
}

Resources