C - read and store data file for further calculations - c

I normally use R, and have a lot of trouble understanding C. I need to read and store a data file, shown below, so that I can perform calculations on the data. These calculations depend on user-entered infomation. Here's the data that I'm trying to read in (called "Downloads/exchange.dat" in my code),
dollar 1.00
yen 0.0078
franc 0.20
mark 0.68
pound 1.96
Here's where I'm at so far. This reads the first line of data only and also returns it, which is not what I want. I need to read this entire file, store the exchange rates with their respective currencies, and be able to perform calculations on them later.
I think I need a typedef, perhaps? Later in the program, I ask the user for information like "Convert from?" and "convert to?". In which case, they'll enter "mark", "yen", "dollar", etc. and I hope to match their response to the respective exchange rate (using the string.h library).
My code so far, to read in the data:
#include <stdio.h>
main(int argc, char *argv[])
{
FILE *fpt; // define a pointer to pre-defined structure type FILE
char c;
// open the data file for reading only
if ((fpt = fopen("Downloads/exchange.dat", "r")) == NULL)
printf("\nERROR - Cannot open the designated file\n");
else // read and display each character from the data file
do
putchar(c = getc(fpt));
while (c != '\n');
// close the data file
fclose(fpt);
}
I feel like I need something similar, but completely different to read and store entire data file. Your help is appreciated.

You'll need to create a data structure to hold them and then a vector of this struct which will hold each currency read. You should make use of the fscanf function that will not only save you from splitting values by hand but will also convert them for you. Here is something I came up with:
/* to store each currency you read */
struct currency {
char name[256];
double value;
};
int main(int argc, char ** argv) {
FILE * fp = fopen("x.dat", "r");
int count = 0, i;
struct currency currencies[30];
/* this will read at most 30 currencies, and stop in case a
* end of file is reached */
while (count < 30 && !feof(fp)) {
/* fscanf reads from fp and returns the amount of conversions it made */
i = fscanf(fp, "%s %lf\n", currencies[count].name, &currencies[count].value);
/* we expect 2 conversions to happen, if anything differs
* this possibly means end of file. */
if (i == 2) {
/* for the fun, print the values */
printf("got %s %lf\n", currencies[count].name, currencies[count].value);
count++;
}
}
return 0;
}
To read them again, you'll need to iterate on the currencies array until you reach count iterations.
Since you already wants to match those values with the strcmp function, read the currency name, iterate on the array until you find a match and then perform calculations on those.
This is basic C knowledge and as much as I understand you're not used to using it, I strongly suggest you read a book to find these answers in them.

