I want to parse information from a CSV file and display it to my screen. My issue is that I keep receiving Segmentation fault core dumped from my while loop, which is supposed to be storing the CSV file info into different arrays to represent the different columns in the CSV. A sample line in my CSV would be rice,10,20,$2.00 which represents the food, stock, reorder limit, and cost per unit.
char buffer[1024];
char *line=buffer;
FILE *file=fopen("inventory.csv", "rw");
int x;
int i=0;
int j=0;
char *food [100];
int stock [100];
double cost [100];
int reorder [100];
while (fgets(line, 2048, file) != NULL){
food[i] = (strtok(line, ","));
stock [i] = (atoi(strtok(NULL, ",")));
reorder[i] = (atoi(strtok(NULL, ",")));
cost[i] = (atof(strtok(NULL, ",")));
line=line+25;
i++; }
First, your code has a basic problem: you want to read an infinite amount of data and store it into finite space.
In more details:
You keep advancing line by 25 after each line, it points to buffer which has a size of 1024, so after 40 lines you will be writing after the end of it.
Your arrays have a size of 100, so after 100 lines you will be writing after the end of them.
Do you need to store everything you read forever?
Now if this is not the problem, it could also be that strtok fails to find the "," and returns NULL, you should always check the return of strtok before using the result. It may for example fail if your file finishes with some empty lines, or just that you don't have a "," after the last element of the line that you read.
My bet is that you're trying to access a position in your arrays that doesn't exist. Try looking the last i value before your program crashes.
Please take a look at the following class. You may call it with
main.cpp
Stock line_stock;
file >> line_stock; //Use it in a loop cause it only reads a line
Stock.h
#include <iostream>
#include <string>
class Stock {
public:
private:
std::string food;
unsigned int stock;
double cost;
unsigned int reorder;
friend std::istream& operator >> (std::istream& is, Stock& s){
char coma;
is >> s.food >> coma>>
s.stock >> coma >>
s.cost >> coma >>
s.reorder;
return is;
}
};
//double doesn't work well on money, use unsigned int instead. Divide by 100 to get the dollar and % 100 to get the cents.
Using that class reads a line, then you process it and you're up to the next line.
When trying to read a file with an unknown number of data lines 2 approaches come to mind:
1) Read line by line and adjust memory allocation as needed.
2) Parse the file twice. First to find needed length.
A robust program typically uses method #1. But #2 is simpler for learners. #2 follows.
FILE *file = fopen("inventory.csv", "rw");
// Test for file == NULL
char buffer[100];
size_t n = 0;
// Find number of lines
while (fgets(buffer, sizeof buffer, file) != NULL)
n++;
char *food = malloc(n * sizeof *food);
int stock = malloc(n * sizeof *stock);
double *cost = malloc(n * sizeof *cost);
int *reorder = malloc(n * sizeof *reorder);
// TBD: Add checks for failed malloc
rewind(file);
size_t i = 0;
for (i = 0; i<n; i++) {
char name[sizeof buffer];
if (fgets(buffer, sizeof buffer, file) != NULL) Handle_UnexpectedError();
int cnt = sscanf(buffer, "%s ,%d ,%d , $%lf",
name, &stock[i], &reorder[i], &cost[i]);
if (cnt != 4) Handle_FormatError();
food[i] = strdup(name);
}
fclose(file);
// Use food, stock, reorder, cost
// When done
for (i = 0; i<n; i++) {
free(food[i]);
}
free(food);
free(stock);
free(reorder);
free(cost);
Related
I need to read a file of ints into an array in C. A sample of the file I need to read is below, though note the files this will process can have thousands or hundreds of thousands of lines.
127
234
97
8723
I've gotten the file open in C, read how many lines there are so I know how many spaces my array needs, but I can't seem to read/parse each line into the array.
FILE *file;
int N = 0;
char filePath[30];
char endFile;
printf("What file should be used?\n");
scanf("%s", filePath);
file = fopen(filePath, "r");
if(file == NULL) {
printf("This file failed to open.\n");
break;
}
for(endFile = getc(file); endFile!=EOF; endFile=getc(file))
if(endFile == '\n') {
N = N+1;
}
int myArray[N];
while(fscanf(file, "%d\n", &a) != EOF) {
fscanf(file, "%d\n", &a); // I'm not sure this line is needed...
printf("%d\n", a);
M[i] = a;
}
From here, I need to read the file contents into myArray, with each line being the corresponding spot in the array (i.e. line zero is myArray[0], line one is myArray[1], etc.). I can't seem to find a way to do this, though I see several methods to do tab-delimited 2d arrays or csv multi-dimensional arrays.
Please also let me know if creating the array/determining the array size can be done in a better way than literally counting new-line characters...
There's no need to first "count the number of lines".
The following code cautiously grows an array of integers (by increments of 10).
#define GROW 10
int *rec = NULL, nRec = 0, sz = 0;
while( fgets( buf, sizeof buf, ifp ) != NULL ) {
if( nRec == sz ) {
rec = realloc( rec, (nRec+GROW) * sizeof *rec );
/*omitting test for failure */
sz += GROW;
}
rec[ nRec++ ] = atoi( buf );
}
This shows what is possible.
Note that realloc() can fail, returning NULL... It's up to you to add a bit of code to handle that condition.
Further, some conventional thought is to double the size of the allocation when needed (because realloc() may not be 'cheap'.) You can decide if you want to grow the array in increments (of 1024?) or grow it exponentially.
I`m making a file reader that reads integers numbers line by line from a file. The problem is that is not working. I think I am using fscanf in a wrong way. Can someone help me?
I had already look for answers in others questions but I can`t find anything that explain why I my code is not working.
int read_from_txt(){
FILE *file;
file = fopen("random_numbers.txt", "r");
//Counting line numbers to allocate exact memory
int i;
unsigned int lines = 0;
unsigned int *num;
char ch;
while(!feof(file)){
ch = fgetc(file);
if (ch == '\n'){
lines++;
}
}
//array size will be lines+1
num = malloc(sizeof(int)*(lines+1));
//storing random_numbers in num vector
for(i=0;i<=lines;i++){
fscanf(file, "%d", &num[i]);
printf("%d", num[i]);
}
fclose(file);
}
The txt file is like:
12
15
32
68
46
...
But the output of this code keeps giving "0000000000000000000..."
You forgot to "rewind" the file:
fseek(file, 0, SEEK_SET);
Your process of reading goes through the file twice - once to count lines, and once more to read the data. You need to go back to the beginning of the file before the second pass.
Note that you can do this in a single pass by using realloc as you go: read numbers in a loop into a temporary int, and for each successful read expand the num array by one by calling realloc. This will expand the buffer as needed, and you would not need to rewind.
Be careful to check the results of realloc before re-assigning to num to avoid memory leaks.
You could try to use the getline function from standard IO and add the parsed numbers into the array using only one loop. See the code below. Please check https://linux.die.net/man/3/getline
Also, you can use the atoi or strtoul functions to convert the read line to an integer. Feel free to check https://linux.die.net/man/3/atoi or https://linux.die.net/man/3/strtoul
The code below evaluate a file with a list of numbers and add those numbers to a C integer pointer
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char ** argv) {
FILE * file;
file = fopen("./file.txt", "r");
size_t read;
char * line = NULL;
size_t line_len = 0;
size_t buffer_size = 10;
int * buffer = (int *)malloc(sizeof(int) * buffer_size);
int seek = 0;
while((read = getline(&line, &line_len, file)) != -1) {
buffer[seek++] = atoi(line);
if (seek % 10 == 0) {
buffer_size += 10;
buffer = (int *)realloc(buffer, sizeof(int) * buffer_size);
}
}
for (int i = 0; i < seek; i++) {
printf("%d\n", buffer[i]);
}
free(buffer);
fclose(file);
}
If you aren't sure which conversion function should you use. You can check the difference between atoi and sscanf at What is the difference between sscanf or atoi to convert a string to an integer?
void inputData()
{
FILE* fp = fopen("input_001.txt", "r");
if(fp == NULL)
{
exit(FILE_FAILED_TO_OPEN);
}
fseek(fp, 0, SEEK_END); //set position to end of file
int size = ftell(fp);
if(size == 0)
{
exit(PARSING_ERROR_EMPTY_FILE);
}
int i;
char *input;
char *errPtr = NULL;
char *data;
fgets(input, 64, fp);
for(i = 0; i < size; i++)
{
strtol(input[i], *errPtr, 10);
//testing the output
printf("%d\n", input[i]);
}
fclose(fp);
if(fclose(fp) != 0)
{
exit(FILE_FAILED_TO_CLOSE);
}
}
I am trying to input data from a text file of unknown size into an array and keep only the integers. The format of the text file is one number per line and there can be any number of lines. I have included my attempt at trying to input the data, but my coding was not going so well. I want to use fgets() and then strtol() to convert the lines I get from fgets() into integers and put them in an array so I can work with that data. Any help is appreciated!
You haven't allocated any space for input to point to. I saw your
earlier version had a malloc; you can use that, or just a char
array.
Did you mean to use data? Because you're not, yet.
fgets reads at most one line at a time, so you need to put your reads
in a loop.
You appear to be converting the string to a number multiple times. For
instance, if the first line were "12345", this code would get 12345,
then 2345, 345, etc. This was presumably not your intention.
You're incrementing i up to size. size is the file size and might
be quite large, but you only read a maximum of 64 characters into the
buffer. (Or would have, if space had been allocated.)
In short, this code is very confused and I recommend starting over from
scratch. Decide whether you want to read the entire file at once, or one
line at a time; I recommend the latter, it takes less memory and is
simpler. If you want to store them in an array, you can do that with
malloc and then realloc as needed to grow the array dynamically.
do not use strtol, use atoi instead.
and use a loop to read the lines.
just a quick answer:
int count = 0;
while( fgets (input, 64, fp) != NULL )
{
count++;
}
fseek(fp, 0, SEEK_SET);
int* arr = new int[count];
count = 0;
while( fgets (input, 64, fp) != NULL )
{
int a =atoi(input);
arr[count++] = a;
}
If you don't know how many lines are in the file, you have a few options:
Loop through the file, counting the number of lines (call it nlines). Then declare the array int numbers[nlines].
Use a linked list instead of an array to store the numbers.
Dynamically allocate blocks of an array.
As for reading the integer data itself, something like this would work if you decide to go with the first option:
void inputData(const char* fname, const unsigned int nlines)
{
FILE* fp = fopen(fname, "r");
if (fp == NULL) {
exit(EXIT_FAILURE);
}
int numbers[nlines];
double d = 0;
fscanf(fp, "%lf", &d);
int i = 0;
while (!feof(fp)) {
numbers[i++] = (int)floor(d);
fscanf(fp, "%lf", &d);
}
fclose(fp);
}
First I open a CSV file (Cars.csv) using my C file cars.c. My CSV file would look like:
toyota, 100, 20, 150.50
nissan, 200, 50, 100.75
BMW, 400, 80, 323.00
The 1st column is names, 2nd column is stock, 3rd column is order, 4th column is price.
Then I have to do two works(I will call the program in the commandline with the work name and any other argument I need to pass):
List all the items in the CSV file while the commas cannot be a part of the output. I also have to print headers for each column. (./cars list)
I would send an argument when running the program from the command line. The argument would be one the name of the items in my csv file. Then I would have to subtract 1 from the stock and update the CSVfile. (./cars reduce nissan)
This is how I opened the CSV file.
void main(int argc, char *argv[]) {
FILE *in = fopen ("Cars.csv" ,"rt");
if (in == NULL) {
fclose (in); return 0;
}
//I need to store the data from the CSV file in an array of pointers. I know how to do that, but Im having a problem in storing them without the commas and then doing what I need to do in part 1.
//This is the only code I could write for the 2nd part.
int p =0;
for(p =0; p<; p = p + 4) {
if(strcmp(argsv[2],save[p]) == 0) { //save is the array of pointers where I
int r = int(save[p +1]); //store the data from the csv file
int s = r - 1;
char str[15];
sprintf(str, "%d", s);
save[p +1] = str; // not sure if this line actually makes sense
}
}
There are a couple of possible solutions.
Here's one:
Read the lines into an array of pointers. Allocate array with malloc and reallocate as needed with realloc. Read lines with fgets.
For each line, use strtok to split the line, and do whatever you need with each field.
Another solution:
Use your favorite search engine to search for an existing CSV-reading library (there are many out there, which can handle very advanced CSV files).
Think how the data should be represented and define a structure.
typedef struct {
char *name;
int stock;
int order;
double price;
} car_type;
Open the file: See OP's code
Find how many lines and allocate memory
char buf[100];
size_t n = 0;
while (fgets(buf, sizeof buf, in) != NULL) n++;
rewind(in);
car_type *car = malloc(n * sizeof *car);
if (car == NULL) OutOfMemory();
Read the cars
for (size_t i=0; i<n; i++) {
if (fgets(buf, sizeof buf, in) == NULL) Handle_UnexpectedEOF();
char car_name[100];
if (4 != sscanf(buf, "%s ,%d ,%d, %lf", car_name,
&car[i].stock, &car[i].order, &car[i].price) Handle_FormatError();
car[i].name = strdup(car_name);
}
// Use `car` ...
When done
fclose(in);
for (size_t i=0; i<n; i++) {
free(car[i].name);
}
free(car);
This is most likely a dumb question!
I have a text file filled with random numbers and I would like to read these numbers into an array.
My text file looks like this:
1231231 123213 123123
1231231 123213 123123
0
1231231 123213 123123
1231231 123213 123123
0
And so on.. The piece of numberse ends with 0
This is what I have tried so far:
FILE *file = fopen("c:\\Text.txt", "rt");
char line[512];
if(file != NULL)
{
while(fgets(line, sizeof line, file) != NULL)
{
fputs(line, stdout);
}
fclose(file);
}
This does clearly not work, since I read each line into the same variable.
How can I read the lines and when the line gets the line where it ends with 0, then store that piece of text into an array?
All help is appreciated.
You just have to store the numbers that you read from the file in some permanent storage! Also, you probably want to parse the individual numbers and obtain their numerical representation. So, three steps:
Allocate some memory to hold the numbers. An array of arrays looks like a useful concept, one array for each block of numbers.
Tokenize each line into strings corresponding to one number each, using strtok.
Parse each number into an integer using atoi or strtol.
Here's some example code to get you started:
FILE *file = fopen("c:\\Text.txt", "rt");
char line[512];
int ** storage;
unsigned int storage_size = 10; // let's start with something simple
unsigned int storage_current = 0;
storage = malloc(sizeof(int*) * storage_size); // later we realloc() if needed
if (file != NULL)
{
unsigned int block_size = 10;
unsigned int block_current = 0;
storage[storage_current] = malloc(sizeof(int) * block_size); // realloc() when needed
while(fgets(line, sizeof line, file) != NULL)
{
char * tch = strtok (line, " ");
while (tch != NULL)
{
/* token is at tch, do whatever you want with it! */
storage[storage_current][block_current] = strtol(tch, NULL);
tch = strtok(NULL, " ");
if (storage[storage_current][block_current] == 0)
{
++storage_current;
break;
}
++block_current;
/* Grow the array "storage[storage_current]" if necessary */
if (block_current >= block_size)
{
block_size *= 2;
storage[storage_current] = realloc(storage[storage_current], sizeof(int) * block_size);
}
}
/* Grow the array "storage" if necessary */
if (storage_current >= storage_size)
{
storage_size *= 2;
storage = realloc(storage, sizeof(int*) * storage_size);
}
}
}
In the end, you need to free the memory:
for (unsigned int i = 0; i <= storage_current; ++i)
free(storage[i]);
free(storage);