File parsing issues using C dynamic strings - c

Following is a part of a file that needs to be parsed
record(ai, "SRC01-VA-IMG1:getPressure")
{
field(DESC, "Reads Cell SR-GC1 Pressure")
field(SCAN, "1 second")
field(DTYP, "stream")
field(INP, "#vacuum-XGS600-gc.proto getPressure(1) SR-GC1")
field(PINI, "YES")
field(VAL, "0")
field(PREC, "3")
field(LOLO, "") field(LLSV, "NO_ALARM")
field(HIGH, "") field(HSV, "NO_ALARM")
field(HIHI, "") field(HHSV, "NO_ALARM")
}
The parsing process will read the record name (In the qoutes) and type (ai, in the line record), and each field type and value (in any line that starts with field. e.g. Type: PINI, Value: "YES"). Note that each line can contain more than one record definition. The example contains some interesting cases like multiple fields on the same line, parenthesis inside qoutes, etc.
Here is my code for that purpose:
struct field
{
char* name;
char* value;
};
struct record
{
char* name;
char* type;
struct field* fields;
};
struct record* get_records(char* file, int* length)
{
size_t SIZE = 0;
size_t fSIZE = 1;
const int N = 100;
struct record* records = malloc(1 * sizeof(struct record));
struct field* fields;
// char line[N];
char* line = malloc(N);
char record_name[100];
char record_type[10];
char field_name[10];
char field_value[100];
char temp[100];
int open, close, comma, q1, q2;
FILE* fp = fopen(file, "r");
if(fp != NULL)
{
while(fgets(line, N, fp))
{
line = strtrim(line);
if(strncmp(line, "record", 6) == 0)
{
memset(record_name, 0, sizeof(record_name));
memset(record_type, 0, sizeof(record_type));
struct record* r = malloc(sizeof(struct record));
open = strchr(line, '(') - line + 1;
close = strchr(line, ')') - line + 1;
comma = strchr(line, ',') - line + 1;
q1 = strchr(line, '"') - line + 1;
q2 = strrchr(line, '"') - line + 1;
strncpy(record_type, &line[open], comma - open - 1);
strncpy(record_name, &line[q1], q2 - q1 - 1);
record_name[q2 - q1 - 1] = '\0';
record_type[comma - open - 1] = '\0';
r->name = record_name;
r->type = record_type;
printf("Name: %s\n", r->name);
printf("Type: %s\n", r->type);
fSIZE = 0;
fields = malloc(100 * sizeof(field)); // HERE
while(fgets(line, N, fp))
{
struct field* f = malloc(sizeof(struct field));
if(strncmp(line, "}", 1) == 0)
break;
if(strncmp(line, "{", 1) == 0)
continue;
line = strtrim(line);
int i1 = 0;
int i2 = 0;
char* p1 = strstr(line, "field");
char* p2;
while(p1 != NULL)
{
i1 = p1 - line;
p2 = strstr(p1 + 1, "field");
if(p2 != NULL)
{
i2 = p2 - line;
p1 = strstr(p1 + 1, "field");
}
else
{
i2 = strlen(line);
p1 = NULL;
}
memset(temp, 0, sizeof(temp));
memset(field_name, 0, sizeof(field_name));
memset(field_value, 0, sizeof(field_value));
strncpy(temp, &line[i1], i2 - i1); temp[i2-i1] = '\0';
printf("Line2 : %s\n", temp);
open = strchr(temp, '(') - temp + 1;
close = strrchr(temp, ')') - temp + 1;
comma = strchr(temp, ',') - temp + 1;
q1 = strchr(temp, '\"') - temp + 1;
q2 = strrchr(temp, '\"') - temp + 1;
strncpy(field_value, &temp[q1], q2 - q1 - 1); field_value[q2-q1-1] = '\0';
strncpy(field_name, &temp[open], comma - open - 1); field_name[comma-open-1] = '\0';
printf("Name : %s\n", field_name);
printf("Value: %s\n\n", field_value);
f->name = field_name;
f->value = field_value;
fields[fSIZE++] = *f;
}
free(line);
line = malloc(N);
}
r->fields = fields;
records[SIZE++] = *r;
}
else
continue;
}
}
else
{
printf("%s\n", "Unable to open file.");
exit(1);
}
*length = SIZE;
// fclose(fp);
return records;
}
int main()
{
int length = 0;
struct record* records = get_records("./test.db", &length);
// printf("Anything \n");
int i = 0;
for(i = 0; i < length; i++)
{
struct record* r = (struct record*) &records[i];
printf("Name: %s\n", r->name);
printf("Type: %s\n", r->type);
}
return 0;
}
Now I have two issues with this implementation which I could not figure out:
In the main function there is a printf statement, if uncommented the printf inside the loop will print garbage, otherwise correct output.
Having fclose in the function get_records uncommented gives a Segmentation fault (core dumped). With trial and error I figured out I either use it or use the malloc line designated by the comment HERE, for some reason using one of them or non the fault goes away. I understand I have something wrong with memory allocation somewhere, I used valgrind but it did not help me locating anything
Notes:
Constant numbers used in malloc are for testing purposes. We have files that contains 100's of records. This leads to a future question, which is better use large enough malloc buffer or use realloc?
Any other implementation in C, if any, is welcome :)