An example for writing:
FILE *fp;
fp=fopen("c:\\test.bin", "wb");
char x[10]="ABCDEFGHIJ";
fwrite(x, sizeof(x[0]), sizeof(x)/sizeof(x[0]), fp);
(write declaration)
size_t fwrite(const void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
If you were to read,
size_t fread(void *ptr, size_t size_of_elements, size_t number_of_elements, FILE *a_file);

I suggest you
define a struct like :
typedef struct{
char currency[10];
double rate;
}rate_currency;
getline:
in your main function you use getline to read the file line by line :
while ((read = getline(&line, &len, fpt)) != -1) ...
Separate:
use strchr to search for the space character to separate currency name from currency rate
Insert :
declare an array of your previous struct :
rate_currency arrOfStruct[10];
then, insert one by one
for example :
arrOfStruct[0].currency = "dollar"; //after you read it and separated it...
arrOfStruct[0].rate = 1.00;
You're Done!

Related

How to create 2D array from text file and print to new file in c

Wondering if I could get some advice. Firstly, I am very new to programming, so I apologise for any silly mistakes. Please feel free to point them out and I will try to go research to improve.
I feel I am doing something fundamentally wrong with my array.
I am trying to read in from a file whose filename is specified by user input, store the information from the file in a 2D array, which I then plan to print into another file, again defined by user input.
I am currently printing out the array, to check that it has been stored, but I believe I am using the 2D array incorrectly, as when I try to fprintf into my file, it just does not work.
Any advice would be greatly appreciated.
Thank you. Code as follows:
#include <stdio.h>
#include <string.h>
int main()
{
char finame[100];
printf("Enter file you would like to open: ");
scanf("%s", finame);
FILE *in = fopen(finame, "r");
char foname[100];
printf("Enter the filename you would like the output included in: ");
scanf("%s", foname);
FILE *out = fopen(foname, "w");
/*Char array to store string */
char str[50][20];
int i =0;
/*Loop for reading the file till end*/
while((fgets(str[i],sizeof(str[i]), in)) != NULL) {
fputs(str[i++],stdout);
//getchar();
}
return 0;
}
Avoid mixing fgets(), scanf() calls. scanf() leaves a newline character in the input buffer which is later consumed by fgets() (doesn't
matter in this case since input comes from a file not from stdin but a good practice overall).
There is also no protection for overflow, if you want to stick to scanf() add a width specifier and check the result to see if it succeeded.
if (scanf("%99s", finame) != 1) /* save one byte for \0 */
{
/* handle error case */
}
Check that you don't exceed the size of your array while writing to it.
Added 2 define directives that could clean your code up.
#define MAX_LINES 50
#define MAX_CHAR 20
char str[MAX_LINES][MAX_CHAR];
int i = 0;
while (i < MAX_LINES && (fgets(str[i], MAX_CHAR, in)) != NULL) /* always check if running out bounds */
{
fputs(str[i++], stdout);
}
The problem with the above code is that, if the file gets too big, you will end up missing data, what you could do is have a dynamic array and use a malloc / realloc approach to expand the array or a linked list.

Convert buffer of ASCII Decimals to a string or array of chars in C

I'm trying to open and read a CSV file that holds data that is needed to set capabilities. I need to set the capabilities based off what capabilities are read in from the CSV file. I open the file and read the data into a buffer. That's where I am stuck. I now want to use that data in the buffer to make string or character comparison to enter if else statements. For example my csv file looks like this:
1000, CAP_SETPCAP, CAP_NET_RAW, CAP_SYS_ADMIN
The first number is the euid and the rest are the capabilities that I want to set for a process. When I read it into a buffer the buffer holds ASCII decimals. I want to be able to convert the buffer into a string or array so I can make comparisons.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]){
FILE *fp;
fp = fopen("csvtest.csv", "r");
char buff[BUFFER_SIZE];
char *newCaps[] = {};
if(!fp){
printf("did not open\n");
}
fgets(buff, 1024, fp);
int i = 0;
//I don't know how to just get the size of what was put into the buffer
while(i < size_of_buffer){
//these are the comparisons I'd like to make, I know this isn't right
if(buffer[1000] == "1000"){
printf("This is the correct euid\n");
newCaps[0] = buffer[CAP_SETPCAP];
newCaps[1] = buffer[CAP_NET_RAW];
newCaps[2] = buffer[CAP_SYS_ADMIN];
}
i++;
}
Something along these lines.
You may want to take a look at the strtok() function. It separates a string in different tokens according to a delimiter you set to it, and returns a pointer to that string, which you may want to compare to the other string.
It is a good tool for you to separate and handle standarized delimited information.

Using fgets for file read

I'm new to using strings in C and am needing to read from a file lines of data that contain strings and numbers, parsing them as I go along. I've done similar programs reading in just numbers, such as a list of ordered pairs, using a for loop so this is the strategy I am leaning towards.
Example of data line in the file: PART,2.000,-1,0.050,V
When I compile I get an error in the for loop declaration of "expected expression before 'char'". What is missing or needs reviewing in this code?
#define flush fflush(stdin)
#define N 50
int main()
{
flush;
const char part[] = "PART"; // String for PART variable
char descriptor[N]; // Starting string of data set
double p_dim[N]; // Array for part dimensions
int t_sens[N]; // Array for sensitivity values: -1 or +1
double t[N]; // Array for part tolerance, bilateral
char t_status[N]; // Array for tolerance status, F(ixed) or V(ariable)
double key_max; // Maximum value of key characteristic
double key_min; // Minimum value of key characteristic
FILE* fpin;
if((fpin = fopen("input.txt","r"))==(FILE*)NULL)
{
printf("File input does not exist\n"); exit(-1);
}
// For loop to parse data lines from file
for(N; char* fgets(descriptor, int N, FILE* fpin); N-1);
{
compare(descriptor, part);
if (descriptor == part)
{
fscanf(fpin, "%lf,%d,%lf,%s", p_dim[N], t_sens[N], t[N], t_status[N]);
}
else if (descriptor != part)
{
fscanf(fpin, "%lf, %lf", &key_min, &key_max);
}
}
1.) #define flush fflush(stdin)
Flushing stdin invokes undefined behaviour.
2.) if((fpin = fopen("input.txt","r"))==(FILE*)NULL)
The cast to (FILE*) is superfluous.
3.) for(N; ... ; N-1);
You defined N as a constant (#define N 50) so this loop won't ever exit.
4.) for(... ; char* fgets(descriptor, int N, FILE* fpin); ...);
This is just plain wrong ...
I'd lean more toward breaking the string apart
See question 3501338 for reading a file line by line
See question 15472299 using strtok to break apart the string
If you need to cast the strings as numbers use sscanf

