I'm trying to save the element of a file into an array of structs.
I want to dynamically increase the memory allocated in the array to fit just the total amount of lines (structs) in my file.
The .txt file looks like this:
R&B;6jvvpPJQJy5rMOEkLlADl6;Trey Songz
R&B;6p8JTP4A9NZHtxRVhkEo6s;T-Pain
R&B;3SwhPTNNU5hpF33bbCsji6;BJ The Chicago Kid
R&B;5yvhdo8FXbBsIllxv2Rr94;SZA
R&B;5Yq38evNk28qlTVHHtwBhT;James Blake
R&B;5yamjs92dcayRVlNuY116G;H.E.R.
R&B;4FjcZsKyGhhZnuYq0nzXpZ;T-Pain
R&B;22jnEneSABg4vRCR1vow7F;Daniel Caesar
R&B;57qiTKh8bVX0VtfUNTQqhw;The Weeknd
This is currently my code. (songFile and songsList does the same thing as genresList but is currently not implemented)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct genresFile{
char* genre;
char* idSong;
char* artist;
} genresFile;
typedef struct songsFile{
char* idSong;
int popularity;
char* mode;
} songsFile;
int main(int argc, char *argv[])
{
FILE* songs = fopen("songs.txt", "r");
FILE* genres = fopen("genres.txt", "r");
genresFile* genresList = NULL;
//songsFile* songsList = NULL;
char *buf = malloc(256);
char *tmp;
int i = 0;
while (fgets(buf, 255, genres)){
genresList = realloc(genresList, (i+1)*sizeof(genresFile));
if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
buf[strlen (buf) - 1] = '\0';
genresFile object;
tmp = strtok(buf, ";");
object.genre = tmp;
tmp = strtok(NULL, ";");
object.idSong = tmp;
tmp = strtok(NULL, ";");
object.artist = tmp;
genresList[i] = object;
i++;
}
printf("%s, %s, %s\n",genresList[3].genre, genresList[3].idSong, genresList[3].artist);
free(genresList);
//free(songsList);
fclose(songs);
fclose(genres);
return 0;
}
This is the output I get from the print at the end:
Soul, qWZdkBl4UVPj9lK6HuuFM, r Thomas & The Volcanos
This is the last line of the .txt file and the artist's name is missing a letter at the beginning.
This is getting saved in every element of the array. Please help :D
Assigning pointers doesn't mean to copy strings.
The buffer buf is overwritten in the following lines, so what are pointed at by the pointers are also changed.
To avoid this, you should copy the strings instead of just assigning pointers.
It can be done like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct genresFile{
char* genre;
char* idSong;
char* artist;
} genresFile;
typedef struct songsFile{
char* idSong;
int popularity;
char* mode;
} songsFile;
char* copy_string(const char* str) {
char* copy = malloc(strlen(str) + 1); /* +1 for terminating null-character */
if (copy != NULL) strcpy(copy, str);
return copy;
}
int main(int argc, char *argv[])
{
FILE* songs = fopen("songs.txt", "r");
FILE* genres = fopen("genres.txt", "r");
genresFile* genresList = NULL;
//songsFile* songsList = NULL;
char *buf = malloc(256);
char *tmp;
int i = 0;
while (fgets(buf, 255, genres)){
genresList = realloc(genresList, (i+1)*sizeof(genresFile));
if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
buf[strlen (buf) - 1] = '\0';
genresFile object;
tmp = strtok(buf, ";");
object.genre = copy_string(tmp);
tmp = strtok(NULL, ";");
object.idSong = copy_string(tmp);
tmp = strtok(NULL, ";");
object.artist = copy_string(tmp);
genresList[i] = object;
i++;
}
printf("%s, %s, %s\n",genresList[3].genre, genresList[3].idSong, genresList[3].artist);
free(genresList);
//free(songsList);
fclose(songs);
fclose(genres);
return 0;
}
You can use strdup() instead of this copy_string() if it is supported in your (target) environment.
Related
I'm struggling with this split function based on C. After returning the struct by reference from the strSplit() function the token handle seems wrong. The variable sd->tokens is the right address, but I cannot get the tokens. But they are correct because inside the function I can get it.
How can I resolve this and what is the reason for this behavior. All remaining variables in the struct are ok.
#include <stdio.h>
#include <string.h>
struct splitResult {
char *source;
long lSource;
int result;
char **tokens;
};
typedef struct splitResult splitResultStruct;
splitResultStruct * strSplit(char *source, char *delimiter);
int main(int argc, const char * argv[]) {
char *source = "part1.part2.part3";
splitResultStruct *sd;
sd = strSplit(source, ".");
printf("%d tokens found\n", sd->result);
for(int i=0; i<sd->result; i++) {
printf("%s\n", sd->tokens[i]);
}
return 0;
}
splitResultStruct * strSplit(char *source, char *delimiter) {
// Defines the result struct
splitResultStruct sData, *sDataPtr;
sDataPtr = &sData;
sData.source = source;
// Gets the length of source string
sData.lSource = strlen(source);
// Don't split if empty string is given
if(sData.lSource == 0) {
sData.result = -1;
return sDataPtr;
}
// Allocate memory according teh size of source string
char data[sData.lSource];
// Copy the source into the allocated memory
strcpy(data,source);
// Just count the tokens
char *token = strtok(data, delimiter);
int tc = 0;
while (token != NULL)
{
token = strtok(NULL, delimiter);
tc++;
}
if(tc == 0) {
sData.result = -1;
return sDataPtr;
}
// Defines an array of char pointer with the dimension of the number of tokens
sData.result = tc;
char *tokens[tc];
// Resets the token engine
strcpy(data,source);
// Strip out the first token found
token = strtok(data, delimiter);
tokens[0] = token;
tc = 0;
while (token != NULL)
{
// Strip out one token and store them into the token array
token = strtok(NULL, delimiter);
tc++;
tokens[tc] = token;
}
sData.tokens = tokens;
for(int i=0; i<sData.result; i++) {
printf("%s\n", sData.tokens[i]);
}
return sDataPtr;
}
Remember, never return a pointer to local variable.
Local variables are destoryed when the function returns.
Try:
sDataPtr = malloc(sizeof(splitResultStruct));
Or:
static splitResultStruct sData;
splitResultStruct* sDataPtr;
Ok, guys, got it regarding the struct memory. Changed the code to this version, so the struct will be declared in main and a pointer is passed. The functions returns nothing but changes the struct itself. But the tokens remains empty. Not a clue...
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct splitResult {
char *source;
long lSource;
int result;
long tSize;
char **tokens;
};
typedef struct splitResult splitResultStruct;
void strSplit(splitResultStruct *, char *source, char *delimiter);
int main(int argc, const char * argv[]) {
char *source = "part1.part2.part3";
splitResultStruct sd;
strSplit(&sd, source, ".");
printf("%d tokens found\n", sd.result);
for(int i=0; i<sd.result; i++) {
printf("%s\n", sd.tokens[i]);
}
return 0;
}
void strSplit(splitResultStruct *sData, char *source, char *delimiter) {
sData->source = source;
// Gets the length of source string
sData->lSource = strlen(source);
// Don't split if empty string is given
if(sData->lSource == 0) {
sData->result = -1;
}
// Allocate memory according teh size of source string
char data[sData->lSource];
// Copy the source into the allocated memory
strcpy(data,source);
// Just count the tokens
char *token = strtok(data, delimiter);
int tc = 0;
while (token != NULL)
{
token = strtok(NULL, delimiter);
tc++;
}
if(tc == 0) {
sData->result = -1;
}
// Defines an array of char pointer with the dimension of the number of tokens
sData->result = tc;
char *tokens[tc];
// Resets the token engine
strcpy(data,source);
// Strip out the first token found
token = strtok(data, delimiter);
tokens[0] = token;
tc = 0;
while (token != NULL)
{
// Strip out one token and store them into the token array
token = strtok(NULL, delimiter);
tc++;
tokens[tc] = token;
}
sData->tokens = tokens;
for(int i=0; i<sData->result; i++) {
printf("%s\n", sData->tokens[i]);
}
}
Result:
Part1
Part2
Part3
3 tokens found
(null)
(null)
(null)
Program ended with exit code: 0
I'm not sure where I'm messing up so I've given a summary of each function so my logic can be checked!
The main program takes arguments from the command line and stores them in char pointer array.
The correct command to run program is ./re-do_hw4_prob6 filename. (filename is sears_kmart_stores_closing_2019.txt in this case)
After checking if argument number is correct, the file is opened.
A while loop copies strings of text from file to buffer until NULL is met.
Then the function getState() is called. The state is printed.
The file is closed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "redo_hw4_functs.h"
int main(int argc, char* argv[])
{
char** states;
FILE* pFile;
char buffer[80];
int i = 0;
if(argc < 2){
printf("Too few arguments! \n");
}
else if(argc > 2){
printf("Too many arguments! \n");
}
pFile = fopen(argv[1], "r");
states = malloc(50*sizeof(char));
for (i = 0; i < 50; i++)
{
states[i] = malloc(3*sizeof(char));
while(fgets(buffer, sizeof(buffer), pFile) != NULL)
{
getState(states[i], buffer);
printf("State: %s \n", states[i]);
}
}
fclose(pFile);
}
The getState() function takes in two char arrays. One to read from the other to copy too.
It tokenizes the string being read from using a comma, a tab, and a new line as the delimiters. -> ",\t\n"
On the last token it copies the last two chars to the empty string array.
//accepts a line of string formatted as expected and stores the store state in char file ¡OJO! This is the hardest one because you cant rely on delimeters alone to find state
void getState(char strState[], char strLine[])
{
int i;
char* token;
char delim[] = ",\t\n";
token = strtok(strLine, delim);
token = strtok(strLine, delim);
token = strtok(strLine, delim);
for(i = (strlen(token) - 2); i < strlen(token); i++)
{
strState[i] =token[i];
}
}
I have also included my other functions to see if there are any other mistakes to be corrected.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "redo_hw4_functs.h"
//accepts a line of string formatted as expected and stores the store name in char file
void getName(char strName[], char strLine[])
{
char* token;
char delim[] = " ,\t\n";
token = strtok(strLine, delim);
while(token != NULL)
{
if(strcmp(token, "sears") == 0 || strcmp(token, "kmart"))
{
strcpy(strName, token);
break;
}
token = strtok(NULL, delim);
}
}
//accepts a line of string formatted as expected and stores the store address in char file
void getAddress(char strAddress[], char strLine[])
{
char* token;
char delim[] = ",\t\n";
token = strtok(strLine, delim);
while(token != NULL)
{
if(isdigit(token[0]) && isalpha(token[sizeof(token)-1]))
{
strcpy(strAddress, token);
break;
}
token = strtok(NULL, delim);
}
}
//accepts a line of string formatted as expected and stores the store city in char file
void getCity(char strCity[], char strLine[])
{
int i;
char* token;
char delim[] = ",\t\n";
token = strtok(strLine, delim);
token = strtok(strLine, delim);
token = strtok(strLine, delim);
for(i = 0; i < (strlen(token) - 3); i++)
{
strcpy(strCity[i], token[i]);
}
}
//accepts a line of string formatted as expected and stores the store state in char file ¡OJO! This is the hardest one because you cant rely on delimeters alone to find state
void getState(char strState[], char strLine)
{
int i;
char* token;
char delim[] = ",\t\n";
token = strtok(strLine, delim);
token = strtok(strLine, delim);
token = strtok(strLine, delim);
for(i = (strlen(token) - 2); i < strlen(token); i++)
{
strcpy(strState[i], token[i]);
}
}
Here is an example of input text that is to be read:
Kmart, 217 Forks Of River Pkwy, Sevierville TN
Kmart, 4110 E Sprague Ave, Spokane WA
Kmart, 1450 Summit Avenue, Oconomowoc WI
Sears, 2050 Southgate Rd, Colorado Spgs CO
Sears, 1650 Briargate Blvd, Colorado Spgs CO
Sears, 3201 Dillon Dr, Pueblo CO
Here is an example of what the program is expected to be outputting:
State:TN
State:WA
State:WI
State:CO
State:CO
State:CO
Here is an example of what the program is outputting:
I assume that you want not only the status but also all the other fields so that you can deal with them later.
The code below may be quite different from yours, but I think that it is easier to use a single function to read each record.
The function read_data() reads data from the file pointer fp and store them in data, which is a pointer to a predefined struct data_t.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 1024
typedef struct {
char name[64];
char addr[64];
char city[64];
char state[8];
} data_t;
int read_data(FILE *fp, data_t *data) {
char buffer[BUFFER_SIZE];
// Read a record. If end-of-file is read, return -1.
if (fgets(buffer, BUFFER_SIZE, fp) == NULL) {
return -1;
}
char delim[] = ",\t\n";
// Find the name of the record.
char *token = strtok(buffer, delim);
strcpy(data->name, token);
// Find the address of the record.
token = strtok(NULL, delim);
while (*token == ' ') {
++token;
}
strcpy(data->addr, token);
// Find the city and status of the record.
// We cannot split them by strtok() easily, so we handle it later.
token = strtok(NULL, delim);
while (*token == ' ') {
++token;
}
// Find the position of the state.
char *ptr = token;
while (*ptr != '\0') {
++ptr;
}
ptr -= 2;
strcpy(data->state, ptr);
// Use NULL to separate the city and the state so that we can use strcpy().
while (*(ptr - 1) == ' ') {
--ptr;
}
*ptr = '\0';
// Copy the city field.
strcpy(data->city, token);
return 0;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "The number of arguments is incorrect.\n");
fprintf(stderr, "Usage: %s filename\n", argv[0]);
return -1;
}
FILE *fp = fopen(argv[1], "r");
data_t *data = malloc(sizeof(data_t));
while (read_data(fp, data) == 0) {
printf("State: %s\n", data->state);
}
free(data);
fclose(fp);
return 0;
}
The way to read data is hard-coded, so if the input format is different, you may need to change the content of read_data(), but it works well for your sample input.
This code reads file and split it lines into array in order to compare lines elements to each other.
The problem that it gives me as the first line
)�H� 2382 2382
I think that the function char **linecontent(char *line) is the problem but I am new in C and I tried every possible solution and I have nothing.
I am very sorry for asking.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char** split(char string[],const char seps[])
{
char ** res = NULL;
char * p = strtok(string, seps);
int n_spaces = 0, i;
while (p) {
res = realloc(res, sizeof (char*) * ++n_spaces);
if (res == NULL) {
exit(-1); /* memory allocation failed */
}
res[n_spaces-1] = p;
p = strtok(NULL, seps);
}
res = realloc(res, sizeof (char*) * (n_spaces+1));
res[n_spaces] = '\0';
return res;
free(res);
}
char** readfile(char *name, int *lsize)
{
FILE *fp;
char *result;
char line[500];
char *pline = NULL;
char **lines = NULL;
int i = 0;
int l = 0;
fp = fopen(name, "r");
while (fgets(line,500, fp)) {
i++;
pline = strdup(line);
lines = (char**)realloc(lines, sizeof (char**) * (++l));
/* Add to lines */
lines[l-1] = pline;
*lsize += 1;
pline = NULL;
}
fclose(fp);
return lines;
}
char** linecontent(char *line)
{
char **linecont;
char hit[300];
strncpy(hit, line, sizeof(hit) - 1);
hit[sizeof(hit) - 1] = '\0';
linecont = split(hit, "\t");
return linecont;
}
int main()
{
char **lines = NULL;
int lsize = 0;
lines = readfile("TEMP", &lsize);
int i = 0;
while (i != lsize) {
char **linecont1;
char *thisline=lines[i];
linecont1=linecontent(thisline);
char *pname1 = linecont1[0];
char *acc1 = linecont1[1];
int start1 = atoi(linecont1[3]);
int miss1 = atoi(linecont1[4]);
printf("%s\t%s\t%d\t%d\n", pname1, acc1, start1, start1);
i++;
}
}
I realize this question has been asked often but I still dont really understand how it works. I want to try and read my file into my structures, inside a function and pass the structures through pointers to the function. I am unsure about how to even write the function prototype.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 90
#define STR 200
#define MAXLEN 40
struct human {
char name[MAXLEN];
char surname[MAXLEN];
int age;
float weight;
};
int main(int argc, char *argv[]) {
char *dlim= " ", *end = "\n";
char *string;
string = (char *)malloc(sizeof(char) * STR);
int i = 0, j = 0;
struct human *man = malloc(sizeof(struct human) * MAX);
FILE *fin = fopen("data.txt", "r");
if (fin == NULL) {
printf("Cannot open file\n");
exit(0);
}
while (fgets (string, STR, fin)){
read (string, &man[i], dlim, end);
i++;
}
fclose(fin);
free(string);
free(man);
return 0;
}
struct human *man read(char *fstring, struct *man, char *div, char *end){
int i=0;
char *tok;
tok = strtok(string, dlim);
strcpy(man[i].name, tok);
tok = strtok(NULL, dlim);
strcpy(man[i].surname,tok);
tok = strtok(NULL, dlim);
man[i].age = atoi(tok);
tok = strtok(NULL, end);
man[i].weight = atof(tok);
return man[i];
}
Whats the function meant to look like? And am i correct in assuming that through the use of pointers, the struct will be automatically be updated in main, without needing to return something in the function?
Can the function also return nothing (void), because the use of pointers will automatically pass onto main?
Thank you!
Your code is close but the pointer logic in the subroutine that fills in the human structure is incorrect. See if the following rework, and slight simplification, helps you understand how to pass the structure:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXIMUM_HUMANS 90
#define MAXIMUM_INPUT_STRING_LENGTH 200
#define MAXIMUM_STRING_LENGTH 40
struct human {
char name[MAXIMUM_STRING_LENGTH];
char surname[MAXIMUM_STRING_LENGTH];
int age;
float weight;
};
void initialize_human(char *string, struct human *man, char *delimiter, char *end) {
char *token;
token = strtok(string, delimiter);
strcpy(man->name, token);
token = strtok(NULL, delimiter);
strcpy(man->surname, token);
token = strtok(NULL, delimiter);
man->age = atoi(token);
token = strtok(NULL, end);
man->weight = atof(token);
}
int main(int argc, char *argv[]) {
char *dlim= " ", *end = "\n";
char string[MAXIMUM_INPUT_STRING_LENGTH];
struct human humans[MAXIMUM_HUMANS];
FILE *fin = fopen("data.txt", "r");
if (fin == NULL) {
fprintf(stderr, "Cannot open file\n");
exit(1);
}
int population; // after loop, this contains total body count
for (population = 0; fgets(string, MAXIMUM_INPUT_STRING_LENGTH, fin); population ++) {
initialize_human(string, &humans[population], dlim, end);
}
printf("population: %d\n", population);
printf("last added: %s who weighs %f\n", humans[population - 1].surname, humans[population - 1].weight); // test if we loaded it up correctly
fclose(fin);
return 0;
}
I tried really hard to search for a solution to this but I can't think of good enough keywords.
Currently I'm having troubles grasping the concept behind makeargv and it's usage with triple pointers (I have no idea what ***foo means, it doesn't seem to be as easy of a concept as **foo or *foo). So I made my own:
const char **makeargv(char *string, int *numargs) {
string = string + strspn(string, delims);
char *copy = malloc(strlen(string) + 1);
int i;
strcpy(copy, string);
int numtokens;
if (strtok(copy, delims) != NULL) {
for (numtokens = 1; strtok(NULL, delims) != NULL; numtokens++) {}
}
strcpy(copy, string);
const char *results[numtokens+1];
results[0] = strtok(copy, delims);
for (i = 1; i < numtokens; i++) {
results[i] = strtok(NULL, delims);
}
results[numtokens+1] = NULL;
*numargs = numtokens;
return results;
}
Here's the part at where it breaks:
void parse_file(char* filename) {
char* line = malloc(160*sizeof(char));
FILE* fp = file_open(filename);
int i = 0;
int numargs = 0;
int *pointer = &numargs;
while((line = file_getline(line, fp)) != NULL) {
if (strlen(line) == 1){
continue;
}
const char **args = makeargv(line, pointer);
printf("%s\n", args[0]);
printf("%s\n", args[1]);
/* This prints out args[0], but then args[1] causes a seg fault. Even if I replace
the args[1] with another args[0] it still causes a seg fault */
}
fclose(fp);
free(line);
}
I have a working array of strings. However when I try to print out the strings in the array, I can only print 1 of my choice and then it seg faults for any subsequent calls. lets pretend my array of strings is argv[3] = {"Yes", "no", "maybe"}, if i call argv[0], it will let me call "Yes", but any other calls (even if i call argv[0] again) do not work and cause a segfault. I can call any of the elements in the array, but once i call one the rest cease to work causing segfaults.
Help please? D: This is in C.
const char *results[numtokens+1];
This array "results" is a local variable, it is only available inside of "makeargv".
You'd better use malloc:
results = malloc(numtokens+1)
And I believe there is memory leak in your code.
You will not be able to free the memory for "char *copy"
char *copy = malloc(strlen(string) + 1);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **makeargv(char *string, int *numargs) {
static const char *delims = " \t\n";
string = string + strspn(string, delims);
char *copy = malloc(strlen(string) + 1), *p = copy;
strcpy(copy, string);
int numtokens;
for (numtokens = 0; strtok(p, delims); ++numtokens, p = NULL);
char **results = malloc(sizeof(char*)*(numtokens+1));
strcpy(copy, string);
int i;
p = copy;
for (i = 0; i < numtokens; ++i, p = NULL)
results[i] = strtok(p, delims);
results[i] = NULL;
*numargs = numtokens;
return results;
}
FILE *file_open(char *filename){
FILE *fp = fopen(filename, "r");
if(!fp){
perror("file_open");
exit(1);
}
return fp;
}
void parse_file(char* filename) {
char* line = malloc(160*sizeof(char));
FILE* fp = file_open(filename);
int i = 0, numargs = 0;
while(fgets(line, 160, fp)){
if (*line == '\n')
continue;
char **args = makeargv(line, &numargs);
for(i = 0;i<numargs;++i)
printf("%s\n", args[i]);
printf("\n");
if(args[0])
free(args[0]);
free(args);
}
fclose(fp);
free(line);
}
int main(int argc, char *argv[]){
parse_file(argv[1]);
return 0;
}