Seems like memory was massively messed up by this implementation due to many calls for malloc and free. What I did was to replace char* in the structs with arrays and line definition converted to char[] and dereference it in strtrim call.

Related

C problem with memory leaks (realloc function)

scanFolderPath - path of folder with files.
filesToScan - array of strings with the files name.
I have problem with the realloc line (the third line in the for loop). And I don't understand why! Thank you for helping programmers community ;)
char* filePath = malloc(0);
char* fileContent = 0;
char* partContent = 0;
FILE* fileToScan;
int i = 0, j = 0, virus = FALSE, flag = FALSE, counter = 0;
for (i = 0; i < amountOfFiles; i++)
{
flag = FALSE;
if (scanFolderPath != NULL && filesToScan[i] != NULL)
{
realloc(filePath, (sizeof(char) * (strlen(scanFolderPath) + 1 + strlen(filesToScan[i]))));
}
strcpy(filePath, "");
getFilePath(filePath, scanFolderPath, filesToScan[i]);
fileToScan = fopen(filePath, "rb");
readFile(&fileContent, filePath);
if (choice == '0')
{
virus = scanSingature(fileContent, virusSingature, getLength(fileToScan), virusSingatureLen);
if (virus)
{
printf("%s - Infected!\n", filePath);
fprintf(logsFile, "%s", filePath);
fprintf(logsFile, " Infected!\n");
}
else
{
printf("%s - Clean\n", filePath);
fprintf(logsFile, ("%s", filePath));
fprintf(logsFile, " Clean\n");
}
fclose(fileToScan);
strcpy(filePath, "");
}
}
try
filePath = realloc(filePath, (sizeof(char) * (strlen(scanFolderPath) + 1 + strlen(filesToScan[i]))));
that way the contents of filepath are reallocated, and the pointer is returned to filepath
realloc returns new allocated block. You do not assign to anything so the reallocated memory is lost and your pointer is pointing to the invalid memory assuming realloc success).
The correct way of reallocating:
void *p = malloc(10);
void t;
/* ... */
t = realloc(p, 20);
if(t) p = t; //check if the realloc was successful
/* ... */
free(p)

Read text from phone book list and add to a structure

