EXC_BAD_ACCESS when reading from csv file using fgets and sscanf - c

I am trying to read from a huge .csv file (about 100,000 lines). Using fgets, I extract the entire line then using sscanf, I read 21 int values inside the line. However, sscanf returns error EXC_BAD_ACCESS at line 758. I tried to increase the size for the buffer and can read more lines but not all. Is there more elegant and clean way to read huge data with C? Thank you.
char buffer[316]; // buffer to contain one line
int x[20][100000]; // int values saved in a matrix
int line = 0; // counter for lines
int j = 0; // counter for lines (excluding headers)
FILE *fp;
char fname[] = "/Users/basho/data_TS-20.csv";
fp = fopen(fname, "r");
if(fp == NULL) {
printf("%s file not open!\n", fname);
return -1;
}
// read one line at a time using fgets
while (fgets(buffer, sizeof buffer, fp) != NULL) {
if (line > 1) // we first skip the two first lines of the file, some empty line and the header.
{
printf("line %d\n",line);
sscanf(buffer, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d, ",
&x[0][j], &x[1][j], &x[2][j], &x[3][j], &x[4][j], &x[5][j], &x[6][j], &x[7][j], &x[8][j], &x[9][j],
&x[10][j], &x[11][j], &x[12][j], &x[13][j],&x[14][j], &x[15][j], &x[16][j], &x[17][j], &x[18][j],
&x[19][j], &x[20][j]);
for(int i = 0; i<20; i++){
printf("%d ",x[i][j]);
}
printf("%d\n",x[20][j]);
j = j+ 1;
//}
}
line =line + 1;
}
fclose(fp);
return 0;
}

Review int x[20][100000]; and &x[20][j] --> off by 1.
// int x[20][100000];
int x[21][100000];
Also
j = j+ 1;
if (j >= 100000) break;

Related

how do i make operations on a specific line in a text file in c?

void main(void)
{
FILE* textfile;
char line[1000];
textfile = fopen("omar.txt", "r");
if (textfile == NULL)
return 1;
while (fgets(line, 1000, textfile)) {
printf(line);
}
fclose(textfile);
}
so this code prints the whole content of a text file , what should I do to read the third line in the file for example ?
To read the nth line in a file you can do something like this
int i = 0;
while (fgets(line, 1000, textfile)) {
i++;
if (i == n) {
// do stuff with nth line
break;
}
}
This approach uses a counter to count until the nth iteration is reached. Once it is, you can do what you need to do with the nth line.
Also this may be unrelated but you should never use printf without a format specifier as you have in printf(line);. This can be dangerous and could be used by an attacker to exploit the program. I would recommend that in your case puts(line); is a better alternative.
For example:
int readNthLine(FILE *fi, char *buff, size_t buffsize, size_t line)
{
fseek(fi, 0, SEEK_SET);
{
for(size_t cline = 0; cline < line; cline++)
{
if(!fgets(buff, buffsize, fi)) return -1;
}
}
return 0;
}
This very simple function will work only if the size of the buffer is larger than the length of the longest line in the file.
Of course, you should check the result of any I/O operation.

How to read from a file and parse it

