I am novice to C programming and I have written a code to a requirement specification but I am consistently getting Segmentation Fault and unable to proceed ahead.
If the file name is 'code.c' and it runs with an error of not passing the argument (filename). But if the filename is passed, we land in Segmentation Fault.
Any help/suggestions will be appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
struct _data
{
char *firstName;
char *lastName;
long number;
};
// SCAN FILE
int SCAN(FILE *(*stream))
{
*stream = fopen("inputFile.data", "r");
int ch = 0, lines = 0;
while (!feof(*stream))
{
ch = fgetc(*stream);
if (ch == '\n')
{
lines++;
}
}
return lines;
}
// LOAD FILE
struct _data *LOAD(FILE *stream, int size)
{
int i;
size_t chrCount;
char *text, *number, *firstName, *lastName;
struct _data *BlackBox;
if ((BlackBox = (struct _data*)calloc(size, sizeof(struct _data))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
rewind(stream);
for (i = 0; i < size; i++)
{
getline(&text, &chrCount, stream);
firstName = strtok(text, " ");
lastName = strtok(text, " ");
number = strtok(NULL, "\n");
// Allocate memory for name part of struct.
if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName), sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName), sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
strcpy(BlackBox[i].firstName, firstName);
strcpy(BlackBox[i].lastName, lastName);
BlackBox[i].number = atol(number);
}
fclose(stream);
return BlackBox;
}
void SEARCH(struct _data *BlackBox, char *name, int size, int inputs)
{
int i;
int found = 0;
char *search = " ";
char *firstName;
char *lastName;
if (inputs == 2)
{
firstName = strtok(name, search);
lastName = strtok(NULL, search);
}
printf("*******************************************\n");
if (inputs == 2)
{
for (i = 0; i < size; i++)
{
if (!strcasecmp(firstName, BlackBox[i].firstName) && !strcasecmp(firstName, BlackBox[i].firstName))
{
printf("The name was found at the %d entry.\n", i);
found = 1;
break;
}
}
}
else
{
for (i = 0; i < size; i++)
{
if (!strcasecmp(firstName, BlackBox[i].firstName) || !strcasecmp(firstName, BlackBox[i].firstName))
{
printf("The name was found at the %d entry.\n", i);
found = 1;
break;
}
}
}
if (found == 0)
{
printf("The name was NOT found.\n");
}
printf("*******************************************\n");
}
// FREE MEMORY
void FREE(struct _data *BlackBox, int size)
{
int i;
for (i = 0; i < size; i++)
{
free(BlackBox[i].firstName);
free(BlackBox[i].lastName);
}
free(BlackBox);
BlackBox = NULL;
}
// MAIN
int main(int argv, char **argc)
{
int size;
FILE *stream;
struct _data *BlackBox;
// argv == 1 WORKS, Below message is printed.
if (argv == 1)
{
printf("*******************************************\n");
printf("* You must include a name to search for. *\n");
printf("*******************************************\n");
}
// argv == 2 DOES NOT WORK, Segmentation Fault.
if (argv == 2)
{
size = SCAN (&stream);
BlackBox = LOAD(stream, size);
SEARCH(BlackBox, argc[1], size, 1);
}
if (argv == 3)
{
size = SCAN(&stream);
BlackBox = LOAD(stream, size);
SEARCH(BlackBox, argc[2], size, 2);
}
return 0;
}
You have a problem in this code:
firstName = strtok(text, " ");
lastName = strtok(text, " ");
number = strtok(NULL, "\n");
...
BlackBox[i].number = atol(number);
The second strtok() call should pass NULL as its first argument. As it is, the third strtok() call is certain to return NULL because the first call modifies text in such a way that the second consumes the whole thing (when tokenizing again from the beginning, as it erroneously does). You do not test for that, however, and as a result, atol() attempts to dereference a null pointer.
Update:
Additionally, as #chqrlie and later #JamesWilkins observed, you do not allocate sufficient space for BlackBox[i].firstName and BlackBox[i].lastName, as you need room for the string terminators as well. This is an entirely separate problem that could also produce a segfault. I like #chqrlie's suggestion to switch to strdup(), but it would be sufficient to just increase each allocation by one unit.
Update 2:
Furthermore, you have an issue with this line:
getline(&text, &chrCount, stream);
You do not initialize variable text before the first call, so it contains a junk value. The function allocates a buffer only when its first argument points to a NULL pointer; otherwise it writes the line to the buffer pointed to by the pointer obtained by dereferencing the first argument. Writing to a random location in memory certainly produces undefined behavior, which in practice often manifests as a segfault.
Moreover, unless you can rely on no line of the file being longer than the first, you also need to free the text pointer at the end of each loop iteration AND reset its value to NULL, so that getline() allocates a fresh buffer on the next iteration. If you do not free it on each iteration, then you need instead to free it after the end of the loop; else you will leak memory.
Try this (though I'm using Visual Studio on Windows). I added code to check for a missing '\n' on the last line, and also allowed for a variable number of search terms. I also increased the memory allocation for strings by 1 to account for the null terminating character. I noticed you are using getline(const char*..., which I think is GNU (Linux?), so I change that to fgets() just so I could compile and test it in VS (so you can change it back if you like). I put in various null checks as well, to be safer.
#include <iostream>
using namespace std;
struct _data
{
char *firstName;
char *lastName;
long number;
};
// SCAN FILE
int SCAN(FILE *(*stream))
{
*stream = fopen("inputFile.data", "r");
if (*stream == NULL)
{
perror("Error opening file");
return 0;
}
char ch = 0, lines = 0, linesize = 0;
while ((ch = fgetc(*stream)) != EOF)
{
if (ch == '\n')
{
lines++;
linesize = 0;
}
else linesize++;
}
if (linesize > 0)
lines++; // (last line doesn't have '\n')
return lines;
}
// LOAD FILE
struct _data *LOAD(FILE *stream, int lineCount)
{
int i;
size_t chrCount = 256;
char text[256], *result, *number, *firstName, *lastName;
struct _data *BlackBox;
if ((BlackBox = (struct _data*)calloc(lineCount, sizeof(struct _data))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
else memset(BlackBox, 0, sizeof(struct _data) * lineCount); // (make sure all data members are null to begin)
rewind(stream);
for (i = 0; i < lineCount; i++)
{
result = fgets(text, chrCount, stream);
if (result == NULL)
break; // (EOF)
firstName = strtok(text, " ");
lastName = strtok(NULL, " ");
number = strtok(NULL, "\n");
// Allocate memory for name part of struct.
if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName) + 1, sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName) + 1, sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
strcpy(BlackBox[i].firstName, firstName);
strcpy(BlackBox[i].lastName, lastName);
BlackBox[i].number = atol(number);
}
fclose(stream);
return BlackBox;
}
void SEARCH(struct _data *BlackBox, char **names, int lineCount, int inputs)
{
int i, l;
int found = 0;
printf("*******************************************\n");
for (i = 0; i < inputs; ++i)
{
for (l = 0; l < lineCount; ++l)
{
if (BlackBox[l].firstName != NULL && !_stricmp(names[i], BlackBox[l].firstName)
|| BlackBox[l].lastName != NULL && !_stricmp(names[i], BlackBox[l].lastName))
{
printf("The name was found on line %d.\n", 1 + l);
found = 1;
break;
}
}
if (found) break;
}
if (!found)
printf("The name was NOT found.\n");
printf("*******************************************\n");
}
// FREE MEMORY
void FREE(struct _data *BlackBox, int lineCount)
{
int i;
for (i = 0; i < lineCount; i++)
{
if (BlackBox[i].firstName != NULL)
free(BlackBox[i].firstName);
if (BlackBox[i].lastName != NULL)
free(BlackBox[i].lastName);
}
free(BlackBox);
}
// MAIN
int main(int argc, char **argv)
{
int lineCount;
FILE *stream;
struct _data *BlackBox;
// argc == 1 WORKS, Below message is printed.
if (argc == 1)
{
printf("*******************************************\n");
printf("* You must include a name to search for. *\n");
printf("*******************************************\n");
}
// argc == 2 DOES NOT WORK, Segmentation Fault.
if (argc > 1)
{
lineCount = SCAN(&stream);
if (lineCount > 0)
{
BlackBox = LOAD(stream, lineCount);
SEARCH(BlackBox, argv + 1, lineCount, argc - 1);
FREE(BlackBox, lineCount);
}
}
return 0;
}
Tested it on the command line, and it works.
The problem is the argv and argc. argc is supposed to be an int (think argument count), while argv is meant to be char**. You have them mixed up in your main.
Related
I'm trying to find all the words with capital letters in a string, but am unable to process my data structure. i seem to be able to print out fileContent, indicating that it is loading in successfully, but my second function is not working on the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* loadFile(char* fileName)
{
FILE *inputFile;
inputFile = fopen(fileName, "r");
//finds the end of the file
fseek(inputFile, 0, SEEK_END);
//stores the size of the file
int size = ftell(inputFile);
//Sets the scan to the start of the file
fseek(inputFile, 0, SEEK_SET);
char *documentStore = (char*)malloc(size);
int i = 0, c;
while((c = fgetc(inputFile)) != EOF)
{
documentStore[i] = c;
i++;
}
return documentStore;
}
void countImportantWords(char* fileContent, char** importantWords, int* frequencyWords)
{
int uniqueWordCount = 0;
int lengthWordStore = 10;
int i = 0;
int recording = 0;
char wordBuffer[50];
int wordBufferCount = 0;
int isWordPresent = 0;
while(fileContent[i] != EOF)
{
//To allocate more memory incase the structure is full
if(uniqueWordCount == lengthWordStore)
{
lengthWordStore += 10;
char** newWordStore = realloc(importantWords, lengthWordStore * sizeof(char*));
int* newFrequencyStore = realloc(frequencyWords, sizeof(int));
importantWords = newWordStore;
frequencyWords = newFrequencyStore;
}
printf("%s", wordBuffer);
//Conditions to fill if its a word
if(fileContent[i] >= 'A' && fileContent[i] <= 'Z' && recording == 0)
{
wordBuffer[0] = fileContent[i];
recording = 1;
}else if(fileContent[i] >= 'a' && fileContent[i] <= 'z' && recording == 1)
{
//each if is to check if the end of word is reached. Any character that is non alphabetical is considered end of word
wordBufferCount += 1;
wordBuffer[wordBufferCount] = fileContent[i];
} else if (fileContent[i] >= 'A' && fileContent[i] <= 'Z' && recording == 1)
{
wordBufferCount += 1;
wordBuffer[wordBufferCount] = fileContent[i];
} else {
//Adding a terminating character so that it strcpy only copies until that point
wordBuffer[wordBufferCount + 1] = '\0';
recording = 0;
//check to see if that word is in the array already, and if it is, it will just increment the frequency
for(int j = 0; j < uniqueWordCount; j++){
if(strcmp(wordBuffer, importantWords[j]) == 0)
{
frequencyWords[j] += 1;
isWordPresent = 1;
}
}
//if its not present, it should assign it to the structure
if(isWordPresent == 0)
{
char* wordStore = (char*)malloc(wordBufferCount * sizeof(char));
strcpy(wordStore, wordBuffer);
uniqueWordCount += 1;
importantWords[uniqueWordCount] = wordStore;
frequencyWords[uniqueWordCount] = 1;
}
}
i++;
}
}
int main() {
char fileName[50];
char *fileContent;
char **importantWords = (char**)malloc(10*sizeof(char**));
int *frequencyWords = (int*)malloc(10*sizeof(int));
printf("Please input the full file path: ");
scanf("%s", fileName);
fileContent = loadFile(fileName);
countImportantWords(fileContent, importantWords, frequencyWords);
int i = 0;
while(importantWords[i] != '\0')
{
printf("%s %d", importantWords[i], frequencyWords[i]);
i++;
}
return 0;
}
I've put in the full file so you can see how the structure was created incase that it is the issue, but ideally what would happen is that the final loop would print out all the words that are important and they're frequency. Currently i'm getting exit code 11, which i'm not sure what it means, but may be worth mentioning. I'd really appreciate any help :)
You can simplify the process dramatically but utilising functions and learning to manage your memory. I wrote a short example which does not take punctuation into account. It just assumes every word is separated by a space, which you can customise to your discretion.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char* readfile(char* filename){
char* data = NULL;
FILE* file = fopen(filename, "r");
if(file == NULL){
return NULL;
}
fseek(file, 0, SEEK_END);
long size = ftell(file)+1;
fseek(file, 0, SEEK_SET);
data = (char*)malloc(size);
if(data == NULL){
return NULL;
}
fgets(data, (int)size, file);
return data;
}
typedef struct uppercase_t{
char** word;
int count;
}uppercase;
void copy(uppercase* u,char* token){
size_t length = strlen(token);
u->word[u->count] = (char*)malloc(length+1);
if(u->word[u->count] == NULL){
return;
}
strcpy(u->word[u->count], token);
++u->count;
}
void createuppercasedata(uppercase* u, char* data){
const char delimeter[] = " ";
char* token = strtok(data, delimeter);
if(token == NULL){
return;
}
u->word = (char**)malloc(u->count+1);
if(u->word == NULL){
return;
}
if(isupper(token[0])){
copy(u,token);
}
while(token != NULL){
token = strtok(0, delimeter);
if(token != NULL)
if(isupper(token[0])) {
char** reallocated = (char**)realloc(u->word, u->count+1);
if(reallocated == NULL){
return;
}
u->word = reallocated;
copy(u, token);
}
}
}
void destroyuppercasedata(uppercase* u){
for(int index = 0; index < u->count; ++index){
free(u->word[index]);
}
free(u->word);
}
int main(){
char filename[] = "textfile";
char* data = readfile(filename);
if(data == NULL){
return -1;
}
uppercase u = {0};
createuppercasedata(&u, data);
printf("found %i uppercase words\n",u.count);
for(int index = 0; index < u.count; ++index){
printf("%s\n", u.word[index]);
}
destroyuppercasedata(&u);
free(data);
}
The code will allocate a new pointer for each uppercase and memory for the word to be copied too. It will free all the memory it allocated in the structure with destroyuppercasedata and it will free the initial data that was read from file. Error checking and memory management in C is really important. So utilise those properly.
This was the test file I used.
textfile
How many Uppercase words can Be Found In this text File the answer should be Seven
And this was the output to the terminal:
How
Uppercase
Be
Found
In
File
Seven
I am trying to read strings and integers from a simple text file to my array. But the problem is that I get some random characters in a line in the middle of my list. It probably has to do with a newline problem, but I am not sure. The text file looks like this:
4
Mr Tambourine Man
Bob Dylan
1965
Dead Ringer for Love
Meat Loaf
1981
Euphoria
Loreen
2012
Love Me Now
John Legend
2016
The first number (4), indicates how many songs there are in the list. I have made a struct which will be able to hold the songs and dynamically allocate memory for each pointer.
Struct:
typedef struct Song {
char *song;
char *artist;
int *year;
} Song;
Allocated:
Song *arr;
arr = (Song*)malloc(sizeof(Song));
Function:
int loadFile(char fileName[], Song *arr, int nrOf) {
FILE *input = fopen(fileName, "r");
if (input == NULL) {
printf("Error, the file could not load!\n");
} else {
int i = 0;
fscanf(input, "%d\n", &nrOf);
for (int i = 0; i < nrOf; i++) {
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
arr[i].year = (int*)malloc(sizeof(int));
fgets(arr[i].song, 100, input);
fgets(arr[i].artist, 100, input);
fscanf(input, "%d\n", arr[i].year);
}
printf("The file is now ready.\n");
fclose(input);
}
return nrOf;
}
Are you able to find the problem? Or do you have a better solution?
This is wrong:
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
You are only allocating buffers of size 1, there's no scaling. This gives you undefined behavior when you overrun the buffers by loading more data into them than they can hold.
I would expect those to read:
arr[i].song = malloc(100);
and so on. Note that no cast is necessary, and sizeof (char) is always 1.
Also, this:
arr[i].year = (int*)malloc(sizeof(int));
is super-strange. There's absolutely no reason to dynamically allocate a single integer, just make the field an int and store the value there directly.
First Issue:
arr[i].song = (char*)malloc(sizeof(char));
arr[i].artist = (char*)malloc(sizeof(char));
Are only allocating 1 byte for your char* pointers, song and artist. You can allocate a size for this:
arr[i].song = (char*)malloc(100 * sizeof(char)); /* or malloc(100) */
arr[i].artist = (char*)malloc(100 * sizeof(char));
Or you can simply malloc() enough space from you buffer:
char buffer[100];
fgets(buffer, 100, input);
/* check for failure, remove newline */
arr[i].song = malloc(strlen(buffer)+1);
/* check error from malloc */
strcpy(arr[i].song, buffer);
Or even use strdup():
arr[i].song = strdup(buffer);
Which is a substitute for malloc()/strcpy().
Note: You can also read Do I cast the result of malloc?.
Second Issue:
Your current struct:
typedef struct Song {
char *song;
char *artist;
int *year;
} Song;
Can be simplified to:
typedef struct {
char *song;
char *artist;
int year;
} Song;
Because year does not need to be a pointer. Easier to manage if its just an int. This avoids having to do allocations like:
arr[i].year = (int*)malloc(sizeof(int));
Other Recommendations:
You should check the return of fscanf() and fgets() as its safe to do this. It helps just incase your file will have incorrect data. This goes the same for malloc(), which can return NULL is unsuccessfully allocated on the heap.
Here is some code with the above considerations in mind:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100
typedef struct {
char *song;
char *artist;
int year;
} Song;
Song *create_array(FILE *input, int *nrof);
void load_data(Song *arr, FILE *input, int nrof);
void print_free_data(Song *arr, int nrof);
void get_buffer(char buffer[], FILE *input);
int main(void) {
FILE *input;
Song *arr;
int nrof;
input = fopen("artist.txt", "r");
if (input == NULL) {
fprintf(stderr, "Error reading file\n");
exit(EXIT_FAILURE);
}
arr = create_array(input, &nrof);
load_data(arr, input, nrof);
print_free_data(arr, nrof);
fclose(input);
return 0;
}
Song *create_array(FILE *input, int *nrof) {
Song *arr;
if (fscanf(input, "%d ", nrof) != 1) {
fprintf(stderr, "Cannot find number of songs\n");
exit(EXIT_FAILURE);
}
arr = malloc(*nrof * sizeof(*arr));
if (arr == NULL) {
fprintf(stderr, "Cannot allocate %d spaces for array\n", *nrof);
exit(EXIT_FAILURE);
}
return arr;
}
void load_data(Song *arr, FILE *input, int nrof) {
char buffer[SIZE];
for (int i = 0; i < nrof; i++) {
get_buffer(buffer, input);
arr[i].song = malloc(strlen(buffer)+1);
if (arr[i].song == NULL) {
fprintf(stderr, "Cannot allocate song\n");
exit(EXIT_FAILURE);
}
strcpy(arr[i].song, buffer);
get_buffer(buffer, input);
arr[i].artist = malloc(strlen(buffer)+1);
if (arr[i].artist == NULL) {
fprintf(stderr, "Cannot allocate artist\n");
exit(EXIT_FAILURE);
}
strcpy(arr[i].artist, buffer);
if (fscanf(input, "%d ", &arr[i].year) != 1) {
fprintf(stderr, "Cannot find year for Song: %s Album: %s\n",
arr[i].song, arr[i].artist);
exit(EXIT_FAILURE);
}
}
}
void get_buffer(char buffer[], FILE *input) {
size_t slen;
if (fgets(buffer, SIZE, input) == NULL) {
fprintf(stderr, "Error from fgets(), line not read\n");
exit(EXIT_FAILURE);
}
slen = strlen(buffer);
if (slen > 0 && buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
fprintf(stderr, "Too many characters entered\n");
exit(EXIT_FAILURE);
}
}
void print_free_data(Song *arr, int nrof) {
for (int i = 0; i < nrof; i++) {
printf("%s\n%s\n%d\n\n", arr[i].song, arr[i].artist, arr[i].year);
free(arr[i].song);
arr[i].song = NULL;
free(arr[i].artist);
arr[i].artist = NULL;
}
free(arr);
arr = NULL;
}
Which Outputs correct data:
Mr Tambourine Man
Bob Dylan
1965
Dead Ringer for Love
Meat Loaf
1981
Euphoria
Loreen
2012
Love Me Now
John Legend
2016
Your memory allocation is incorrect. The structure should have char arrays for the song and artist names and an int for the year, and you should modify your API to return the array and its size to the caller:
int loadFile(const char *fileName, Song **arr, int *numberp);
Here is a corrected and simplified of your program:
#include <stdio.h>
#include <stdlib.h>
typedef struct Song {
char song[100];
char artist[100];
int year;
} Song;
/* call as
if (loadFile(fileName, &songs, &songs_size) < 0) {
// deal with error...
}
*/
int loadFile(const char *fileName, Song **arrp, int *numberp) {
FILE *input;
Song *arr;
int i, nrOf;
input = fopen(fileName, "r");
if (input == NULL) {
fprintf(stderr, "Cannot open file %s\n", filename);
return -1;
} else {
if (fscanf(input, "%d\n", &nrOf) != 1) {
fprintf(stderr, "%s: missing number of items\n", filename);
fclose(intput);
return -1;
}
arr = calloc(sizeof(*arr), nrOf);
if (arr == NULL) {
fprintf(stderr, "cannot allocate memory for %d items\n", nrOf);
fclose(intput);
return -1;
}
for (int i = 0; i < nrOf; i++) {
char cc;
if (fscanf(input, "%99[^\n]%*c%99[^\n]%*c%d%c",
sarr[i].song, arr[i].artist,
&arr[i].year, &cc) != 4 || cc != '\n') {
fprintf(stderr, "%s: invalid format for item %d\n",
filename, i);
break;
}
}
printf("The file is now ready.\n");
fclose(input);
*arrp = arr;
*numberp = i;
return i;
}
}
I have a file with tab delimited data. I want to read the every line into a Structure. I have a code to read the data to char buffer. But I want to load the data into a Structure.
This is My sample data.
empname1\t001\t35\tcity1
empname2\t002\t35\tcity2
My Structure definition .
struct employee
{
char *empname;
char *empid;
int age;
char *addr;
};
My sample program to read data to a char array buffer
char buffer[BUF_SIZE]; /* Character buffer */
input_fd = open (fSource, O_RDONLY);
if (input_fd == -1) {
perror ("open");
return 2;
}
while((ret_in = read (input_fd, &buffer, BUF_SIZE)) > 0){
// Do Some Process
}
Here I want to load the content to a structure variable instead of the character buffer. How I can achieve that?
Well, a possible solution could be
Read a complete line from the file using fgets().
tokenize the input buffer based on the required delimiter [tab in your case] using strtok().
allocate memory (malloc()/ realloc()) to a pointer variable of your structure.
copy the tokenized inputs into the member variables.
Note:
1. fgets() reads and stores the trailing \n.
2. Please check carefully how to use strtok(). The input string should be mutable.
3. Allocate memory to pointers before using them. IMO, use statically allocated array as struct employee member variables.
You can use the fscanf function. Open a file as a stream then use the fscanf to get a input from the file.
int fscanf(FILE *stream, const char *format, ...);
FILE *fp=fopen(fsource,"r+");
struct employee detail;
fscanf(fp,"%s %s %d %s",detail.empname,detail.empid,&detail.age,detail.addr);
Make sure that allocation of memory to the variables.
Or else you can use the strtok function. That time you have to use the sscanf function.
You can use fscanf to read each line from file, strtok to tokenize the line read.
Since your structure members are pointers, allocate memory appropriately.
The following minimal code does exactly what you want.
#define SIZE 50
FILE *fp = NULL;
int i = 0;
struct employee var = {NULL, NULL, 0, NULL};
char line[SIZE] = {0}, *ptr = NULL;
/* 1. Open file for Reading */
if (NULL == (fp = fopen("file.txt","r")))
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
/* 2. Allocate Memory */
var.empname = malloc(SIZE);
var.empid = malloc(SIZE);
var.addr = malloc(SIZE);
/* 3. Read each line from the file */
while (EOF != fscanf(fp, "%s", line))
{
/* 4. Tokenise the read line, using "\" delimiter*/
ptr = strtok(line, "\\");
var.empname = ptr;
while (NULL != (ptr = strtok(NULL, "\\")))
{
i++;
/* 5. Store the tokens as per structure members , where (i==0) is first member and so on.. */
if(i == 1)
var.empid = ptr;
else if(i == 2)
var.age = atoi(ptr);
else if (i == 3)
var.addr = ptr;
}
i = 0; /* Reset value of i */
printf("After Reading: Name:[%s] Id:[%s] Age:[%d] Addr:[%s]\n", var.empname, var.empid, var.age, var.addr);
}
Working Demo: http://ideone.com/Kp9mzN
Few things to Note here:
This is guaranteed to work, as long as your structure definition (and order of members) remains the same (see manipulation of value i).
strtok(line, "\\");, Second argument is just escaping (first \) the actual \ character.
Clarification from the OP:
In your structure definition, third member is an int, however you're trying to read t35 into it (which is a string).
So var.age = atoi(ptr); will give you 0,
You could change the structure definition, making third member as char * and allocating memory like other members.
Or change file contents, making sure an int is present as the third value.
I think this may be what you are looking for
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
struct employee
{
char *empname;
char *empid;
int age;
char *addr;
};
int readEmploee(char *line, struct employee *employee)
{
char *token;
char *saveptr;
char *endptr;
if ((employee == NULL) || (line == NULL))
return 0;
token = strtok_r(line, "\t", &saveptr);
if (token == NULL)
return 0;
employee->empname = strdup(token);
token = strtok_r(NULL, "\t", &saveptr);
if (token == NULL)
return 0;
employee->empid = strdup(token);
token = strtok_r(NULL, "\t", &saveptr);
if (token == NULL)
return 0;
employee->age = strtol(token, &endptr, 10);
if (*endptr != '\0')
return 0;
token = strtok_r(NULL, "\t", &saveptr);
if (token == NULL)
return 0;
employee->addr = strdup(token);
return 1;
}
char *mygetline(int fd)
{
char *line;
size_t length;
size_t count;
char character;
line = malloc(128);
if (line == NULL)
return NULL;
length = 0;
count = 1;
do
{
if (read(fd, &character, 1) != 1) /* end of file probably reached */
{
free(line);
return NULL;
}
else if (character != '\n')
{
if (length > 128 * count)
{
char *temp;
temp = realloc(line, 128 * count);
if (temp == NULL)
{
free(line);
return NULL;
}
line = temp;
count += 1;
}
line[length++] = character;
}
} while (character != '\n');
line[length] = 0;
return line;
}
struct employee *readFile(const char *const fSource, size_t *count)
{
struct employee *employees;
int employeeCount;
int input_fd;
char *line;
if ((count == NULL) || (fSource == NULL))
return NULL;
*count = 0;
employees = NULL;
employeeCount = 0;
input_fd = open (fSource, O_RDONLY);
if (input_fd == -1)
{
perror ("open");
return NULL;
}
while ((line = mygetline(input_fd)) != NULL)
{
struct employee employee;
if (readEmploee(line, &employee) != 0)
{
struct employee *temp;
temp = realloc(employees, (1 + employeeCount) * sizeof(struct employee));
if (temp != NULL)
employees = temp;
employees[employeeCount++] = employee;
}
free(line);
}
*count = employeeCount;
return employees;
}
int
main()
{
size_t count;
size_t index;
struct employee *employees;
employees = readFile("somesamplefile.txt", &count);
if (employees == NULL)
return 1;
for (index = 0 ; index < count ; index++)
{
struct employee current;
current = employees[index];
fprintf(stderr, "%s, %s, %d, %s\n", current.empname, current.empid, current.age, current.addr);
if (current.empname != NULL)
free(current.empname);
if (current.empid != NULL)
free(current.empid);
if (current.addr != NULL)
free(current.addr);
}
free(employees);
return 0;
}
this is an assignment for my CS course,
im trying to write a code that reads a file line by line and put the input into a struct element.the struct looks like this:
typedef char* Name;
struct Room
{
int fStatus;
Name fGuest;
};
the status is 0 for available and 1 for booked. the name will be empty if the room is available.
there are 2 function, one to read and put the values to a struct element, and the other one to print it out.
int openRoomFile()
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
while (getline(&buffer, &length, roomFile) != -1) {
if (count % 2 == 0) {
sscanf(buffer, "%d", &AllRooms[count].fStatus);
} else {
AllRooms[count].fGuest = buffer;
}
count++;
}
fclose(roomFile);
free(buffer);
return 0;
}
print function
void printLayout(const struct Room rooms[])
{
for (int i=0; i<3; i++) {
printf("%3d \t", rooms[i].fStatus);
puts(rooms[i].fGuest);
}
}
the output is not what i expected, given the input file is :
1
Johnson
0
1
Emilda
i will get the output :
1 (null)
0
0 (null)
i dont know what went wrong, am i using the right way to read the file? every code is adapted from different sources on the internet.
Here is a fixed version of the openRoomFile()
int openRoomFile(void)
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
while (1) {
buffer = NULL;
if (getline(&buffer, &length, roomFile) == -1) {
break;
}
sscanf(buffer, "%d", &AllRooms[count].fStatus);
free(buffer);
buffer = NULL;
if (getline(&buffer, &length, roomFile) == -1) {
fprintf(stderr, "syntax error\n");
return 1;
}
AllRooms[count].fGuest = buffer;
count++;
}
fclose(roomFile);
return 0;
}
When you no longer need those fGuest anymore, you should call free on them.
If your input is guaranteed to be valid (as were many of my inputs in my CS classes), I'd use something like this for reading in the file.
while(!feof(ifp)){
fscanf(ifp,"%d%s",&AllRooms[i].fStatus, AllRooms[i].fGuest); //syntax might not be right here
//might need to play with the '&'s
//and maybe make the dots into
//arrows
//do work here
i++;
}
You are not allocating memory for Name. Check this. In the below example i'm not included free() calls to allocated memory. you need to call free from each pointer in AllRooms array, once you feel you are done with those and no more required.
#include<stdio.h>
#include<stdlib.h>
typedef char* Name;
struct Room
{
int fStatus;
Name fGuest;
}Room_t;
struct Room AllRooms[10];
int openRoomFile()
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
size_t itemCount = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
buffer = (char *) malloc(16); // considering name size as 16 bytes
while (getline(&buffer, &length, roomFile) != -1) {
if (count % 2 == 0) {
sscanf(buffer, "%d", &AllRooms[itemCount].fStatus);
} else {
AllRooms[itemCount].fGuest = buffer;
itemCount++;
}
count++;
buffer = (char *) malloc(16); // considering name size as 16 bytes
}
fclose(roomFile);
free(buffer);
return 0;
}
void printLayout(const struct Room rooms[])
{
int i;
for (i=0; i<3; i++) {
printf("%3d \t", rooms[i].fStatus);
puts(rooms[i].fGuest);
}
}
int main(void)
{
openRoomFile();
printLayout(AllRooms);
// free all memory allocated using malloc()
return 0;
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
Doing a homework and I'm having problems with, what I believe, pointers.
The assignment consists in the following:
I have a txt file where each line as a name and a password.
thisismyname:thisismypassword
I have to read this data, process it into struct linked list, run all the list and send the password to a brute-force algorithm. This algorithm, after finding the pass, should write the pass on the struct. In the end, I should run the list and write the data to a txt file
My problem is when I find the password. It is not storing its value in the struct. At the end I can read the data, I can see that the brute-force is working but at the end, I'm only managing to write the name and pass to file. The unencrypted pass is being written as NULL so I believe is a pointer problem.
This is the code (Removed all the things that I believe are irrelevant):
typedef struct p {
char *name;
char *pass;
char *pass_desenc;
struct p *next_person;
} person;
typedef struct n {
int a;
int b;
} numbers;
int readFile(person **people) {
FILE * fp;
char line[100];
if ((fp = fopen(STUDENTS_FILE, "r")) != NULL) {
while (fgets(line, sizeof (line), fp) != NULL) {
person *p;
char email[27] = "";
char password[14] = "";
char *change = strchr(line, '\n');
if (change != NULL)
*change = '\0';
/* Gets email*/
strncpy(email, line, 26);
email[27] = '\0';
/* Gets pass*/
strncpy(password, line + 27, 14);
password[14] = '\0';
p = (person*) malloc(sizeof (person));
if (p == NULL) {
return -1;
}
p->name = (char*) malloc(strlen(email));
if (p->name == NULL) {
return -1;
}
sprintf(p->name, "%s", email);
p->name[strlen(email)] = '\0';
p->pass = (char*) malloc(strlen(password));
if (p->pass == NULL) {
return -1;
}
sprintf(p->pass, "%s", password);
p->pass[strlen(password)] = '\0';
p->next_person = (*people);
(*people) = p;
countPeople++;
}
fclose(fp);
return 0;
}
return -1;
}
void fmaps(int id, numbers pass_range, person *people) {
/*This function will run all my list and try to uncrypt pass by pass.
On the brute-force pass in unencrypted and when it return to this function, I can print the data.
*/
while (people != NULL && j > 0) {
for (i = 1; i <= PASS_SIZE && notFound == 1; i++) {
notFound = bruteForce(i, people, &total_pass);
}
notFound = 1;
count = count + total_pass;
printf("#####Email: %s Pass: %s PassDesenq: %s \n", people->name, people->pass, people->pass_desenc);
people = people->next_person;
j--;
}
}
void fcontrol(int n, person *people) {
/*This function should write the data to a file
I can see that all data is written as expected but people->pass_desenc is writing/printing NULL
*/
if ((fp = fopen(STUDENTS_LOG_FILE, "a+")) != NULL) {
while (people != NULL) {
printf("#####1111Email: %s Pass: %s PassDesenq: %s \n", people->name, people->pass, people->pass_desenc);
fprintf(fp, "%d%d%d%d%d%d:grupo%d:%s:%s\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, 1, people->name, people->pass_desenc);
people = people->next_person;
}
}
fclose(fp);
}
int main() {
/*Struct*/
person *people = NULL;
if (readFile(&people)) {
printf("Error reading file!\n");
return 0;
}
/*Function to send data to brute-force*/
fmaps(i, pass_range, people);
/*After all data is processed, this function writes the data to a file*/
fcontrol(NR_PROC, people);
destroyList(&people);
return 0;
}
int bruteForce(int size, person *people, int *total_pass) {
int i;
char *pass_enc;
int *entry = (int*) malloc(sizeof (size));
char pass[50];
char temp;
pass[0] = '\0';
for (i = 0; i < size; i++) {
entry[i] = 0;
}
do {
for (i = 0; i < size; i++) {
temp = (char) (letters[entry[i]]);
append(pass, temp);
}
(*total_pass)++;
/*Compare pass with test*/
pass_enc = crypt(pass, salt);
if (strcmp(pass_enc, people->pass) == 0) {
people->pass_desenc = (char*) malloc(strlen(pass));
if (people->pass_desenc == NULL) {
return -1;
}
sprintf(people->pass_desenc, "%s", pass);
people->pass_desenc[strlen(pass)] = '\0';
return 0;
}
pass[0] = '\0';
for (i = 0; i < size && ++entry[i] == nbletters; i++) {
entry[i] = 0;
}
} while (i < size);
free(entry);
return 1;
}
void append(char *s, char c) {
int len = strlen(s);
s[len] = c;
s[len + 1] = '\0';
}
void destroyList(person **people) {
person *aux;
printf("\nList is being destroyed.");
while (*people != NULL) {
aux = *people;
*people = (*people)->next_person;
free(aux);
printf(".");
}
printf("\nList destroyed.\n");
}
I believe that the changes being made in fmaps are local and are not passing to main.
Any help is appreciated...
This is how you could code the file reader/parser. It avoids str[n]cpy(), and does all string operations using memcpy() + the offsets + sizes. (which need to be correct in both cases, obviously)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct p {
char *name;
char *pass;
// char *pass_desenc;
struct p *next;
} person;
#define STUDENTS_FILE "students.dat"
unsigned countPeople = 0;
int readFile(person **people) {
FILE * fp;
char line[100];
size_t len, pos;
fp = fopen(STUDENTS_FILE, "r");
if (!fp) {
fprintf(stderr, "Could not open %s:%s\n"
, STUDENTS_FILE, strerror(errno));
return -1;
}
while ( fgets(line, sizeof line, fp) ) {
person *p;
len = strlen(line);
/* remove trailng '\n', adjusting the length */
while (len && line[len-1] == '\n') line[--len] = 0;
/* Ignore empty lines */
if ( !len ) continue;
/* Library function to count the number of characters in the first argument
** *not* present in the second argument.
** This is more or less equivalent to strtok(), but
** 1) it doen not modify the string,
** 2) it returns a size_t instead of a pointer.
*/
pos = strcspn(line, ":" );
/* Ignore lines that don't have a colon */
if (line[pos] != ':') continue;
p = malloc(sizeof *p);
if ( !p ) { fclose(fp); return -2; }
p->next = NULL;
p->name = malloc(1+pos);
if ( !p->name ) { fclose(fp); return -3; } /* this could leak p ... */
memcpy(p->name, line, pos-1);
p->name[pos] = 0;
p->pass = malloc(len-pos);
if ( !p->pass ) {fclose(fp); return -4; } /* this could leak p and p->name */
memcpy(p->pass, line+pos+1, len-pos);
/* Instead of pushing (which would reverse the order of the LL)
** , we append at the tail of the LL, keeping the original order.
*/
*people = p;
people = &p->next ;
countPeople++;
}
fclose(fp);
return 0;
}