I have an array (POINTS) with a length >20M of the following structure (point) and would like to save it in a binary file to read it afterwards in a different program:
typedef struct {
char *name;
unsigned char namelength;
} point;
if((POINTS = (point *) malloc (N * sizeof(point))) == NULL){
printf("when allocating memory for the points vector\n");
exit(0);
}
What I'm doing is the following; First I'm creating a variable namestot to know how many characters I have in total (some names may be empty):
for (i = 0; i < N; i++) namestot += POINTS[i].namelength;
Then I'm writing the array in a file:
if ((fin = fopen(name, "wb")) == NULL){
printf("the output binary data file cannot be open\n");
exit(0);
}
for ( i=0; i < N; i++){
if (POINTS[i].namelength){
if( fwrite(point[i].name, sizeof(char), POINTS[i].namelength, fin) != POINTS[i].namelength){
printf("when writing names to the output binary data file\n");
exit(0);
}
}
}
With that I create the file without issues. Now we change to another program with the binary file saved, so we open and read it:
if ((fin = fopen (argv[1], "r")) == NULL){
printf("the data file does not exist or cannot be opened\n");
exit(0);
}
//Here we allocate memory again for the POINTS vector
if((POINTS = (point *) malloc (N * sizeof(point))) == NULL){
printf("when allocating memory for the points vector\n");
exit(0);
}
if((allnames = (char *) malloc (namestot * sizeof(char))) == NULL){
printf("when allocating memory for the names vector");
exit(0);
}
if (fread(allnames, sizeof(char), namestot, fin) != namestot){
printf("when reading names from the binary data file\n");
exit(0);
}
//Setting pointers to names
for (i = 0; i < N; i++){
if(POINTS[i].namelength > 0){
POINTS[i].name = allnames;
allnames += POINTS[i].namelength;
}
}
However, when I try to read the first non-empty name the program prints the entire array of names, any advice on how to set the pointers to the mesan to have the POINTS array as I had it in the first program?
Thanks!
PS: if there's any non-declared variable I've probably forgot to paste it here, that's not the issue.
You are writing variable length strings to files, so when you read it you need to know length of individual string
You may decide to include length string along with the string to write in the file or ensure you write trailing '\0' to indicate earlier string ends and new one begins from next byte, your reader program will have to parse the strings accordngly
prints the entire array of names
Sounds like because you do not tell your program the end of each name, print char * stop when meet a \0 .
Related
How can I retrieve an array of floating-point values from a file in C? This is the code I have used so far, but I am running into a segmentation fault(marked in my code where). If you see a less painful way of doing this that would be helpful as well.
The values are stored in the file with a single space after each value like so:
-667.0897114275529 544.6798599456312 -148.0586015260273 -323.4504101541069 .
// open file
FILE *fp;
fp = fopen(sig_file, "r");
if (fp == NULL){
printf("File opened incorrectly or is empty");
return 1;
}
// find file size
fseek(fp, 0L, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
// store file contents to f_contents
char *f_contents = 0;
f_contents = malloc(sz);
if (f_contents){
fread(f_contents, 1, sz, fp);
}
fclose(fp);
if (f_contents){
// find how many points are in the file
long pt_count = 0;
int i;
for (i=0; i<sz; i++){
if (f_contents[i] == ' '){
pt_count++;
}
}
// store points to a float array
double signal[pt_count];
char *pt;
pt = strtok(f_contents, " ");
// seg fault 11:
if (pt == NULL){
printf("error with pt");
return 1;
}
signal[0] = atof(pt);
//
for (i=1; i<pt_count; i++){
pt = strtok(NULL, " ");
signal[i] = atof(pt);
}
}
free(f_contents);
If you see a less painful way of doing this that would be helpful as well.
I haven't studied your code enough to identify the source of the segfault, because it should just be rewritten anyway. I might be inclined to do something more like this:
// open file
FILE *fp = fopen(sig_file, "r");
if (fp == NULL){
perror("fopen");
return 1;
}
size_t sz = 0;
for (int c = getc(fp); c != EOF; c = getc(fp)) {
if (c == ' ') sz++;
}
rewind(fp);
double signal[sz];
for (size_t i = 0; i < sz; i++) {
if (fscanf(fp, "%lf", &signal[i]) != 1) {
fclose(fp);
fputs("I/O error, malformed input, or premature EOF\n", stderr);
return 1;
}
}
fclose(fp);
If the file is long, then it might be worthwhile to read it all in one pass instead of counting elements first (and especially instead of slurping the entire file into memory). You can achieve such a one-pass read by storing the values in a more flexible data structure: some variety of linked list, or a dynamically allocated (and reallocated as necessary) array.
A linked list would need to be postprocessed into array form, but you might be able to use a dynamically allocated array as-is. You would need to use either a dynamically allocated array or a large-enough fixed-size array with a C implementation that does not support variable-length arrays, such as MS Visual Studio.
The first argument to strtok() must be a null-terminated string. fread() doesn't add a null terminator, and even if it did, you didn't allocate enough space for f_contents to hold it.
So use this code to allocate and fill in f_contents
f_contents = malloc(sz+1);
if (f_contents){
fread(f_contents, 1, sz, fp);
f_contents[sz] = '\0';
}
fclose(fp);
I am trying to use C to read a large text file (say, a novel with more than ten thousand words) with fscanf, but each time the program will read until a certain point and seemingly reached EOF, while it is way far away from end of file.
Here is my code:
void putWordsInArray(){
FILE* fileToRead;
char words[1024];
int singleSize = 0;
singleWordArray = (char**)malloc(1024*sizeof(char*));
indexArray1 = (int*)malloc(1024*sizeof(int));
//read the temp file automatically generated
fileToRead = fopen("temp.txt", "r");
while(fscanf(fileToRead, "%s", words) != EOF){
printf("%s\n", words);
//the following if-else statement add the word read to the single word array
//if they are not yet in the array, or increment its count by 1 isf its already there
int result = -1;
result = contains(words, singleWordArray, singleSize);
if(result == -1){
indexArray1 = (int*)realloc(indexArray1, (singleSize + 1) * sizeof(int));
singleWordArray = (char**)realloc(singleWordArray, (singleSize + 1) * sizeof(char*));
singleWordArray[singleSize] = (char*)calloc((strlen(words) + 1), 1024*sizeof(char));
strcpy(singleWordArray[singleSize], words);
indexArray1[singleSize] = 1;
singleSize++;
}
else{
indexArray1[result] += 1;
}
//check if the current list contains the incoming word or not
}
}
Does anyone know what had happened here? I believe I have allocated enough memory each time. Thanks to whoever helps.
I'm trying to read the files in a directory and store each file name in an array of strings. I can't get it to work for the life of me. Here's the function:
char *readFile(char *dir){
char *fileStringArray[1000];
DIR *dirPointer;
int file_count = 0;
struct dirent *file;
struct stat fileType;
int i = 0;
if ((dirPointer = opendir(dir)) == NULL){
printf("Directory not found, try again\n");
return NULL;
}else{
printf("Reading files in directory\n");
while((file = readdir(dirPointer)) != NULL){ //iterate through contents of directory
stat(dir, &fileType);
if(i > 1){ //ignore . and .. directories that appear first
file_count++;
printf("%s\n", file->d_name);
strcpy(fileStringArray[i-2], file->d_name); //crashes, replace
//with [i] to not crash, but i-2 makes more sense to me
//fileStringArray[i-2] = &file->d_name; alternate idea
}
else{
i++;
}
}
int j;
for(j = 0; j < file_count; j++){
printf(":::%s\n", fileStringArray[j]); //print the string array
}
}
printf("Done reading\n\n");
closedir(dirPointer);
return dir;
}
There are two issues with your code. Main one is that you try to store your strings in 1000 element array of pointers to chars. Pointer to char is not enough to store string, it actually need to point to some memory. You can solve it on many ways, consider changing your strcpy function to strdup - which will allocate memory for you. Or you need to change your fileStringArray to be array of array's of chars (char fileStringArray[1000][100]).
Second issue is with that i , you should increment it unconditionally if you actually want to advance in your array.
Also, it would be nice if you could post full example, so I don't have to guess headers to include.
I have to implement an application where user passes multiple words via command line and the application finds count of the word in each line of file. Each word will search the file in its own thread.
So far I have implemented it as single threaded app.
The code looks like:
//Below function reads file line and returns it
char* readLine(FILE* file, char* line)
{
if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}
int maximumLineLength = 128;
char *lineBuffer = (char *) malloc(sizeof(char) * maximumLineLength);
if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}
char ch = getc(file);//Get each character
int count = 0;
//loop for line or EOF
while ((ch != '\n') && (ch != EOF))
{
if (count == maximumLineLength)
{
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL)
{
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;
ch = getc(file);
}
lineBuffer[count] = '\0';//Add null character
line = (char *) malloc(sizeof(char) * (count + 1));
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
return line;
}
//Below function finds the occurance of
//word in the line
//Need to refine to take into consideration
//scenarios such that {"Am"," am "," am","?Am",".Am"}etc
int findWord(char* line,char* word)
{
int count=0;
int lineLen = strlen(line);
int wordLen = strlen(word);
char* temp= (char *) malloc(sizeof(char) * (lineLen+1));
strcpy(temp,line);
while(true)
{
if( strstr(temp,word) == NULL)
break;
strcpy(temp, strstr(temp,word));
// printf("##%s\n",temp);
strcpy(temp,temp+wordLen+1);
// printf("##%s\n",temp);
count++;
}
//printf("%d\n",count);
free(temp);
return count;
}
//Below function fills the linked list for data structure lineCount
//with word occurance statistics
//line by line and the total
//The number of elements in the list would be number of lines in the
//file
LineCount* findCount(FILE* file, char* word,LineCount** lineCountHead)//Make it multithreaded fn()
{
LineCount* lineHead= NULL;
char* line = NULL;
int lineNumber=1;
int count=0;
if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}
while (!feof(file)) {
LineCount* temp=NULL;
line = readLine(file, line);
//printf("%s\n", line);
count=findWord(line,word);
//Critical Section Start
temp=LineCountNode(lineNumber,count);
addToLineCountList(temp,lineCountHead);
//Criticla Section End
lineNumber++;
}
free(line);
return lineHead;
}
So basically I want my calling thread function to be LineCount* findCount(FILE* file, char* word,LineCount** lineCountHead)
My understanding is that, the file will be accessed - only for read purpose by the threads, so no need to take care of synchronization.
Currently I am opening the file as:
pFile = fopen (argv[1],"r");. My question is how do I open in read shared mode ?
I know in C++ there exists a read shared mode. How to achieve this in c?
Also how do I write my function LineCount* findCount(FILE* file, char* word,LineCount** lineCountHead) in the form required by thread call function i.e. the form void* fn(void*)
While in read-only mode there are no issues with the file itself, the IO functions in the standard C library are not designed to be usable from multiple threads in parallel. They are thread-safe (or at least, I think so) but using them correctly from multiple threads is not trivial.
At the lowest level, each FILE structure contains a file position pointer - or the IO functions maintain an OS-provided pointer. Having multiple threads mess with the file cursor position sounds like a good way to make your life more difficult than it should be.
The best approach would be to open your file multiple times - once in each thread. Each thread would then have its own FILE pointer, stream buffer etc. Note that this is not unique to C & POSIX threads - its an inherent issue with using multiple threads.
In any case, I am unsure what you are trying to achieve by using multiple threads. Generally search operations like this are I/O bound - multithreaded accesses to the same file are quite likely to make things worse.
The only case where it might make sense is if you had a huge amount of strings to search for and you had a single I/O thread feeding all other threads through a common buffer. That would distribute the CPU-intensive part, without causing undue I/O...
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);
}