I have a file .txt containing some values formatted like this:
0,30,25,10
Now, I open up the file and store it into an array
char imposta_tratt[300];
FILE *fp;
fp = fopen("/home/pi/Documents/imposta_trattamento.txt", "r");
if (fp == 0) return;
fread(imposta_tratt, sizeof(imposta_tratt), 1, fp);
fclose(fp);
Now I expect to have the array filled with my data. I have the values separated by a , so I go on and parse it:
const char delim[2] = ",";
int t=0;
char *token = strtok(imposta_tratt, delim);
while (token!=NULL){
strcpy(tratt[t],token);
token = strtok(NULL, delim);
tratt[t]=token;
t++;
}
Here, referring to what's in the file .txt, I expect to have tratt[0]=0; tratt[1]=30; tratt[2]=25; and so on, but seems like I am missing something since it's not like this.
All I want is to have the values of the txt file stored in single variables. Can someone help?
What you are trying to achieve can simply be done using fgets():
bool read_file_content(const char *filename, const size_t tsizemax, int tratt[tsizemax], size_t *tsize, const char *delim)
{
// Attempt to open filename.
FILE *fp = fopen(filename, "r");
if (!fp) return false; // Return false upon failure.
// Try to read one line. If you have more, you need a while loop.
char imposta_tratt[300];
if (!fgets(imposta_tratt, sizeof imposta_tratt, fp)) {
fclose(fp);
return false;
}
*tsize = 0;
char tmp[300]; // Temporary buffer. Used for conversion into int.
char *token = strtok(imposta_tratt, delim);
while (token && *tsize < tsizemax) {
strncpy(tmp, token, sizeof tmp);
tratt[(*tsize)++] = atoi(tmp);
token = strtok(NULL, delim);
}
fclose(fp);
return true;
}
const char *filename: The file you want to parse.
const size_t tsizemax: The maximum size of your tratt array. It is important to control the size, otherwise your code will have buffer overflow (think of when your file has more than 100 tokens, for example).
int tratt[tsizemax]: The array that will hold the values.
size_t *tsize: The number of tokens read (used in combination of tsizemax).
const char *delim: The delimiter(s), in your case a ,.
This is your main():
int main(void)
{
int tratt[100];
size_t size = 0;
if (!read_file_content("in.txt", 100, tratt, &size, ",")) {
puts("Failed");
return 1;
}
for (size_t i = 0; i < size; ++i)
printf("%d\n", tratt[i]);
}
Output:
0
30
25
10
Suppose "in.txt" has contents
0,30,25,10
The below program uses fscanf to read the integers into the tratt array, one-by-one. As we read integers using fscanf, we make sure it's return value is as expected. If not, we close the file and exit. In the event that the return value of fscanf is not as expected, the program also prints which type of error occurred. Currently, if any error occurs, the program stops. However, you can make the program behave differently depending on the error that occurred if you like.
As output, the program prints all of the integers read into the tratt array. The output is
0
30
25
10
Now this program assumes we know the number of elements we want to read into tratt. If we do not, we could allow for dynamically allocating more memory should the array need more elements or perhaps "in.txt" could contain a data structure, say, at the beginning/end of the file that records information about the file, such as the number of numbers in the file and the data type (a binary file would be best suited for this). These are just a couple of the possibilities.
A better approach might be to read characters in one-by-one (say, using getc) and use strtol to convert a sequence of character digits to a long int (I would have taken an approach similar to this).
Nevertheless, this approach is more succinct and should suffice.
#include <stdio.h>
#include <stdlib.h>
#define FILE_NAME "in.txt"
#define MAX_LEN 4
int main(void) {
int i, tratt[MAX_LEN];
FILE *fp = fopen(FILE_NAME, "r"); /* open file for reading */
/* if cannot open file */
if (fp == NULL) {
printf("Cannot open %s\n", FILE_NAME);
exit(EXIT_FAILURE);
}
/* read integer, checking return value of scanf as expected */
if (fscanf(fp, "%d", &tratt[0]) != 1) {
if (ferror(fp))
printf("fscanf: read error\n");
else if (feof(fp))
printf("fscanf: end of file\n");
else
printf("fscanf: matching failure\n");
fclose(fp);
exit(EXIT_FAILURE);
}
for (i = 1; i < MAX_LEN; i++)
/* read comma plus integer, checking return value of scanf */
if (fscanf(fp, ",%d", &tratt[i]) != 1) {
if (ferror(fp))
printf("fscanf: read error\n");
else if (feof(fp))
printf("fscanf: end of file\n");
else
printf("fscanf: matching failure\n");
fclose(fp);
exit(EXIT_FAILURE);
}
fclose(fp); /* close file */
/* print integers stored in tratt */
for (i = 0; i < MAX_LEN; i++)
printf("%d\n", tratt[i]);
return 0;
}

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.

using fgets() to extract lines from a file not working

I'm trying to develop a function that reads each line of a text file and the it stores them in an array of strings (char**) but fgets() doesnt seem to work, it always return a null character.
Here is the function
char** getLines(FILE* fp){
char** lines;
int numberOfLines; //number of lines int the file
char ch; //aux var
int i; //counter
while(!feof(fp)){
ch = fgetc(fp);
if( ch == '\n'){
numberOfLines++;
}
}
lines = malloc(numberOfLines*sizeof(char*));
if (lines==NULL){
fprintf(stderr,"Error, malloc failed");
exit(1);
}
for(i = 0; i<numberOfLines; i++){
lines[i] = malloc(MAX_LENGTH*sizeof(char)); //MAX_LENGTH = 128
}
i=0;
while(fgets(lines[i], MAX_LENGTH,fp)){
printf("Line %d: %s \n",i,lines[i]);
i++;
}
return lines;
}
The function never gets inside the while loop so it doesn't print anything
I'm also using a very simple input file:
test line 1
test line 2
test line 3
test line 4
Hope you can help me,
Thank you in advance.
You are already on the end of the file before entering the while loop.
Take a look here http://en.cppreference.com/w/cpp/io/c/rewind
Moves the file position indicator to the beginning of the given file stream. The function is equivalent to std::fseek(stream, 0, SEEK_SET); except that end-of-file and error indicators are cleared. The function drops any effects from previous calls to ungetc.
Check if this works:
char** getLines(FILE* fp){
/* ...... */
i=0;
rewind(fp); // Rewind here
while(fgets(lines[i], MAX_LENGTH,fp)){
printf("Line %d: %s \n", i, lines[i]); // Also use the index as first parameter
i++;
}
return lines;
}

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