I am attempting to read from file "pb_List.txt" that contains:
John:789-654-3210
Bill:852-123-4567
Amy:963-321-0000
I need to add the name and number contents to a phone book structure "pb"
struct phonebook{
char name[value_size];
char phone[value_size];
}
struct phonebook pb[book_size];
UPDATE:
void addFile(){
File *pb_List;
pb_List = fopen("pb_List.txt", "r");
char name[value_size];
char phone[value_size];
fscanf(pb_List, "%s %s", name, phone);
strcpy(pb[size].name, name);
strcpy(pb[size].phone, phone);
size++
}
I was able to add the first line, but my function obviously doesn't iterate to the next line. How would my while loop look for this?
So I ended up wanting to continue writing an almost full program to your problem. Thus, I updated this answer with a full example of how to use my original answer in a program. Below the code of my original answer is the actual code of a working program.
For your code, I don't think the "phonebook"'s name and phone should be an array but should be a pointer to arrays of chars. That is because if you have a large program even if you give a size that is always bigger than the necessary size or working with a version of "c" that allows dynamic array allocation at runtime, you can run into the problem of running out of stack memory.
Nonetheless, this code below is an example and is for parsing just one line. In a real usage case, you would have to modify the code properly to use it in a loop. For the routines, you just look for the ":" location. If you found that then look for the null char(end of string) location. I did place comments in the code, thus, I am not going to explain much here.
Also, you should give consideration to letting pb be dynamic with calloc() and realloc(). Also, calloc() and realloc() do not always work. You can also, use malloc() but you have to inject the null char by yourself.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct phonebook{
char * name;
char * phone;
};
int main(void) {
char * line = "John:789-654-3210";
char c;
// search for the :
struct phonebook pb[1];
int dotloc = -1;
int endloc = 0;
int recordIndex = 0;
// Find the dot first
// and then end line.
// This could be done in one loop
// But split into two
for ( int i = 0; ; i++) {
if( line[i] == ':' ) {
dotloc = i;
break;
}
if( line[i] == '\0' ) break;
}
// If found : then that is valid
if ( dotloc > -1 ){
for( int i = dotloc + 1; ; i++){
if( line[i] == '\0' ){
endloc = i;
break;
}
}
// Positioning
// If : is at pos, there is 5 char in the string
// add + for null char.
// If : is at 0 there isn't a char but when calloc still need one for the null char. Empty string.
pb[recordIndex].name = (char*) calloc(dotloc + 1, sizeof(char));
// If : is at 5 and end is at 10, there is only 4 char in between but add keep five because of end char.
pb[recordIndex].phone = (char*) calloc(endloc - dotloc, sizeof(char));
// Memory allocation fail.
// Do something else.
if( pb[recordIndex].name == NULL ) return 1;
if( pb[recordIndex].phone == NULL ) return 1;
// copy from line[0] to dotloc location as how many chars.
// if dotloc is 0, nothing will be copy.
// null char is already appended by calloc.
memcpy(pb[recordIndex].name, line, dotloc * sizeof(char));
// copy from the location of where dotloc is plus 1
// how many char is base on where endloc is - dotloc
// -1
// If endloc at 1 and dot loc is at 0(next to), nothing to be copy.
memcpy(pb[recordIndex].phone, &line[dotloc + 1], (endloc - dotloc - 1));
recordIndex++;
}
printf("%s\n", pb[0].name);
printf("%s", pb[0].phone);
// Sometimes you need to free memory properly.
// depend on which system you target or your use case.
free(pb[0].name);
free(pb[0].phone);
return 0;
}
I won't give much explanation into what each function in the program does, as the key point already mentioned above and that can take hours to write. This program below to demonstrate how to utilize my original answer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct phoneRecord{
char * name;
char * phone;
};
struct phoneBook{
struct phoneRecord * record;
int length;
int size;
};
struct phoneBook newBook(){
// change init_size to 1 for debugging
int init_size = 30;
static size_t recordSize = sizeof(struct phoneRecord);
struct phoneBook output;
output.record = (struct phoneRecord*) malloc( init_size * recordSize);
output.length = 0;
output.size = init_size;
return output;
}
void freeBook( struct phoneBook * pb){
for ( int i = 0; i < pb->length; i++ ){
free(pb->record[i].name);
free(pb->record[i].phone);
}
free(pb->record);
pb->record = NULL;
pb->length = 0;
pb->size = 0;
}
// 0 for success
// 1 for error
int increaseBookSize(struct phoneBook * pb){
if ( pb == NULL ) return 1;
static size_t recordSize = sizeof(struct phoneRecord);
const int newSize = pb->size * 2;
if ( newSize == 0 ) return 1;
struct phoneRecord * tempPointer = (struct phoneRecord*) realloc(pb->record, newSize * recordSize);
if ( tempPointer != NULL ) {
pb->record = tempPointer;
pb->size = newSize;
return 0;
}
return 1;
}
// Return - 1 for error.
// Return 0 for no record found or no dot.
// Return 1 for record found.
int getRecord( const char * line, struct phoneRecord * pr){
// Null check
if ( pr == NULL ) return -1;
int dotloc = -1;
int endloc = 0;
for ( int i = 0; ; i++) {
if( line[i] == ':' ) {
dotloc = i;
break;
}
if( line[i] == '\0' ) break;
}
if ( dotloc > -1 ){
for( int i = dotloc + 1; ; i++){
if( line[i] == '\0' ){
endloc = i;
break;
}
}
pr->name = (char*) calloc(dotloc + 1, sizeof(char));
pr->phone = (char*) calloc(endloc - dotloc, sizeof(char));
if( pr->name == NULL ) return -1;
if( pr->phone == NULL ) return -1;
memcpy(pr->name, line, dotloc * sizeof(char));
memcpy(pr->phone, &line[dotloc + 1], (endloc - dotloc - 1));
return 1;
}
return 0;
}
int main(void) {
struct phoneBook pb = newBook();
const char * fileName = "test.txt";
char * line = NULL;
FILE *fp;
size_t len = 0;
int recordReturnCode;
fp = fopen(fileName, "r");
if (fp == NULL){
printf("Couldn't open file %s.\n", fileName);
return 1;
}
while (getline(&line, &len, fp) != -1) {
if ( pb.length >= pb.size ) {
if ( increaseBookSize(&pb) != 0 ) {
printf("Something is wrong with getting more memory for the book. However, still print out what already got.\n");
break;
}
}
recordReturnCode = getRecord(line, &pb.record[pb.length]);
if ( recordReturnCode == 1 ) pb.length++;
if ( recordReturnCode == -1 ){
printf("Something is wrong with getting the record. Clean up and exit.\n");
freeBook(&pb);
free(line);
return 1;
}
}
free(line);
printf("Print phonebook size for debuging. Size: %d\n", pb.size);
printf("Read file '%s' and found %d records. Printing each record.\n\n", fileName, pb.length);
for ( int i = 0; i < pb.length; i++ ){
printf("Record: %d | Name: %s | Phone: %s", i, pb.record[i].name, pb.record[i].phone);
}
freeBook(&pb);
printf("\n\nChecking book after free. Length: %d, Size: %d", pb.length, pb.size);
if ( pb.record == NULL ) printf("\nPhonebook free properly, record is NULL.");
return 0;
}
test.txt content:
John:789-654-3210
Bill:852-123-4567
This is not a valid record
Amy:963-321-0000
AfterEmpty:123-456-789
:###-###-####
noNumber:
Kevin:123-123-1234