How to read a char array stored in a file, into a char buffer during runtime

I'm working in C and I'm modifying existing code.
I have a char array which is stored in a file as follows:
"\x01\x02\x03"
"\x04\x05\x06"
"\x07\x08\x09"
In the original source code this char array is included as follows:
const static char chs[] =
#include "file.h"
;
I'm modifying this code to load the file into a char array during runtime (to get the exact same result as with the above approach) instead of it to be included by the pre-processor. My first approach was to simply read the file into a char buffer, as follows:
FILE *fp;
const char *filename = "file.h";
fp = fopen (filename, "rb");
assert(fp != NULL);
fseek(fp, 0L, SEEK_END);
long int size = ftell(fp);
rewind(fp);
// read entire file into the buffer
char *buffer = (char*)malloc(sizeof(char) * size);
size_t nrOfBytesRead = fread(buffer, 1, size, fp);
However I've quickly discovered that this is not correct. The file already contains the exact code representation of the char array, I cannot simply read it into a char buffer and get the same result as the include approach.
What is the best way to get my char array which is stored in file, into a char array during runtime?
As you've seen, when you read the file using fread it reads it byte for byte. It doesn't get any of the syntactic processing that the compiler does on your source files. It doesn't know that strings live inside of quotes. It doesn't map escape sequences like \x01 into single bytes.
You have several different possibilities for fixing this:
Teach your program how to do that processing as it reads the file. This would be a fair amount of work.
Put just the bytes you want into the file.
Pick a different encoding for your file.
To say a little more about #2: If you don't want to change your file-reading code, what you can do is to create an (in this case) 9-byte file containing just the nine bytes you want. Since your nine bytes are not text, it'll end up being a "binary" file, which you won't be able to straightforwardly edit with an ordinary text editor, etc. (In fact, depending on the tools you have available to you, it might be challenging just to create this particular 9-byte file.)
So if you can't use #1 or #2, you might want to go with #3: pick a brand-new way to encode the data in the file, easier to parse than #1, but easier to prepare than #2. My first thought would be to have the file be hexadecimal. That is, the file would contain
010203040506070809
or
010203
040506
070809
Your file-reading code, instead of the single call to fread, would read two characters at a time and assemble them into bytes for your array. (I'd sketch this out for you, but the compilation I was waiting for has finished, and I ought to get back to my job.)
This should read the hex values from the file and save them to buffer.
fgets() reads each line from the file.
sscanf() reads each hex value from the line.
The format string for sscanf, "\\x%x%n", scans the backslash, an x, the hex value and stores the number of characters processed by the scan. The number of characters processed is used to advance through the line. This is needed if some lines have a different number of hex values.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char line[100] = {'\0'};
unsigned char *buffer = NULL;
unsigned char *temp = NULL;
unsigned int hex = 0;
int size = 0;
int offset = 0;
int used = 0;
int bufferused = 0;
int increment = 100;
int each = 0;
FILE *pf = NULL;
if ( ( pf = fopen ( "file.h", "r")) != NULL) {
while ( fgets ( line, sizeof ( line), pf)) {//get each line of the file
offset = 1;//to skip leading quote
//sscanf each hex value in the line
while ( ( sscanf ( line + offset, "\\x%x%n", &hex, &used)) == 1) {
offset += used;// to advance through the line
if ( bufferused >= size) {
temp = realloc ( buffer, size + increment);
if ( temp == NULL) {
//one way to handle the failure
printf ( "realloc failed\n");
free ( buffer);
exit (1);
}
buffer = temp;
size += increment;
}
buffer[bufferused] = hex;
bufferused++;
}
}
fclose ( pf);
}
for ( each = 0; each < bufferused; each++) {
printf ( "%x\n", buffer[each]);
}
free ( buffer);
return 0;
}

