I have a text file that looks like this:
Author; Title
Author; Title
etc...
I need to open this file and read it line by line into a linked list. So far I have this, but I'm not sure how to use strtok() since it's not reading correctly.
Can someone please help me with it?
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
struct node
{
char* author;
char* title;
struct node* next;
};
int main()
{
struct node *root;
struct node *c;
root = malloc( sizeof(struct node) );
root->next = 0;
c = root;
FILE *f;
f = fopen("books.txt", "r");
char line[255];
while( fgets( line, sizeof(line),f) != NULL)
{
char *token = strtok(line, ";");
while(token!=NULL)
{
fread( &c->author, sizeof( c->author ), 1, f );
token = strtok(NULL, " ");
}
fread( &c->title, sizeof( c->title ), 1, f );
//printf("%s",&c->author);
}
fclose(f);
return 0;
}
Looks wrong. You always need to:
Read enough data.
Parse the data.
Allocate memory from heap.
Copy the data.
The line variable is just a temporary buffer, while the char *author and char *title variables can't live without allocated memory. Calling fread() is an utter nonsense in your code. You already called fgets(), that's where you read the data from the file. The rest should be only string operations.
A typical way is to get char *start pointed to the beginning of the data you are interested in, char *end to the first character behind the data you are interested in, and then request heap-allocated copy using author = strndup(start, end-start) or perform the same using a combination of malloc() and memcpy() or strncpy().
Related
I have two char pointers:
char *temp;
char *saveAlias;
I want to assign saveAlias with whatever is stored in temp; saveAlias is currently empty while temp has a string of an unknown size saved from user input.
Note that I don't want saveAlias to point to where temp points; I want the content of temp and assign (get pointed) it to saveAlias.
I have attempted using strcat and strcpy but to no avail.
Assuming that your temp variable points to a character string that is suitably nul-terminated (as strings in C should be), then you can just use the strdup() function to make a copy of it and store a pointer to that in saveAlias. This function will duplicate the given string into newly-allocated memory; that memory should be released, using the free function, when no longer needed:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char* temp = "source string";
char* saveAlias = strdup(temp);
printf("Temp is <%s> (at %p).\n", temp, (void*)temp);
printf("Alias is <%s> (at %p).\n", saveAlias, (void*)saveAlias);
free(saveAlias);
return 0;
}
The strdup function effectively combines malloc and strcpy into a single function call, with the call shown above being the equivalent of:
char* saveAlias = malloc(strlen(temp) + 1);
strcpy(saveAlias, temp);
If you want to allocate memory for a copy of the string currently pointed to by temp, use strdup():
#include <stdio.h>
#include <string.h>
int main() {
char buf[128];
char *temp;
char *saveAlias = NULL;
if ((temp = fgets(buf, sizeof buf, stdin)) != NULL) {
saveAlias = strdup(temp);
if (saveAlias == NULL) {
fprintf(stderr, "allocation failed\n");
} else {
printf("saveAlias: %s\n", saveAlias);
}
}
free(saveAlias);
return 0;
}
You can basically point the saveAlias to temp; Thus you would have:
saveAlias = temp;
As noted by Chris. This will make one pointer point to another.
Correcting my answer. I suggest you define de size of saveAlias with malloc, and then use the memcpy function. You will have:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
char *temp = "your_char";
//with malloc you make sure saveAlias will have the same size as temp
//and then we add one for the NULL terminator
char *saveAlias = (char*) malloc(strlen(temp) + 1);
//then just
strcpy(saveAlias, temp);
printf("%s\n", temp);
printf("%s", saveAlias);
return 0;
}
Thank you chqrlie for the explanation also. I was mistaken about the memcpy.
I have read a lot of the answers on the theoretical issues with memory allocation to pointer to arrays, but have not been able to fix my code...so turning to you.
I have an array of strings in a STRUCT, which I need to write to and read from. Declared as:
typedef struct client_mod
{
/* Client ad_file */
char *ad_filenames[10];
/* Client's current ad array index*/
unsigned int ad_index;
} client;
Then , inside a function , I assign values to pointer:
static int get_spots (client_mod *client)
{
char buf[512];
FILE *ptr;
if ((ptr = popen("php /media/cdn/getspot.php", "r")) != NULL) {
/* Read one byte at a time, up to BUFSIZ - 1 bytes, the last byte will be used for null termination. */
size_t byte_count = fread(buf, 1, 512 - 1, ptr);
/* Apply null termination so that the read bytes can be treated as a string. */
buf[byte_count] = 0;
}
(void) pclose(ptr);
// parse extracted string here...
int i = 0;
client->ad_filenames[i] = strdup(strtok(buf,"|"));
while(client->ad_filenames[i]!= NULL && i<5)
{
client->ad_filenames[++i] = strdup(strtok(NULL,"|"));
if (client->ad_filenames[i] != NULL && strlen(client->ad_filenames[i]) > 5) {
LOG("TESTING FOR CORRECT FILE NAMES %s\n", client->ad_filenames[i]);
}
}
}
The problem comes when I retreive the values later:
/* in looping code block */
LOG("Checking file under index = %d, file is %s", client->ad_index, client->ad_filenames[client->ad_index]);
The first two members of the array are retreived normally, everything after that is garbled.
I would appreciate any guidance. Thanks!
I understand this probablby comes from undefined behaviour of assigning directly to the pointer, but I can't figure out how to solve it.
I think the problem is with assigning to this struct element.
char *ad_filenames[10];
ad_filenames is an array of 10 of pointer to characters.
What that means is that memory allocation is needed for each index.
Something like
client->ad_filenames[0] = strdup(var1);
strdup() does both malloc() and strcpy() within this function.
client should be a variable name. You already defined client as a type.
Here is working code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct client_mod
{
/* Client ad_file */
char *ad_filenames[10];
/* Client's current ad array index*/
unsigned int ad_index;
}CLIENT1;
CLIENT1 *client;
int func( char *var1 ) {
client->ad_filenames[0] = strdup(var1);
}
int
main(void)
{
char str1[10];
client = malloc( sizeof client );
strcpy( str1, "Hello" );
func( str1 );
printf("%s\n", client->ad_filenames[0] );
free(client->ad_filenames[0]);
free (client);
}
Your problem is with the line,
size_t byte_count = fread(buf, 1, 1000 - 1, ptr);
Read the man fread page,
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
you read 1000-1 members of size 1 into buf, which is only allocated buf[512], either expand buf or decrease fread 3rd argument,
buf[1000+1];
size_t byte_count = fread(buf, 1, sizeof(buf)-1, ptr);
Im trying to read into a linked list from a textfile. The text file has the title of the book, author and year separated by ":". each book is on a separate line. the textfile entries look like this:
Absalom, Absalom!:William Faulkner:1936
After Many a Summer Dies the Swan:Aldous Huxley:1939
Ah, Wilderness!:Eugene O'Neill:1933
i'm rewriting it from scratch. comments would be appreciated.
#include <stdlib.h>
#include <stdio.h>
struct BookNode
{
char linebuffer[128];
char delim[]=":";
char * Title[50];
char * Author[50];
char * Year[5];
struct BookNode *next;
// char *token = NULL;
};
int main(void)
{
static const char booklist[]= "booklist.txt";
FILE *fr=fopen("booklist.txt", "r");
if ( fr != NULL)
{
char Title[50];
char Author[50];
char Year[5]
struct BookNode Booknode;
while (fgets(linebuffer,128, fr) != NULL &&
sscanf(line, "%49s %49s %4s",
&BookNode.Title, BookNode.Author, BookNode.Year)==3)
{
printf("%50s %50s %5s",
BookNode.Title, BookNode.Author, BookNode.Year);
}
}
There are multiple problems in your code right now.
The first one (I kid you not) is with code formatting and indentation. Your pasted sample didn't have a regular format or indentation to speak of. It's more difficult to follow code flow even in short samples such as this. Always indent your code, and pick a coding style (there are several) and stick to it.
Regarding code flow, the first problem is in error checking. Namely, you check for fopen return status, but do not take sufficient action should opening a file fail.
The second problem is a conceptual one. You don't seem to realise that an array of N characters can only hold a string with a lenght of N-1. Therefore, char[4] is hardly ever a suitable format for storing years as strings.
Now that those issues have been dealed with, here are the actual flaws that would prevent your code from working in any case:
1) The fgets function will read up until it either fills your buffer or reaches an end-of-line or an end-of-file character. Yet you still call fgets thrice to try and read a single-line entry in your file. It's unlikely what you want to do. You have to re-think the contents of your loop.
2) Your "main" loop condition is likely to be flawed. This is a very common misunderstanding of the use of feof & co. Assuming your data file contains a newline at the end (and it would only be regular for it to do so), your loop will execute one time too many.
It's better to structure your line reading loops like this:
while (fgets(buffer, BUF_SIZE, stdin)) { /* parse buffer */ }
3) You have elementary problems with memory management in your code: namely, the function addEntry fails to allocate memory to store your records. Instead, all entries in your linked list will end up pointing to the same shared buffer you allocate in your main function.
There are several ways to fix this. One is to use several calls to malloc for each member of your BookNode structs (title, author, and year). Another, perhaps preferable method is to use variable-size structs, like this:
struct BookNode {
char *title;
char *author;
char *year;
struct BookNode *next;
char buffer[]; // this shorthand requires C99
};
For each struct BookNode you allocate enough storage after them, so that you can copy there the contents of your shared buffer. title, author, and year then point to this appended storage. This way you won't end up overwriting the contents of other BookNodes in the next iteration of your loop. And you only need one free to free an entire node.
I probably didn't list all the problems in your code here. Perhaps instead of another rewrite, you should first try to tackle a smaller subproblem such as reading a single entry from stdin and building up from there?
addEntry should allocate memory for title, author and year.
Also, doing fgets three times would read 3 lines. You need one fgets per loop, and split the result to different parts (e.g. using strtok_r).
What you do is save a pointer to a static buffer. When reading the next line, this buffer is overwritten with new data.
Note that if you allocated data, you must eventually free it. The entry's destructor will need to free.
example of strtok
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char line[] = "Absalom, Absalom!:William Faulkner:1936\n";
char *p;
char * Title;
char * Author;
char * Year;
p = strtok(line, ":");
Title = strdup(p);
Author = strdup(strtok(NULL, ":"));
Year = strdup(strtok(NULL, ": \n"));
printf("\"%s\",\"%s\",\"%s\"\n", Title, Author, Year);
free(Title);
free(Author);
free(Year);
}
//result:"Absalom, Absalom!","William Faulkner","1936"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct BookNode {
char * Title;
char * Author;
char * Year;
struct BookNode * next;
} * head;
void addEntry(char * T, char * A, char * Y);
void display();
int numEntries();
//void writeBookData(struct BookNode * selection);
void free_book(struct BookNode *bnp){
if(bnp == NULL) return;
free(bnp->Title);
free(bnp->Author);
free(bnp->Year);
free_book(bnp->next);
free(bnp);
}
int main() {
FILE * fpointer;
fpointer=fopen("booklist.txt","r");
if(fpointer == NULL){
printf("Booklist could not be opened.\n");
exit(EXIT_FAILURE);
}
char Title[50+1];
char Author[50+1];
char Year[4+1];
head = NULL;
while (EOF!=fscanf(fpointer, "%50[^:]%*c%50[^:]%*c%4[^\n]%*c", Title, Author, Year)){
//note:The input number of characters is limited (Eg50), it (because minutes in excess of the limit is used in the following items) there must be large enough.
addEntry(Title, Author, Year);
}
fclose(fpointer);
int entryCount = numEntries();
printf("There are %d entries in this Book list\n", entryCount);
display();
free_book(head);
return 0;
}
void addEntry(char * T, char * A, char * Y){
struct BookNode * tempNode, * iterator;
tempNode = (struct BookNode *)malloc(sizeof(struct BookNode));
tempNode->Title = (char *)malloc(strlen(T)+1);
strcpy(tempNode->Title, T);
tempNode->Author = (char *)malloc(strlen(A)+1);
strcpy(tempNode->Author, A);
tempNode->Year = (char *)malloc(strlen(Y)+1);
strcpy(tempNode->Year, Y);
tempNode->next = NULL;
iterator = head;
if (head == NULL){
head = tempNode;
} else {
while(iterator->next != NULL){
iterator = iterator->next;
}
iterator->next = tempNode;
}
}
int numEntries(){
if(head == NULL)
return 0;
else{
int count;
struct BookNode *iterator;
for(count=0, iterator=head; iterator!=NULL; iterator = iterator->next, ++count)
;
return count;
}
}
void display(){
if(head == NULL)
return ;
else{
struct BookNode *iterator;
for(iterator=head; iterator!=NULL; iterator = iterator->next)
fprintf(stdout, "%s:%s:%s\n", iterator->Title, iterator->Author, iterator->Year);
}
}
I am new to linked list in C and the problem I have is that I am trying to make a linked list of Strings, but when I try to print that list it prints first char from two different strings. I think I am messing some pointers. Any help please?
Here is my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
typedef struct _song{char *songTitle; char *songAuthor; char *songNote; struct _song *next;}SONG;
int songCount =4;
char SongTitle[songCount];
char AuthorName[songCount];
char SongNotes[songCount];
char songTitle0[21] = "19 problems";
char songArtist0[21]="JayZ";
char songNotes0[81]="JiggaWhoJiggaWhat";
SongTitle[0]=*songTitle0;//points at string songTitle0
AuthorName[0]=*songArtist0;
SongNotes[0]=*songNotes0;
char songTitle1[21] = "Cig Poppa";
char songArtist1[21]="Biggie Smalls";
char songNotes1[81]="I Luv it When you call me big poppa";
SongTitle[1]=*songTitle1;
AuthorName[1]=*songArtist1;
SongNotes[1]=*songNotes1;
SONG *CurrentSong, *header, *tail;
int tempCount=0;
header = NULL;
for(tempCount=0;tempCount<songCount;tempCount++)
{
CurrentSong = malloc(sizeof(struct _song));
CurrentSong->songTitle= &SongTitle[tempCount];
CurrentSong->songAuthor=&AuthorName[tempCount];
CurrentSong->songNote=&SongNotes[tempCount];
if(header == NULL)
{
header=CurrentSong;//head points to first thing in memory
}
else
{
tail->next=CurrentSong;
}
tail = CurrentSong;//always the last thing in the list
tail->next=NULL;//the next pointer is null always
}
tempCount =0;
for(CurrentSong=header; CurrentSong!=NULL; CurrentSong=CurrentSong->next)
{
printf("\n%d: ", tempCount);
printf("Title: %s ",CurrentSong->songTitle);
printf("Author: %s ",CurrentSong->songAuthor);
tempCount++;
}
return 0;
}
That's not how you're supposed to use linked lists.
It's
typedef struct list {
void *data;
struct list *next;
}
SONG *s = (SONG *)songList->data;
Likewise, for cloning strings, you need to use strdup.
E.g.
s->songTitle = strdup(SongTitle);
s->songAuthor = strdup(AuthorName);
s->songNote = strdup(SongNotes);
Don't forget to free the strings once you're done with them.
SongTitle[0]=*songTitle0;//points at string songTitle0
The comment is not true. You're copying the first character songTitle0 into the first position of SongTitle.
Your setup far too complex. You'll want to just assign songTitle0, without any * or &, to the songTitle element of the list's first link; both are of type char*, so that's just a pointer copy. Skip the SongTitle, AuthorName and SongNotes variables, they serve no purpose.
The three variables SongTitle, AuthorName and SongNotes are array of char, not array of string. You need to change their declaration to:
char* SongTitle[songCount];
char* AuthorName[songCount];
char* SongNotes[songCount];
Then, you need to update them like that:
SongTitle[0] = songTitle0;//points at string songTitle0
AuthorName[0] = songArtist0;
SongNotes[0] = songNotes0;
And when you store them in the linked list:
CurrentSong = malloc(sizeof(struct _song));
CurrentSong->songTitle = SongTitle[tempCount];
CurrentSong->songAuthor = AuthorName[tempCount];
CurrentSong->songNote = SongNotes[tempCount];
I am writing a software in C. For that purpose I use lex. I wrote a piece of code in C to create a symbol table and manage it. So, whenever lex finds a new symbol, it puts it in a symbol table. Problem is, when I try to print all results from symbol table, I get output I didn't expect.
If, for example, the input file was:
int main(){}
the output should be:
int
main
(
)
{
}
but the output is:
int main(){}
main(){}
(){}
...
and so on.
The function used for printing is something like this
void print_entries(struct symtab *start) {
struct symtab *s = start;
while(s != NULL) {
printf("%s\n", s->name);
s = s->next;
}
}
Here is the code for adding new symbols:
void add_entry(char* name, int type, struct symtab *start)
{
struct symtab *new;
new = malloc(sizeof(struct symtab));
last_entry(start)->next = new;
new->name = name;
new->type = type;
new->next = NULL;
}
Any ideas?
You need to copy the symbol names into the symbol table entries. If for some peculiar reason your system does not have strdup() already, then use:
#include <string.h>
#include <stdlib.h>
char *strdup(const char *str)
{
size_t len = strlen(str) + 1;
char *dup = malloc(len);
if (dup != 0)
memmove(dup, str, len);
return dup;
}
(In this context, I could use memcpy() safely; I use memmove() because it always works and memcpy() does not. And I use memmove() because I know exactly how long the string is so the copy doesn't need to test each character for nullness as it goes.)
With strdup() on hand:
void add_entry(char* name, int type, struct symtab *start)
{
struct symtab *sym;
sym = malloc(sizeof(struct symtab));
last_entry(start)->next = sym;
sym->name = strdup(name);
sym->type = type;
sym->next = NULL;
}
Note that this still omits the error checking from the two memory allocations, which is not a good habit to get into. I've revised it to use sym rather than new because the latter is a C++ keyword and I avoid using those as identifiers, even in C code.