Load/fill a struct with char** array as a struct member, c

In the last two days i have asked a question to load struct, but i have a problem to access my struct out side my loop(a loop to load my struct). i have edited my question/and code this way:
myfile.txt
Biology,chemistry,maths,music
Mechanics,IT,Geology,music,Astronomy
football,vollyball,baseball
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define path "myfile.txt"
typedef struct student_info
{
char **cources_as_list;
} std_info;
std_info *myinfo; //a global var that will conatain student info
int line_count = 0, cource_count = 0;
char** load_file()
{
char *line = NULL;
size_t len = 0;
FILE *fp;
int indexq=0;
fp = fopen(path, "r");
if (fp == NULL)
{
perror("FILE OPEN ERROR[IN load_file]: ");
exit(1);
}
char **mydata = malloc (sizeof (char *) * 4);//aup to four elements
while (getline(&line, &len, fp) != -1)
{
strtok(line, "\n");
mydata[indexq]= strdup(line);
indexq++;
}
line_count = indexq;
return mydata;
}
char **return_cource_list(char *cources_string) {
char *token;
char **cource_list = malloc(sizeof(char *) * 10);
int index = 0;
//course_string is delimited by ",": (eg. Biology,chemistry,maths,music). parse this and add to my char ** variable.
token = strtok(cources_string, ",");
while (token != NULL)
{
cource_list[index] = strdup(token);
token = strtok(NULL, ",");
index++;
}
cource_count = index;
return cource_list;
}
int main()
{
int i, j;
char** mydata = load_file(); //returns lines as a list/char ** array from file
for (i = 0; i < line_count; i++) //line_count is the number of elements/lines in "mydata"
{
printf("line_data: %s\n",mydata[i]);//i can see all my lines!
char **std_cource_list = return_cource_list(mydata[i]);
for (j = 0; j < cource_count; j++)
{
printf("\tcourse[%d]: %s\n",j,std_cource_list[j]);//i have all my courses as a list from each line
}
//can i load my struct like this? or any option to load my struct?
myinfo[i].cources_as_list = std_cource_list;
}
// i want to see my structure elements here, (nested for loop required).
}
Am getting seg_fault error while loading my char array to my struct.
(i.e: this line: myinfo[i].cources_as_list = std_cource_list;)
You need to allocate the memory for your struct.
std_info *myinfo = malloc(sizeof(std_info));
Also don't make it global, since there is really no need for global variables in this task.
Try
std_info * myinfo = malloc(line_count * sizeof *myinfo);
This allocates memory to hold line_count objects of std_info, with myinfo pointing to the 1st.
You never allocate space for myinfo and I would suggest making it a local variable. There is almost no need for global variables except in very specific cases.
Also, you are using malloc() almost only for fixed size allocations which would be easier to manage and more efficient if you do statically in the sense that you can use arrays for that.
This might be what you're interested in
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
struct student_info
{
char **courses;
size_t size;
};
char **
load_file(const char *const path)
{
char *line;
FILE *file;
char **data;
size_t row;
size_t length;
size_t count;
file = fopen(path, "r");
if (file == NULL)
{
perror("FILE OPEN ERROR[IN load_file]: ");
return NULL; // Notify the caller that there was a problem
// but do not necessarily quit as you might
// retry with another path.
}
count = 0;
for (int chr = fgetc(file) ; chr != EOF ; chr = fgetc(file))
count += (chr == '\n') ? 1 : 0;
rewind(file);
data = malloc((count + 1) * sizeof(*data));
if (data == NULL)
{
// Perhaps notify the error
fclose(file);
return NULL;
}
data[count] = NULL; // Use as end of array delimiter
row = 0;
line = NULL;
length = 0;
while ((length = getline(&line, &length, file)) != -1)
{
// The last character is always `\n' so remove it
data[row] = malloc(length);
if (data == NULL)
{
fclose(file);
for (size_t i = row ; i >= 0 ; --i)
{
free(data[i]);
free(data);
return NULL;
}
}
data[row][length - 1] = '\0';
memcpy(data[row], line, length - 1);
++row;
}
fclose(file);
// You need to `free' this, read the documentation
free(line);
return data;
}
char **
extract_courses_as_list(const char *const input)
{
char **courses;
size_t index;
const char *tail;
const char *head;
size_t count;
head = input;
count = 0;
/* Count the number of fields to allocate memory */
while (head != NULL)
{
tail = strchr(head, ',');
if (tail != NULL)
head = tail + 1;
else
head = NULL;
count += 1;
}
index = 0;
/* Allocate memory for the list, and the sentinel */
courses = malloc((count + 1) * sizeof(*courses));
head = input;
while (head != NULL)
{
ptrdiff_t length;
/* find the next `,' in the input string */
tail = strchr(head, ',');
if (tail == NULL) /* if it's not there, it's the last one */
tail = strchr(head, '\0');
/* compute the number of characters of the field */
length = (ptrdiff_t) (tail - head);
/* allocate space to copy the string */
courses[index] = malloc(length + 1);
if (courses == NULL) /* always be safe and check */
{
for (size_t i = index ; i >= 0 ; --i)
free(courses[index]);
free(courses);
return NULL;
}
/* always remember to `null' terminate */
courses[index][length] = '\0';
/* finally, copy the string */
memcpy(courses[index], head, length);
/* check whehter it was the last field and
* update the pointer to the next one accordingly
*/
if ((tail != NULL) && (*tail != '\0'))
head = tail + 1;
else
head = NULL;
/* Don't forget the fields counter */
index++;
}
courses[count] = NULL;
return courses;
}
void
concatenate_lists(struct student_info *info, char **source)
{
char **temporary;
size_t length;
length = info->size;
for (size_t i = 0 ; source[i] != NULL ; ++i)
length++;
temporary = realloc(info->courses, length * sizeof(*temporary));
if (temporary == NULL)
return;
for (size_t i = 0 ; source[i] != NULL ; ++i)
temporary[i + info->size] = strdup(source[i]);
info->courses = temporary;
info->size = length;
}
void
free_list(char **lines)
{
if (lines == NULL)
return;
for (size_t i = 0 ; lines[i] != '\0' ; ++i)
free(lines[i]);
free(lines);
}
int
main()
{
struct student_info info;
char **lines;
lines = load_file("data.tx");
if (lines == NULL)
return -1;
info.courses = NULL;
info.size = 0;
for (size_t i = 0 ; lines[i] != NULL ; ++i)
{
char **courses;
courses = extract_courses_as_list(lines[i]);
if (courses == NULL)
continue;
concatenate_lists(&info, courses);
free_list(courses);
}
for (size_t i = 0 ; i < info.size ; ++i)
{
fprintf(stderr, "%s\n", info.courses[i]);
free(info.courses[i]);
}
free(info.courses);
free_list(lines);
return 0;
}
You will notice that I never used strdup(), the reason being that the length of the string that we want to copy is always known.

