I want to read information from the file but can only read the first line in the file:
int deserialize(Contact **head) {
FILE *file = fopen("contacts.csv", "rb");
Contact *temp = (Contact *)malloc(5 * sizeof(Contact));
*head = (Contact *)malloc(50 * sizeof(Contact));
if (file == NULL)
return 0;
while (*head != NULL &&
(fscanf(file, "%d,%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%d\n",
&((*head)->id), &((*head)->firstName),
&((*head)->middleName), &((*head)->lastName),
&((*head)->company), &((*head)->phone),
&((*head)->company), &((*head)->address),
&((*head)->birthday), &((*head)->website),
&((*head)->note), &((*head)->status))) > 0)
{
printf(" head: %s\n", (*head)->firstName);
*head = (*head)->next;
}
fclose(file);
return 1;
}
My file.csv:
4,d,d,d,,,,,,,,0
3,c,c,c,,,,,,,,0
2,b,b,b,,,,,,,,0
1,a,a,a,,,,,,,,0
my output:
head: d
There are multiple problems in the posted code:
you have a memory leak in case of fopen failure
opening the text file in binary mode is not recommended: you will need to handle legacy line termination sequences explicitly. Better use text mode ("r") and let the library handle them.
you allocate an array of 50 contacts for head to point to, but this is not a linked list.
*head = (*head)->next has undefined behavior as (*head)->next was never set. You could instead store the next entry to (*head)[1] or use an actual pointer instead of a double pointer.
%[^,] is a risky conversion specifier: you may have a buffer overflow if the file contains a long string before the ,
%[^,] cannot parse an empty field and you have many such fields in your file
you do not check the number of conversions returned by fscanf() so you do not detect conversion error caused by the first empty field and the next call, returns 0 because the empty field cannot be converted as an integer, causing the loop to exit.
for more reliable error recovery, you should read the file one line at a time and convert the CSV line, check the result and report failures explicitly
to parse empty fields, you cannot use fscanf(), nor strtok and friends, you should instead write custom function to parse number and string fields.
Here is a safer approach, assuming string members of the Contact object are all char arrays.
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int parse_csv_number(const char **pp, int *dest) {
char *p = *pp;
char *end;
long num;
errno = 0;
num = strtol(p, &end, 10);
if (p == end)
return 1; // not a number
if (errno != 0 || num < INT_MIN || num > INT_MAX)
return 2; // number too large
if (*end == ',')
end++;
else
if (*end != '\0' && *end != '\n')
return 3; // extra characters after number
*dest = num;
return 0;
}
int parse_csv_string(const char **pp, char *dest, size_t len) {
char *p = *pp;
size_t i = 0;
p += strspn(" \t"); // skip banks
while (*p != ',' &&*p != '\n' && *p != '\0') {
if (i + 1 >= len)
return 1; // string too long
dest[i++] = *p++;
}
dest[i] = '\0';
if (*p == ',')
p++;
*pp = p;
return 0;
}
int deserialize(Contact **headp) {
const char *filename = "contacts.csv";
char buf[256];
char *p;
int count = 0;
FILE *file = fopen(filename, "rb");
if (file == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename,
strerror(errno));
return -1;
}
Contact *head = NULL;
Contact *tail = NULL;
while (fgets(buf, sizeof buf, file)) {
Contact *cp = malloc(sizeof(*cp));
if (cp == NULL) {
fprintf(stderr, "memory allocation error: %s\n", strerror(errno));
break;
}
p = buf;
if (parse_csv_number(&p, &cp->id)
|| parse_csv_string(&p, cp->firstName, sizeof cp->firstName)
|| parse_csv_string(&p, cp->middleName, sizeof cp->middleName)
|| parse_csv_string(&p, cp->lastName, sizeof cp->lastName)
|| parse_csv_string(&p, cp->phone, sizeof cp->phone)
|| parse_csv_string(&p, cp->company, sizeof cp->company)
|| parse_csv_string(&p, cp->address, sizeof cp->address)
|| parse_csv_string(&p, cp->birthday, sizeof cp->birthday)
|| parse_csv_string(&p, cp->website, sizeof cp->website)
|| parse_csv_string(&p, cp->note, sizeof cp->note)
|| parse_csv_number(&p, &cp->status)
|| p[strspn(p, " \t")] != '\n') {
fprintf(stderr, "invalid CSV line: %s\n" buf);
continue;
}
count++;
cp->next = NULL;
if (head == NULL)
tail = head = cp;
else
tail = tail->next = cp;
}
*headp = head;
fclose(file);
return count;
}
If the strings in the Contact object are string pointers (char *), you must allocate the strings. Here is a modified version:
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int parse_csv_number(const char **pp, int *dest) {
char *p = *pp;
char *end;
long num;
errno = 0;
num = strtol(p, &end, 10);
if (p == end)
return 1; // not a number
if (errno != 0 || num < INT_MIN || num > INT_MAX)
return 2; // number too large
if (*end == ',')
end++;
else
if (*end != '\0' && *end != '\n')
return 3; // extra characters after number
*dest = num;
return 0;
}
int parse_csv_string(const char **pp, char **dest) {
char *p = *pp;
char *str;
size_t i = 0, len;
p += strspn(" \t"); // skip banks
len = strcspn(p, ",\n"); // locate end of string
str = strndup(p, len);
if (str == NULL) {
fprintf(stderr, "memory allocation error: %s\n", strerror(errno));
return -1;
}
p += len;
if (*p == ',')
p++;
*dest = str;
return 0;
}
int deserialize(Contact **headp) {
const char *filename = "contacts.csv";
char buf[256];
char *p;
int count = 0;
FILE *file = fopen(filename, "rb");
if (file == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename,
strerror(errno));
return -1;
}
Contact *head = NULL;
Contact *tail = NULL;
while (fgets(buf, sizeof buf, file)) {
Contact *cp = malloc(sizeof(*cp));
if (cp == NULL) {
fprintf(stderr, "memory allocation error: %s\n", strerror(errno));
break;
}
p = buf;
if (parse_csv_number(&p, &cp->id)
|| parse_csv_string(&p, &cp->firstName)
|| parse_csv_string(&p, &cp->middleName)
|| parse_csv_string(&p, &cp->lastName)
|| parse_csv_string(&p, &cp->phone)
|| parse_csv_string(&p, &cp->company)
|| parse_csv_string(&p, &cp->address)
|| parse_csv_string(&p, &cp->birthday)
|| parse_csv_string(&p, &cp->website)
|| parse_csv_string(&p, &cp->note)
|| parse_csv_number(&p, &cp->status)
|| p[strspn(p, " \t")] != '\n') {
fprintf(stderr, "invalid CSV line: %s\n" buf);
continue;
}
count++;
cp->next = NULL;
if (head == NULL)
tail = head = cp;
else
tail = tail->next = cp;
}
*headp = head;
fclose(file);
return count;
}
The hard-coded file name ("contacts.csv") doesn't match the file name you claim "file.csv". This appears to be an error in your question.
You probably don't want to open a text .csv file in binary mode.
fscanf() returns number of fields matched which in this case 4, and it would not match the last status field. > 0 is a defective test of success.
%[,] "Matches a nonempty sequence of characters from the specified set" but you have an empty set so it doesn't match. You cannot use fscanf() to match empty fields.
(*head)->next is not initialized to an address and quite possibly NULL which would terminate your loop. After your malloc() your array you would have loop through the .next pointer to the following entry in the array. Or just use the array and forget about the linked list:
for(size_t i = 0; i < 50; i++) {
(*head)[i].id ...
}
Always specify a maximum field when reading strings to avoid buffer overflows.
I suggest you use an existing csv library.
If you need to write a parser yourself then I I suggest you use fgets() to read a line from your file then use strchr() to find the next separator. Remember to strdup() each field values as the line read would change for each loop iteration. Here's a sample to get your started:
#define _POSIX_C_SOURCE 200809L
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINE_LEN 100
typedef struct Contact {
int id;
char *firstName;
char *middleName;
char *lastName;
char *company;
char *phone;
char *address;
char *birthday;
char *website;
char *note;
int status;
} Contact;
const char *parse_chr(const char *s, char chr) {
if(*s == chr)
return s + 1;
return NULL;
}
const char *parse_int(const char *s, int *d, int fallback) {
char *next;
long l = strtol(s, &next, 10);
if(l > INT_MAX || l < INT_MIN || s == next) {
*d = fallback;
return NULL;
}
*d = l;
return next;
}
const char *parse_str(const char *s, char **v, char *fallback) {
size_t n = strcspn(s, ",");
if(!n)
*v = fallback ? strdup(fallback) : fallback;
else
*v = strndup(s, n);
return s + n;
}
#define PARSE_CHR(CHR) \
next = parse_chr(s, CHR);\
if(!next)\
goto err;\
s = next
#define PARSE_INT(NAME) \
next = parse_int(s, &(*c)[*n].id, -1);\
if(next)\
s = next
#define PARSE_STR(NAME) \
next = parse_str(s, &(*c)[*n].NAME, NULL);\
if(next)\
s = next
int deserialize(FILE *f, Contact **c, size_t *n) {
if(!f)
return EXIT_FAILURE;
for(*c = NULL, *n = 0;; (*n)++) {
char line[LINE_LEN];
if(!fgets(line, LINE_LEN, f))
break;
Contact *tmp = realloc(*c, (*n + 1) * sizeof **c);
if(!tmp)
return EXIT_FAILURE;
*c = tmp;
const char *s = line;
const char *next;
PARSE_INT(id); PARSE_CHR(',');
PARSE_STR(firstName); PARSE_CHR(',');
PARSE_STR(middleName); PARSE_CHR(',');
PARSE_STR(lastName); PARSE_CHR(',');
PARSE_STR(company); PARSE_CHR(',');
PARSE_STR(phone); PARSE_CHR(',');
PARSE_STR(company); PARSE_CHR(',');
PARSE_STR(address); PARSE_CHR(',');
PARSE_STR(birthday); PARSE_CHR(',');
PARSE_STR(website); PARSE_CHR(',');
PARSE_STR(note); PARSE_CHR(',');
PARSE_INT(status); PARSE_CHR('\n');
continue;
err:
printf("Could not parse %s\n", line);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int main() {
Contact *c;
size_t n;
deserialize(fopen("contacts.csv", "r"), &c, &n);
for(size_t i = 0; i < n; i++) {
printf(
"%d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d\n",
c[i].id,
c[i].firstName,
c[i].middleName,
c[i].lastName,
c[i].company,
c[i].phone,
c[i].address,
c[i].birthday,
c[i].website,
c[i].note,
c[i].status
);
free(c[i].firstName);
free(c[i].middleName);
free(c[i].lastName);
free(c[i].company);
free(c[i].phone);
free(c[i].address);
free(c[i].birthday);
free(c[i].website);
free(c[i].note);
}
free(c);
}
and example output:
4, d, d, d, (null), (null), (null), (null), (null), (null), 0
3, c, c, c, (null), (null), (null), (null), (null), (null), 0
2, b, b, b, (null), (null), (null), (null), (null), (null), 0
1, a, a, a, (null), (null), (null), (null), (null), (null), 0
PARSE_INT() and PARSE_STR() considers their respective field values optional. PARSE_CHR() is required. The underlying functions support optional values with fallback or required values.
Related
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* Search and Print all non-duplicate struct names inside input file */
int main(int argc, char *argv[])
{
char temp[64], buf[64], filename[128], array[1024] = "";
char *ptr, *line = NULL;
char *tmp1, *tmp2;
ssize_t rv;
size_t len;
int count = 0;
FILE *fp;
if (argc < 2) {
printf("enter file name at cmd line...\n");
return -1;
}
sprintf(filename, argv[1], strlen(argv[1]));
fp = fopen(argv[1], "r");
if (!fp) {
printf("File could not be opened: %s\n", argv[1]);
return -1;
}
while ((rv = getline(&line, &len, fp)) != -1) {
ptr = strstr(line, "struct");
if (ptr) {
ptr += strlen("struct");
while (*ptr == ' ')
ptr++;
tmp1 = strchr(ptr, ' ');
tmp2 = strchr(ptr, ';');
len = 0;
if (tmp1 == NULL && tmp2 == NULL) {
continue;
}
else if (tmp1 == NULL && tmp2 != NULL) {
len = tmp2 - ptr;
}
else if (tmp1 != NULL && tmp2 == NULL) {
len = tmp1 - ptr;
}
else if (tmp1 && tmp2) {
len = tmp1 < tmp2 ? tmp1 - ptr : tmp2 - ptr;
}
if (len) {
snprintf(temp, len+1, "%s", ptr);
if (!strstr(array, temp)) {
sprintf(buf, "%2d. ", count++);
strcat(buf, temp);
strcat(array, buf);
strcat(array, "\n");
}
}
}
}
fclose(fp);
if (line)
free(line);
printf("%s\n", array);
return 0;
}
Above program finds struct names correctly, however I see chars like , and ) at the end of output names. How to remove it? Below is sample output:
[root#mnm-server programs]# ./a.out /usr/src/linux/drivers/net/ethernet/smsc/smsc911x.c
0. smsc911x_data
1. smsc911x_ops
2. smsc911x_platform_config
3. phy_device
4. mii_bus
5. net_device
6. napi_struct
7. regulator_bulk_data
8. clk
9. platform_device
10. smsc911x_data,
11. sk_buff
12. net_device_stats
13. netdev_hw_addr
14. sockaddr
15. ethtool_drvinfo
16. ethtool_eeprom
17. ethtool_ops
18. net_device_ops
19. ures,
20. resource
21. device_node
22. smsc911x_data))
23. dev_pm_ops
24. of_device_id
25. platform_driver
Notice output of line 10 and 22. One approach would be to do strchr for ,, ), ; and remove char from end. However, this is not a clean solution if the number of non-alphabetic characters increases.
NOTE: The best solution I found for this is here.
Thanks to inputs from Daniel Jour, the following code handles all cases of struct name* ptr;, struct name{ };, struct { };
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
/* Search and Print all non-duplicate struct names inside input file */
int main(int argc, char *argv[])
{
char temp[64], buf[64], filename[128], array[1024] = "";
char *ptr, *line = NULL;
size_t len;
int count = 0, flag = 0;
FILE *fp;
if (argc < 2) {
printf("enter file name at cmd line...\n");
return EXIT_FAILURE;
}
sprintf(filename, argv[1], strlen(argv[1]));
fp = fopen(argv[1], "r");
if (!fp) {
printf("File could not be opened: %s\n", argv[1]);
return EXIT_FAILURE;
}
while ((getline(&line, &len, fp)) != -1) {
ptr = flag ? line : strstr(line, "struct ");
if (ptr) {
if (!flag)
ptr += strlen("struct ");
while (*ptr == ' ')
ptr++;
len = 0;
while (isalnum(*ptr) || *ptr == '_' || *ptr == '{' || *ptr == '}') {
if (*ptr == '{') {
flag++;
}
else if (*ptr == '}') {
len = 0;
flag--;
do {
ptr++;
} while (*ptr == ' ');
ptr--;
}
else if ((*ptr != '{') || (*ptr != '}')) {
len++;
}
ptr++;
}
if (len && !flag) {
ptr -= len;
snprintf(temp, len+1, "%s", ptr);
if (!strstr(array, temp)) {
sprintf(buf, "%2d. ", count++);
strcat(buf, temp);
strcat(array, buf);
strcat(array, "\n");
}
}
}
}
fclose(fp);
if (line)
free(line);
printf("%s\n", array);
return EXIT_SUCCESS;
}
This program doesn't handle cases like func(struct x, struct y), interested users can fix it or just use grep -o "struct [^ ;,)]\+" # | awk '{print $2}' | sort -u. Output of the above program for pre-processed file hworld.i:
[root#server]# cat hworld.c
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
[root#server]# gcc -Wall --save-temps hworld.c
[root#server]# ./find_structs hworld.i
0. _IO_FILE
1. _IO_marker
2. _IO_FILE_plus
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.
I am currently collecting input from a file but my program separates each letter into the char array instead of each word. How can I change my code to get each word?
char c, fileName[20];
FILE *f;
void getFile() {
f = fopen(fileName, "r");
while((c = fgetc(f)) != EOF) {
printf("%c",c);
}
fclose(f);
}
You can use a scanset with fscanf or sscanf. This scanset, %29[a-zA-Z], reads lower and upper case English characters and stops when it encounters a character not in the set. The 29 limits the maximum number of characters to read so as to not overwrite the buffer, word[30]. When fscanf fails, the else will read one character from the file and give fscanf another try at reading another word.
This also uses the command line to pass in the file to read as argv[1].
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[])
{
char word[30] = {'\0'};
int ch = 0;
FILE *pf = NULL;
if ( argc != 2) {//command requires program name and a file name
printf ( "useage: program filename\n");
return 1;
}
if ( ( pf = fopen ( argv[1], "r")) == NULL) {
perror ( "could not open file");
return 1;
}
while ( 1) {
if ( ( fscanf ( pf, "%29[a-zA-Z]", word)) == 1) {
printf ( "%s\n", word);
}
else {
if ( ( ch = fgetc ( pf)) == EOF) {//read one character and check for end of file
break;
}
//could do something here with the value of ch if needed
}
}
printf ( "--DONE--\n");
return 0;
}
This will allocate an array for each word. As words are added the array is expanded using realloc.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( int argc, char *argv[])
{
char **words = NULL;//pointer for words
char **temp = NULL;
char word[30] = {'\0'};
int ch = 0;
int each = 0;
int found = 0;
int count = 0;
int wordsize = 0;
FILE *pf = NULL;
if ( argc != 2) {//command requires program name and a file name
printf ( "useage: program filename\n");
return 1;
}
if ( ( pf = fopen ( argv[1], "r")) == NULL) {
perror ( "could not open file");
return 1;
}
while ( 1) {
if ( ( fscanf ( pf, "%29[a-zA-Z]", word)) == 1) {
found = 0;
for ( each = 0; each < wordsize; each++) {
if ( strcmp ( words[each], word) == 0) {
found = 1;
break;
}
}
if ( found == 0) {
wordsize += 1;// increment number of words
temp = realloc ( words, wordsize * sizeof ( char *));//reallocate for another word
if ( temp != NULL) {
words = temp;
words[wordsize - 1] = malloc ( strlen ( word) + 1);//malloc for the word itself
if ( words[wordsize - 1] != NULL) {
strcpy ( words[wordsize - 1], word);
}
else {
printf ( "malloc failed\n");
wordsize -= 1;
break;
}
}
else {
printf ( "realloc failed\n");
wordsize -= 1;
break;
}
}
printf ( "%s\n", word);
}
else {
if ( ( ch = fgetc ( pf)) == EOF) {//read one character and check for end of file
break;
}
//something could be done with ch if needed
}
}
printf ( "--DONE Reading file--\n");
for ( each = 0; each < wordsize; each++) {// print each word
printf ( "%s\n", words[each]);
}
count = 0;
printf ( "Enter a word to search for\n");
if ( ( scanf ( "%29[a-zA-Z]", word)) == 1) {
for ( each = 0; each < wordsize; each++) {
if ( strcmp ( words[each], word) == 0) {
printf ( "Found %s at index %d\n" word, each);
count++;
}
}
printf ( "Found %s %d times\n" word, count);
}
for ( each = 0; each < wordsize; each++) {//release memory
free ( words[each]);
}
free ( words);
return 0;
}
You could use char * fgets ( char * str, int num, FILE * stream );
Then use char *strtok(char *str, const char *delim)
for example
#include <stdio.h>
int main()
{
FILE * pFile;
char mystring [100];
const char delimters[2] = " ,:";
char *token;
pFile = fopen ("myfile.txt" , "r");
if (pFile == NULL) perror ("Error opening file");
else {
if ( fgets (mystring , 100 , pFile) != NULL )
/* get the first token */
token = strtok(mystring, delimiters);
/* walk through other tokens */
while( token != NULL )
{
printf( " %s\n", token );
token = strtok(NULL, delimiters);
}
fclose (pFile);
}
return 0;
}
Use fscanf(3) instead
char word[256], *p;
while(fscanf(f, "%s", &word) != EOF) {
printf("%s\n", word);
/* break down word into individual chars */
for(p=word; *p; p++) {
printf("%c ", *p);
}
printf("\n");
}
First problem fgetc() returns an int you cannot store EOF in a char variable.
You should check if fopen() didn't return NULL.
You can use this technique to read words like this
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAX_BUFFER_SIZE 256
char **load_words_from_file(const char *filename)
{
size_t wordcount;
size_t charcount;
char buffer[MAX_BUFFER_SIZE];
int chr;
FILE *file;
char **words;
void *pointer;
file = fopen(filename, "r");
if (file == NULL)
return NULL;
wordcount = 0;
charcount = 0;
words = NULL;
while ((chr = fgetc(file)) != EOF)
{
/* it's a white space or it exceeded buffer size, it's a word delimiter */
if ((isspace(chr) != 0) || (charcount >= sizeof(buffer) - 1))
{
/* 'nul' terminate 'buffer' for strcpy() and strlen() */
buffer[charcount] = '\0';
pointer = realloc(words, (1 + wordcount) * sizeof(char *));
if (pointer == NULL) /* failure, free allocated memory and return NULL */
goto failure;
words = pointer;
words[wordcount] = malloc(1 + charcount);
charcount = 0; /* reset character count */
if (words[wordcount] == NULL)
goto failure;
strcpy(words[wordcount], buffer);
wordcount += 1;
}
else
{
/* store the character and count it */
buffer[charcount] = (char)chr;
charcount += 1;
}
}
pointer = realloc(words, (1 + wordcount) * sizeof(char *));
if (pointer == NULL)
goto failure;
words = pointer;
words[wordcount] = NULL; /* this will let you know when to stop fetching words */
fclose(file);
return words;
failure:
for (size_t i = 0 ; i < wordcount ; ++i)
free(words[i]);
free(words);
return NULL;
}
int
main()
{
const char *filename = "your-file-name-here";
char **words = load_words_from_file(filename);
size_t counter = 0;
if (words == NULL)
{
printf("no words found in the file\n");
return -1;
}
while (words[counter] != NULL)
{
printf("%zuth word: %s\n", 1 + counter, words[counter]);
free(words[counter]); /* this program will not use it again */
counter += 1;
}
free(words);
return 0;
}
the strtok() method would also work, but it's harder to understand.
Note the use of goto, it's a benign usage, although some people think that goto is always harmful, that's not true, using it like this makes the program adhere to the DRY priniciple.
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 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;
}