So following up on a previous question i asked Dynamic arrays clarification in C
the code works fine exept when I try to print the elements, all the elements are printed correct exept the 3rd element weirdly enough even when i change the input file always the 3rd element ir printet wrong. This are the print results: host0host1♥host3. Thanks for any help in advance! Updated to a complete code with the results again beeing host0host1♥host3, This is the file that i read from:
0 host0
1 host1
2 host2
3 host3
4 host4
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int total = 0;
struct names
{
int number;
char *name;
};
void readNames(FILE *fpN, struct names **name)
{
char sentence[512];
struct names *local_name = *name;
while (fgets(sentence, sizeof(sentence), fpN))
{
local_name = realloc(local_name, total + 1 * sizeof(*local_name));
char *token = strtok(sentence, " "); // grab the number
local_name[total].number = atoi(token); // add it to the array
token = strtok(NULL, " \n");
local_name[total].name = malloc(strlen(token) + 1);
strcpy(local_name[total].name, token);
total++;
// printf("TOTAL:%d",*total);
// local_numbers = (int*)realloc(local_numbers,(total+1)*sizeof(int)); //allocate more space for the int array
// local_names = (char**)(realloc(local_names, (total+1)*sizeof(char*))); //allocate more space for the string array
// printf("%d", sizeof(names));
}
*name = local_name;
printf("%s", local_name[0].name);
printf("%s", local_name[1].name);
printf("%s", local_name[2].name);
printf("%s", local_name[3].name);
}
int main(int argc, char **argv)
{
FILE *fpN = NULL;
FILE *fpG = NULL;
if (argc != 2)
{
printf("Wrong arguments");
exit(0);
}
if ((fpN = fopen(argv[1], "r")) == NULL)
{ // check if the file exists
printf("File not found!");
exit(0);
}
struct names *name = NULL; // Allcation will be done later
readNames(fpN, &name);
}
Related
I have a struct storing persons' names, surnames and salaries, but the number of their names is random. For example:
list.txt
John Smith Green 1000 //He has two names
Jennifer Wilson 2000 //She has one name
Steve Adams 1500 //He has one name
James Robert Harris 1250 //He has two names
Robin Walker 1750 //He has one name
I want to store their names in person[count].name, their surnames in person[count].surname and their salaries in person[count].salary.
To do that, I wrote:
fscanf(file, "%s %s %d", person[count].name, person[count].surname, &person[count].salary)
However, problem is that if a person has two names, his second name is stored in person[count].surname, and I cannot take the surname.
How can I take the name of a person with two names in person[count].name?
For this text file:
person[0].name ==> "John Smith"
person[1].name ==> "Jennifer"
person[2].name ==> "Steve"
person[3].name ==> "James Robert"
person[4].name ==> "Robin"
I tried reading the file line by line, then separating it into tokens (words). I am assuming each line contains at max 10 tokens (words), and the last token is salary, the one before the last is surname and first N-2 tokens are the names of the person. So, each person could have surnames with only one word, I am assuming. Here is the code, note that I did not pay attention to memory leaks or dangling pointers etc.
I edited the solution according to the suggestions from #
chux - Reinstate Monica
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Person
{
char *name, *surname;
int salary;
} Person;
int main()
{
Person persons[10];
FILE *file = fopen("text.txt", "r");
if (file == NULL)
{
printf("Error opening file!\n");
exit(1);
}
// read file line by line
char line[256];
int person_count = 0;
while (fgets(line, sizeof(line), file) != NULL)
{
char *tokens[10];
int i = 0;
tokens[0] = strtok(line, " ");
while (tokens[i] != NULL && i < 9)
{
tokens[++i] = strtok(NULL, " ");
}
char name[sizeof line];
strcpy(name, tokens[0]);
for (int j = 1; j < i - 2; j++)
{
strcat(name, " ");
strcat(name, tokens[j]);
}
persons[person_count].name = strdup(name);
persons[person_count].surname = strdup(tokens[i - 2]);
persons[person_count].salary = atoi(tokens[i - 1]);
person_count++;
}
for (int i = 0; i < person_count; i++)
{
printf("%s %s %d\n", persons[i].name, persons[i].surname, persons[i].salary);
}
fclose(file);
}
You cannot use fscanf() for this problem. Here is a simple approach reading one line at a time and parsing it explicitly with strrchr():
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char *name, *surname;
int salary;
};
int main() {
const char *filename = "list.txt";
FILE *file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "Cannot open %s: %s\n", filename, strerror(errno));
return 1;
}
// read file line by line
char line[256];
struct Person *persons = NULL;
int count = 0;
while (fgets(line, sizeof line, file) != NULL) {
char *last = strrchr(line, ' ');
if (!last) // invalid format
continue;
*last++ = '\0';
int salary;
if (sscanf(last, "%d", &salary) != 1)
continue;
const char *name = line;
char *surname = strrchr(line, ' ');
if (surname) {
*surname++ = '\0';
} else {
name = ""; // handle Superman: no first name
surname = line;
}
persons = realloc(persons, (count + 1) * sizeof(*persons));
if (persons == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
persons[count].name = strdup(name);
persons[count].surname = strdup(lastname);
persons[count].salary = salary;
count++;
}
fclose(file);
// dump the database
for (int i = 0; i < count; i++) {
printf("%s %s %d\n", persons[i].name, persons[i].surname, persons[i].salary);
}
// free the database
for (int i = 0; i < count; i++) {
free(persons[i].name);
free(persons[i].surname);
}
free(persons);
return 0;
}
I want to parse a .csv file of the form: grade(float), year(int), name(string), county(string), number(int) and I don't see my mistake:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 300
typedef struct{
char *name, *county;
float grade;
unsigned long int year, number;
}info;
int main(int argc, char **argv)
{
FILE *fin = fopen("lab3.txt", "r");
if(!fin)
{
fprintf(stderr, "ERROR OPENING THE FILE.\n");
exit(1);
}
char buf[MAX];
while(fgets(buf, MAX, fin))
{
buf[strcspn(buf,"\n")] = '\0'; // remove the trailing newline
char *fields[6], *word = strtok(buf, ",");
int i = 0;
while(word)
{
info *array = (info *)malloc(sizeof(info));
fields[i] = word;
array->name = strdup(fields[2]);
array->county = strdup(fields[3]);
array->grade = atof(fields[0]);
array->year = atoi(fields[1]);
array->number = atoi(fields[4]);
printf("Name : %s | County: %s | Year : %ld | Grade : %f | Number: %ld",array->name, array->county, array->year, array->grade, array->number);
//printf("Word : %s\n", fields[i]);
word = strtok(NULL,",");
i++;
free(array->county);
free(array->name);
free(array);
}
}
fclose(fin);
return 0;
}
There are exactly 5 fields on each line, so I wanted to break each line into words andI also used gdb to check what's wrong and the problem seems to be here array->name = strdup(fields[2]);. I've tried numerous things so far, I've allocated memory for the array and the name and county, I'm freeing the memory so, what's the mistake?
There are a number of issues.
You don't really need [nor want] either fields or word. You can just use realloc to increase the size of the array and operate on the current pointer directly.
Doing malloc(sizeof(char)), as you have it, just leaks memory because of the strdup after it.
You do not want to do free at the bottom of the loop--it just kills what you did strdup for.
You are not expanding the size of array on each iteration [using realloc], so your array size stays at one. And, you blow away the previous value on each iteration, so, again, you're leaking memory.
Here's a refactored version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 300
typedef struct {
char *name;
char *county;
float grade;
unsigned long int year;
unsigned long int number;
} info;
#define TOK \
({ \
cp = strtok(bp,","); \
bp = NULL; \
cp; \
})
#define SAVE_S(_sym) \
arrcur->_sym = strdup(TOK)
#define SAVE_F(_sym) \
arrcur->_sym = atof(TOK)
#define SAVE_I(_sym) \
arrcur->_sym = atol(TOK)
int
main(int argc, char **argv)
{
FILE *fin = fopen("lab3.txt", "r");
if (!fin) {
fprintf(stderr, "ERROR OPENING THE FILE.\n");
exit(1);
}
char buf[MAX];
info *arrbase = NULL;
int arrcnt = 0;
int arrmax = 0;
char *bp;
char *cp;
while (fgets(buf, MAX, fin)) {
// remove the trailing newline
buf[strcspn(buf, "\n")] = '\0';
// enlarge array -- use of arrmax limits number of realloc calls
if (arrcnt >= arrmax) {
arrmax += 100;
arrbase = realloc(arrbase,sizeof(*arrbase) * arrmax);
}
// point to current array entry
info *arrcur = &arrbase[arrcnt];
bp = buf;
SAVE_S(name);
SAVE_S(county);
SAVE_F(grade);
SAVE_I(year);
SAVE_I(number);
printf("Name : %s | County: %s | Year : %ld | Grade : %f | Number: %ld",
arrcur->name, arrcur->county, arrcur->year, arrcur->grade,
arrcur->number);
++arrcnt;
}
fclose(fin);
// trim to size used
arrbase = realloc(arrbase,sizeof(*arrbase) * arrcnt);
return 0;
}
I want to change my input.txt file to an integer array.
But sadly I keep missing one integer whenever new-line-character is met.
Following is my main()
int main(int args, char* argv[]) {
int *val;
char *STRING = readFile();
val = convert(STRING);
return 0;
}
Following is my file input function
char *readFile() {
int count;
FILE *fp;
fp = fopen("input.txt", "r");
if(fp==NULL) printf("File is NULL!n");
char* STRING;
char oneLine[255];
STRING = (char*)malloc(255);
assert(STRING!=NULL);
while(1){
fgets(oneLine, 255, fp);
count += strlen(oneLine);
STRING = (char*)realloc(STRING, count+1);
strcat(STRING, oneLine);
if(feof(fp)) break;
}
fclose(fp);
return STRING;
}
Following is my integer array function
int *convert(char *STRING){
int *intarr;
intarr = (int*)malloc(sizeof(int)*16);
int a=0;
char *ptr = strtok(STRING, " ");
while (ptr != NULL){
intarr[a] = atoi(ptr);
printf("number = %s\tindex = %d\n", ptr, a);
a++;
ptr = strtok(NULL, " ");
}
return intarr;
}
There are many issues.
This is a corrected version of your program, all comments are mine. Minimal error checking is done for brevity. intarr = malloc(sizeof(int) * 16); will be a problem if there are more than 16 numbers in the file, this should be handled somehow, for example by growing intarr with realloc, similar to what you're doing in readFile.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
char *readFile() {
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL)
{
printf("File is NULL!n");
return NULL; // abort if file could not be opened
}
#define MAXLINELENGTH 255 // define a constant rather than hardcoding "255" at several places
char* STRING;
char oneLine[MAXLINELENGTH];
STRING = malloc(MAXLINELENGTH);
int count = MAXLINELENGTH; // count mus be initialized and better declare it here
assert(STRING != NULL);
STRING[0] = 0; // memory pointed by STRING must be initialized
while (fgets(oneLine, MAXLINELENGTH, fp) != NULL) // correct usage of fgets
{
count += strlen(oneLine);
STRING = realloc(STRING, count + 1);
strcat(STRING, oneLine);
}
fclose(fp);
return STRING;
}
int *convert(char *STRING, int *nbofvalues) { // nbofvalues for returning the number of values
int *intarr;
intarr = malloc(sizeof(int) * 16);
int a = 0;
char *ptr = strtok(STRING, " \n"); // strings may be separated by '\n', or ' '
*nbofvalues = 0;
while (ptr != NULL) {
intarr[a] = atoi(ptr);
printf("number = %s\tindex = %d\n", ptr, a);
a++;
ptr = strtok(NULL, " \n"); // strings are separated by '\n' or ' '
} // read the fgets documentation which
// terminates read strings by \n
*nbofvalues = a; // return number of values
return intarr;
}
int main(int args, char* argv[]) {
int *val;
char *STRING = readFile();
if (STRING == NULL)
{
printf("readFile() problem\n"); // abort if file could not be read
return 1;
}
int nbvalues;
val = convert(STRING, &nbvalues); // nbvalues contains the number of values
// print numbers
for (int i = 0; i < nbvalues; i++)
{
printf("%d: %d\n", i, val[i]);
}
free(val); // free memory
free(STRING); // free memory
return 0;
}
I'm not sure what your requirement is, but this can be simplified a lot because there is no need to read the file into memory and then convert the strings into number. You could convert the numbers on the fly as you read them. And as already mentioned in a comment, calling realloc for each line is inefficient. There is room for more improvements.
In the below code, the file test.txt has the following data :
192.168.1.1-90
192.168.2.2-80
The output of this is not as expected.
I expect the output to be
192.168.1.1
90
192.168.2.2
80
Any help would be much appreciated.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char *result[10][4];
int i=0;
const char s[2] = "-";
char *value,str[128];
fp = fopen("test.txt", "r");
if (fp == NULL)
printf("File doesn't exist\n");
else{
while(!feof(fp)){
if(fgets(str,sizeof(str),fp)){
/* get the first value */
value = strtok(str, s);
result[i][0]=value;
printf("IP : %s\n",result[i][0]); //to be removed after testing
/* get second value */
value = strtok(NULL, s);
result[i][1]=value;
printf("PORT : %s\n",result[i][1]); //to be removed after testing
i++;
}}
for (int k=0;k<2;k++){
for (int j=0;j<2;j++){
printf("\n%s\n",result[k][j]);
}
}
}
return(0);
}
You can try this solution. It uses dynamic memory instead, but does what your after.
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFSIZE 128
void exit_if_null(void *ptr, const char *msg);
int
main(int argc, char const *argv[]) {
FILE *filename;
char buffer[BUFFSIZE];
char *sequence;
char **ipinfo;
int str_size = 10, str_count = 0, i;
filename = fopen("ips.txt", "r");
if (filename == NULL) {
fprintf(stderr, "%s\n", "Error Reading File!");
exit(EXIT_FAILURE);
}
ipinfo = malloc(str_size * sizeof(*ipinfo));
exit_if_null(ipinfo, "Initial Allocation");
while (fgets(buffer, BUFFSIZE, filename) != NULL) {
sequence = strtok(buffer, "-\n");
while (sequence != NULL) {
if (str_size == str_count) {
str_size *= 2;
ipinfo = realloc(ipinfo, str_size * sizeof(*ipinfo));
exit_if_null(ipinfo, "Reallocation");
}
ipinfo[str_count] = malloc(strlen(sequence)+1);
exit_if_null(ipinfo[str_count], "Initial Allocation");
strcpy(ipinfo[str_count], sequence);
str_count++;
sequence = strtok(NULL, "-\n");
}
}
for (i = 0; i < str_count; i++) {
printf("%s\n", ipinfo[i]);
free(ipinfo[i]);
ipinfo[i] = NULL;
}
free(ipinfo);
ipinfo = NULL;
fclose(filename);
return 0;
}
void
exit_if_null(void *ptr, const char *msg) {
if (!ptr) {
printf("Unexpected null pointer: %s\n", msg);
exit(EXIT_FAILURE);
}
}
The key thing to understand is that char *strtok(char *str, const char *delim) internally modifies the string pointed to by str and uses that to store the result. So the returned pointer actually points to somewhere in str.
In your code, the content of str is refreshed each time when you parse a new line in the file, but the address remains the same. So after your while loop, the content of str is the last line of the file, somehow modified by strtok. At this time, result[0][0] and result[1][0] both points to the same address, which equals the beginning of str. So you print the same thing twice in the end.
This is further illustrated in the comments added to your code.
int main()
{
FILE *fp;
char *result[10][4];
int i=0;
const char s[2] = "-";
char *value,str[128];
fp = fopen("test.txt", "r");
if (fp == NULL)
printf("File doesn't exist\n");
else{
while(!feof(fp)){
if(fgets(str,sizeof(str),fp)){
/* get the first value */
value = strtok(str, s);
// ADDED: value now points to somewhere in str
result[i][0]=value;
// ADDED: result[i][0] points to the same address for i = 0 and 1
printf("IP : %s\n",result[i][0]); //to be removed after testing
/* get second value */
value = strtok(NULL, s);
// ADDED: value now points to somewhere in str
result[i][1]=value;
// ADDED: result[i][1] points to the same address for i = 0 and 1
printf("PORT : %s\n",result[i][1]); //to be removed after testing
i++;
}}
// ADDED: now result[0][0]==result[1][0], result[0][1]==result[1][1], you can test that
for (int k=0;k<2;k++){
for (int j=0;j<2;j++){
printf("\n%s\n",result[k][j]);
}
}
}
return(0);
}
To get the expected output, you should copy the string pointed by the pointer returned by strtok to somewhere else each time, rather than just copy the pointer itself.
The problem I am having with my program is that I create a linked list, but when I go to print out my linked list it just prints out the last person in the list. It is supposed to print out everyone in the list. This is the only problem that I have in my program and if someone could point me in the right direction it would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define MAX_PLAYERS 9
#define MAX_STR_LEN 25
typedef struct Player_ {
char name[MAX_STR_LEN];
int number_of_hits;
int number_of_strikes;
struct Player_ *next;
} Player;
int pitch_result(void);
Player *add_player(Player *first_player, const char *name);
void destroy_team(Player *first_player);
void display_line_up(Player* first);
void readFile(Player **lineup, const char* file);
int main(int argc, char *argv[]) {
if(argc < 2){
exit(1);
}
srand(time(NULL));
int strikes = 0;
int hits = 0;
Player *lineup = NULL;
readFile(&lineup, argv[1]);
printf("\nLine up for this team is: ");
display_line_up(lineup);
printf("\n\n%s is batting \n", lineup->name);
for ( ; strikes < 3; ) {
int result = pitch_result();
if (result) {
++hits;
}
else
++strikes;
}
printf("\nScore of game was: %d\n", hits/4);
return 0;
}
void readFile(Player **lineup, const char* file){
FILE *input;
input = fopen(file, "r");
if(input == NULL){
printf("Failed to open");
}
char player[MAX_STR_LEN];
while(fscanf(input, "%s", &player) != EOF){
// printf("\n%s", player);
*lineup = add_player(*lineup, player);
}
}
int pitch_result(void) {
int result;
result = rand() % 2;
return result;
}
void destroy_team(Player *first_player){
Player *temp = first_player->next;
Player *free_player;
while (temp) {
free_player = temp;
temp = temp->next;
free(free_player->name);
free(free_player);
}
}
Player *add_player(Player *first_player, const char *name) {
Player *new_player = (Player*)malloc(sizeof(Player));
int nameLength;
nameLength = strlen(name);
// new_player->name = malloc(nameLength * sizeof(char));
new_player->next = NULL;
strncpy(new_player->name, name, nameLength);
new_player->number_of_hits = 0;
new_player->number_of_strikes = 0;
return new_player;
}
void display_line_up(Player *first){
if(first == NULL)
printf("\nThe list is empty");
else {
while(first != NULL){
printf("\n%s", first->name);
first = first->next;
}
}
}
Here is the file that I read into the program when I run it (stats.txt)
Yadier_Molina
Allen_Craig
Daniel_Descalso
Rafael_Furcal
David_Freese
Matt_Holiday
Jon_Jay
Carlos_Beltran
Matt_Adams
This is the sample output that I was given for my program
Line up for this team is:
Yadier_Molina
Allen_Craig
Daniel_Descalso
Rafael_Furcal
David_Freese
Matt_Holliday
Jon_Jay
Carlos_Beltran
Matt_Adams
Yadier_Molina is batting
Strike!
hit!
Strike!
Strike!
Score of game was: 0
Your add_player function returns a pointer to a node that contains the new player-- and has no connection to the preexisting list (the variable first_player is never even used).
Your readFile function uses this, discarding the old list each time it reads the name of a new player:
*lineup = add_player(*lineup, player);
so the resultant list contains only the last player.
As Beta said, you are not concatenating the players to the list. You can keep a pointer to the last element, which you modify from readFile:
void readFile(Player **lineup, const char* file){
Player* last = NULL;
FILE *input;
input = fopen(file, "r");
if(input == NULL){
printf("Failed to open");
}
char player[MAX_STR_LEN];
while(fscanf(input, "%s", &player) != EOF){
// printf("\n%s", player);
Player* p = add_player(player);
if (!last)
*lineup = p;
else
last->next = p;
last = p;
}
}
And the function add_player just creates a new player so, I would rename it to: create_player(const char* name)