curious segmentation faults

I wrote a program that worked well without any errors.
I read a text file and write the contents to an array (line by line and seperat strings in this line one by one). Then I added a second textfile and again tried to read the content but then I got some crazy segmentation faults that are only reported when I debug my program.
This is the part of the program that causes the error:
vrFile = fopen("file.txt","r");
//some temporary arrays
float taz[5000], tel[5000], ttime[5000], tvr[5000][nbins], tdbz[5000][nbins];
int size = 1024, pos;
int c;
//buffer for the line to read
char *buffervr = (char *)malloc(size);
int lin=0;
if(vrFile) {
do { // read all lines in file
pos = 0;
do{ // read one line
c = fgetc(vrFile);
if(c != EOF) buffervr[pos++] = (char)c;
if(pos >= size - 1) { // increase buffer length if line is too long - leave room for 0
size *=2;
buffervr = (char*)realloc(buffervr, size);
}
}while(c != EOF && c != '\n');
buffervr[pos] = 0;
// line is now in buffer
char *ptr;
ptr = strtok(buffervr,"\t");
int abs=1;
while(ptr != NULL) {
if(abs==1){
taz[lin] = atof(ptr);
}
else if(abs==2) {
tel[lin] = atof(ptr);
}
else if (abs==3) {
ttime[lin] = atof(ptr);
}
else {
tvr[lin][abs-4]=atof(ptr);
}
abs++;
ptr = strtok(NULL, "\t");
}
lin++;
} while(c != EOF);
fclose(vrFile);
}
//free(buffervr);
The first thing is that I get a segmentation fault when I try to allocate a buffer char *buffervr = (char *)malloc(size);. When I do this before I allocate the arrays float taz[5000], tel[5000], ttime[5000], tvr[5000][nbins], tdbz[5000][nbins]; it works?!
The second thing is that I cant read the file with c = fgetc(vrFile); although I can open it. Here I get another segmentation fault?!
Can someone tell me that went wrong?
Thank you!
EDIT: actual code:
int size = 1024, pos;
int c, cdbz;
char *buffervr = (char *)malloc(size);
char *bufferdbz = (char *)malloc(size);
int lin=0;
char *taz, *tel, *ttime, **tvr, **tdbz;
taz=(char *) malloc(5000*sizeof(char));
tel=(char *) malloc(5000*sizeof(char));
ttime=(char *) malloc(5000*sizeof(char));
tvr = malloc(5000 * sizeof(char *));
int i;
for(i = 0; i < 5000; i++) {
tvr[i] = malloc(nbins * sizeof(char));
}
tdbz = malloc(5000 * sizeof(char *));
for(i = 0; i < 5000; i++) {
tdbz[i] = malloc(nbins * sizeof(char));
}
//float taz[5000], tel[5000], ttime[5000], tvr[5000][nbins], tdbz[5000][nbins];
if(vrFile!=NULL) {
do { // read all lines in file
pos = 0;
do{ // read one line
c = fgetc(vrFile);
if(c != EOF) buffervr[pos++] = (char)c;
if(pos >= size - 1) { // increase buffer length - leave room for 0
size *=2;
buffervr = (char*)realloc(buffervr, size);
}
}while(c != EOF && c != '\n');
buffervr[pos] = 0;
// line is now in buffer
char *ptr;
ptr = strtok(buffervr,"\t");
int abs=1;
while(ptr != NULL) {
if(abs==1){
taz[lin] = (ptr);
}
else if(abs==2) {
tel[lin] = (ptr);
}
else if (abs==3) {
ttime[lin] = (ptr);
}
else {
tvr[lin][abs-4]=(ptr);
}
abs++;
ptr = strtok(NULL, "\t");
}
lin++;
} while(c != EOF);
fclose(vrFile);
}
free(buffervr);
.
.
.
int lins,abss;
for (lins=0; lins<lin; lins++)
{
time[0]=ttime[lins];
az[0]=taz[lins];
el[0]=tel[lins];
for (abss=0; abss<nbins; abss++)
{
vr[abss]=tvr[lins][abss];
//dbZ[abss]=tdbz[lins][abss];
}
}
note: time, lat, lon, az, el and vr are pointers.
EDIT: I solved my Problem this way:
vrFile = fopen("file.txt","r");
if (vrFile != NULL){
while ((getline(&line, &len, vrFile)) != -1) {
NumberOfLines++;
if(NumberOfLines == 1){
line = strtok(line, "\t");
while(line != NULL){
NumberOfDoubles++;
line = strtok(NULL, "\t");
}
}
}
}
rewind(vrFile);
int i,j;
float* taz;
float* tel;
float* ttime;
float** tvr;
float** tdbz;
//1D Arrays
taz = (float*) malloc (sizeof(float)*NumberOfLines);
tel = (float*) malloc (sizeof(float)*NumberOfLines);
ttime = (float*) malloc (sizeof(float)*NumberOfLines);
//2D Arrays
tvr = (float**) malloc (sizeof(float*)*NumberOfLines);
for(i=0;i<NumberOfLines;i++){
tvr[i] = (float*) malloc (sizeof(float)*NumberOfDoubles);
}
tdbz = (float**) malloc (sizeof(float*)*NumberOfLines);
for(i=0;i<NumberOfLines;i++){
tdbz[i] = (float*) malloc (sizeof(float)*NumberOfDoubles);
}
if (vrFile != NULL){
i = 0;
while ((getline(&line, &len, vrFile)) != -1) {
line = strtok(line, "\t");
j = -3;
while(line != NULL){
switch(j){
case -3: taz[i] = strtod(line, NULL);break;
case -2: tel[i] = strtod(line, NULL);break;
case -1: ttime[i] = strtod(line, NULL);break;
default: tvr[i][j] = strtod(line, NULL);break;
}
line = strtok(NULL, "\t");
j++;
}
i++;
}
}

