C - Reading multiline from text file - c

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

Related

Need to read a file with multiple lines of integers into an array

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.

Reading text file into an array in C

I want to parse a .txt file into a 1D array in C. I'm using the fgets function to read the contents of the file into the array("waveform" as the array into which the file contents are to be stored - defined as a "char"). The saved values need to be saved into a new array as integer values. I am not sure where I am going wrong.
P.S: I am new to programming in C, please bear with me :)
Please ignore the indexing issues, done due to pasting
int main(){
int a, win[10];
FILE *filename = fopen("testFile.txt","r");
char waveform[10];
if (filename == NULL)
{
printf("Error opening file.\n");
exit(8);
}
for(int i =0;1;i++){
if(fgets(waveform[i], 10, filename) == NULL);
break;
if(i < 10)
{
a = atoi(waveform[i]);
win[i] = a;
}
}
fclose(filename);
return 0;
}
Compiler errors - image embedded
Data in testFile.txt:
1 to 10 in a row vector.
You are on the right track. Here is my contribution on the topic:
Open the file (fopen)
Count number of lines (getc and rewind)
Read all lines into array (getline)
Free memory and close file (free and fclose)
Code example:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
// Open File
const char fname[] = "testFile.txt";
FILE *fp = fopen(fname, "r");
if( !fp )
goto error_open_file;
printf("Opened file: %s\n", fname);
// Count Lines
char cr;
size_t lines = 0;
while( cr != EOF ) {
if ( cr == '\n' ) {
lines++;
}
cr = getc(fp);
}
printf("Number of lines: %ld\n", lines);
rewind(fp);
// Read data
{// 'goto' + data[lines] causes error, introduce block as a workaround
char *data[lines];
size_t n;
for (size_t i = 0; i < lines; i++) {
data[i] = NULL;
size_t n = 0;
getline(&data[i], &n, fp);
if ( ferror( fp ) )
goto error_read_file;
}
for (size_t i = 0; i < lines; i++) {
printf("%s", data[i]);
free(data[i]);
}
}
// Close File
fclose(fp);
return 0;
error_read_file:
perror("fopen ");
return 1;
error_open_file:
perror("getline ");
return 2;
}
There are several errors in this loop
for(int i =0;1;i++){
if(fgets(waveform[i], 10, filename) == NULL);
break;
if(i < 10)
{
a = atoi(waveform[i]);
win[i] = a;
}
}
For starters there is a semicolon after the if statement
if(fgets(waveform[i], 10, filename) == NULL);
^^^
Secondly the fgets call
fgets(waveform[i], 10, filename)
^^^
is invalid because the type of the expression waveform[i] is char.
And correspondingly this statement
a = atoi(waveform[i]);
is also invalid.
There must be at least
fgets( waveform, 10, filename)
and
a = atoi( waveform );
I suppose that each line of the file contains exactly one number. (Otherwise you should use for example sscanf to extract numbers from a line using an internal additional loop.)
The loop can look like
int i = 0;
for ( ; i < 10 && fgets( waveform, 10, filename) != NULL; i++ )
{
a = atoi( waveform );
win[i] = a;
}
After the loop the variable i will contain the actual number of elements of the array win.
Pay attention to that the name filename is not good for a pointer of the type FILE *. File name is the string "testFile.txt" in your code.
If you want to use the fgets() function you don't have to put it into a loop. Indeed, the second argument of fgets() is the number of elements you want to read.
I would have put the fgets() into a singl-line instruction, and then loop from 0 to 10 to make the conversion from char to int with the atoi() function.
Moreover, you have a ; at the end of your if() statement, so you'll execute it not in the way you want.

Reading integers from txt file in C

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?

Infinitely reading input when inputting a file from stdin

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

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