Random exclamation marks in stream from malloc() but goes away if the line is removed? Am I corrupting heap?

I don't work with C often so please excuse any mistakes I might be making in terms of coding style :P I'm currently getting an error that I'm a bit stumped on: when I include the line tokenCopy = malloc(sizeof(fileSize));, I get random a random exclamation about 1/4th of the way through the output of a file to std but if the line is removed/commented, the data displays as expected:
MAY +1.32 D1 1002
JUNE -1.57 D3 201
JULY -2.37 D4 478
AUGUST +5.03 D2 930
SEPTEMBER -3.00 D1 370
OCTOBER +7.69 D1 112
and the actual output I get when the line is in place:
MAY +1.32 D1 1002
JUNE -1.57 D3 2!
and the relevant code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/**
* Machine struct - three column
**/
/**
* Parses the input file, size is the size of name
**/
char parseInputFile(char *name, int size) {
FILE *fp;
if ((fp = fopen(name,"r")) == NULL) {
printf("Cannot open file %s\n", name);
return 1;
}
else {
int fileSize;
fileSize = 0;
char *fileContent;
char *processedFileContent;
//get file size
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
//allocate
fileContent = malloc(sizeof(fileSize));
processedFileContent = malloc(sizeof(fileSize));
//read
char c;
int g;
g=0;
while((c = getc(fp)) != EOF) {
fileContent[g] = c;
g++;
}
//process
char delim[6] = " ";
char *tokenCopy;
tokenCopy = malloc(sizeof(fileSize));
strcpy(tokenCopy, fileContent);
char *tokens = strtok(tokenCopy, delim);
while (tokens) {
tokens = strtok(NULL, delim);
}
puts(fileContent);
//printf("File Size: %i \n",fileSize);
//puts(tokenCopy);
return *processedFileContent;
}
}
int main(int argc, char *argv[])
{
//char *input;
if (argc == 1)
puts("You must enter a filename");
else {
int size = sizeof(argv[1]);
parseInputFile(argv[1],size);
}
return 0;
}
Could anyone offer any insight into what I'm doing wrong (or if my code is causing problems in itself)?
You put the size of the file in fileSize but then allocate only the space to store an int that is what sizeof(FileSize) will give you.
These two lines
fileContent = malloc(sizeof(fileSize));
processedFileContent = malloc(sizeof(fileSize));
should be (assuming you will treat the text you'll read as a string):
fileContent = malloc(fileSize+1);
processedFileContent = malloc(fileSize+1)
and, after having read the file content, you should put a '\0' at the end.
That said, I really don't get what you are trying to achieve by using strtok(). If you only need to separate the three components of each line, you can do it much easily while you read the file since you read it one character at the time.
If you elaborate a little bit more on what you're trying to achieve, we might have other advice.
UPDATE AFTER COMMENT BELOW
You should step back a second and reconsider your problem as I suspect you don't need to store any string at all. The first value is a month name, which can be stored as an integer, the second is a double (or float), the third seems 'Dx' with x varying from 1 to 4, again this could be an integer. It seems the name of a sensor, so I suspect it could be coded in an integer anyway as there will surely be a finite number of them. And the fourth is clearly another integer.
With a wild guess on what those fields mean, your struct would look like something like this:
struct val {
int month;
double value;
int sensor;
int var;
}
Now, you can get the values as you go one char at the time, or read an entire line and get the values from there.
Going one char at the time will not require any additional space but will result in a longer program (full of 'if' and 'while'). Reading the line will be slightly easier but will require you to handle the maximum size of a line.
A proper structuring of functions will help you a lot:
do {
if ((c = get_month(fp, &month)) != EOF)
if ((c = get_value(fp, &value)) != EOF)
if ((c = get_sensor(fp, &sensor)) != EOF)
if ((c = get_var(fp, &var)) != EOF)
measures = add_data(measures, month, value, sensor, var);
} while (c != EOF);
return measures
Where measures can be a linked list or a resizable array of your structs and assuming you'll go one char at the time.
There are quite many other details you should set before you're done, I hope this will help you find the right direction.
Your delims string is not null-terminated.
Also, the delimiters are matched character-by-character, so there is no use repeating the space six times, nor will your call to strtok match a run of six spaces. Perhaps you need something like strpbrk.

Resources