Dynamic array of strings

I have to dynamically allocate array of words. Words are stored in a file separated by variable count of white-space characters. I don't know how many words is in the file a they can have variable length.
I have this code:
void readWord(FILE* stream, char *word, char first_c) {
word[0] = first_c;
char val;
int wlen = 1;
// isWhitespac is my function - tests if char is blank or '\n'
while ((val = fgetc(stream)) != EOF && isWhitespace(val) == 0) {
wlen++;
word = realloc(word, (wlen+1) * sizeof (char));
word[wlen-1] = val;
}
word[wlen] = '\0';
}
int readList(const char *file) {
FILE* f;
char **arr;
char val;
int wcount = 0;
arr = malloc(sizeof (char*));
f = fopen(file, "r");
while (fscanf(f, " %c", &val) == 1) {
wcount++;
arr = realloc(arr, wcount * sizeof (char *));
arr[wcount - 1] = malloc(sizeof (char));
readWord(f, arr[wcount-1], val);
printf("%s\n", arr[wcount-1]);
}
for (int i = 0; i < wcount; ++i) {
free(arr[i]);
}
free(arr);
fclose(f);
return 0;
}
It appears to work fine, it reads a prints all the words. But when I run the program with Valgrind the are too many errors, which I can't find. Could anyone help me? (I know I have to test if malloc and others went fine, it is just a test func.)
The Valgrind log is quite long, should I post it too?
One of the issues is that you do realloc inside readWord. If realloc allocates a new buffer and doesn't just extend the current one then your code will crash (you will double free the pointer) and this is what Valgrind picks up. To fix this I would rewrite the code so it returns a pointer instead of void.
char * readWord(FILE* stream, char *word, char first_c) {
word[0] = first_c;
char val;
int wlen = 1;
// isWhitespac is my function - tests if char is blank or '\n'
while ((val = fgetc(stream)) != EOF && isWhitespace(val) == 0) {
wlen++;
word = realloc(word, (wlen+1) * sizeof (char));
word[wlen-1] = val;
}
word[wlen] = '\0';
return word;
}
And then change the loop in readList to this:
while (fscanf(f, " %c", &val) == 1) {
wcount++;
arr = realloc(arr, wcount * sizeof (char *));
arr[wcount-1]=malloc(sizeof(char));
arr[wcount - 1] = readWord(f, arr[wcount-1], val);
printf("%s\n", arr[wcount-1]);
}

Resources