I need some help with an assignement I have to do for school which consists in sorting some books after the title,author and the publication date of it. All the infos are given as a string in a txt file using a delimiter between them. The problem is that I don't manage to properly read the data, my program crashes after I try to perform a strcpy(). Can you guys help me with this and tell me what have I done wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct book
{
char author[100],title[100];
int year;
};
int main()
{
struct book b[25];
int i,n;
char intro[25][150],*p;
const char delim[2] = "#";
FILE *fp;
fp=fopen("text.txt", "r");
fscanf(fp,"%d",&n);
for(i=0;i<=n;i++)
{
fgets(intro[i], sizeof (intro[i]), fp);
p=strtok(intro[i], delim);
strcpy(b[i].title,p);
p=strtok(NULL, delim);
strcpy(b[i].author,p); /// The program works until it reaches this point - after performing this strcpy() it crashes
if(p!=NULL)
{
p=strtok(NULL,delim);
b[i].year=atoi(p);
}
}
return 0;
}
An example of input could be this:
5
Lord Of The Rings#JRR Tolkien#2003
Emotional Intelligence#Daniel Goleman#1977
Harry Potter#JK Rowling#1997
The Foundation#Isaac Asimov#1952
Dune#Frank Herbert#1965
The problem is with the newline left in the file after the initial fscanf() call.
This
fscanf(fp,"%d",&n);
reads the 5 and the subsequent fgets() reads just the \n. So this is not what you want. Read n using fgets() and convert it into integer using sscanf() or strto*. For example, instead of the fscanf() call, you can do:
char str[256];
fgets(str, sizeof str, fp);
sscanf(str, "%d", &n);
to read the n from file.
You should also check if strtok() returns NULL. If you did, you would have figured out the issue easily.
Also, your need to go from 0 to n-1. So the condition in the for loop is wrong. It should be
for(i=0; i<n; i++)
Related
I am writing a simple c program that reads in a bunch of values from stdin using getline and sscanf.
The program apparently works: I invoke the function reading the line and print the results with printf. The problem arises when I just tried to print the values again, and the output is gibberish!
-------------------------- EDIT -----------------------------
I found the problem, reported in my own answer below. Thanks everyone for pointing me in the right direction!
My test input file is (with the 2 first lines to be skipped):
//---------------------------------------------------------------------------------
// MODEL VALUE
500 // X DIR
600 // Y DIR
10.0 // Grid Spacing
2e-4 // Time step
80001 // Max run iterations
And my code is:
Functions to read different kinds (separate from main just for clearness in the post)
int readint(void)
{
char *line = NULL;
size_t size;
int my_int;
getline(&line, &size, stdin);
sscanf(line,"%d",&my_int);
return my_int;
}
double readfloat(void)
{
char *line = NULL;
size_t size;
float my_float;
getline(&line, &size, stdin);
sscanf(line,"%f",&my_float);
return my_float;
}
char* readstr(void)
{
char *line = NULL;
size_t size;
getline(&line, &size, stdin);
char * token = strtok(line, " ");
return token;
}
int readvoid(void)
{
char *line = NULL;
size_t size;
getline(&line, &size, stdin);
return 0;
}
Main
#include <stddef.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
int dum;
//- read in input file
//---------------------------------------------------------------------------------
// MODEL VALUE
dum=readvoid();
dum=readvoid();
#define Nx (readint()) // run on 64 nodes
#define Ny (readint()) // Y DIR
#define dx (readfloat()) // Grid Spacing
#define dt (readfloat()) // Time step
#define Max_Time (readint()) // Max run iterations
printf("model: %d,%d,%f,%f,%d\n",Nx,Ny,dx,dt,Max_Time);
printf("model: %d,%d,%f,%f,%d\n",Nx,Ny,dx,dt,Max_Time);
printf("model: %d,%d,%f,%f,%d\n",Nx,Ny,dx,dt,Max_Time);
return 0;
}
With the first printf working just fine and the others outputting random values.
What am I doing wrong? I am new to c but have experience in fortran, shell, python, matlab and am a bit lost.
Of course the responses are "gibberish". my_int is not initialized, so if sscanf does not read a value, then readint is returning an uninitialized variable. (aka, "gibberish"). Since you've already read the file and not rewound, getline is (probably) returning -1 and sscanf is not assigning a value. But you can't tell, because your program does not check the values returned by either getline or sscanf.
You need to rewind the stream if you want to read it again.
Also, you cannot interleave the calls to readint and readfloat in the parameters list of printf. The order in which they will be called is not fixed.
I found out the problem, thanks to above comments:
I naively thought that #define was able to read the output of e.g. readint() and store it as an object-like macro (e.g. #define Nx 500), but apparently this is not the case.
Thanks everyone!
#include <stdio.h>
#include <string.h>
void find_track(char *search_for);
char tracks[][80] = {
"I left my heart in Harvard Med School",
"Newark, Newark - a wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima"
};
int main() {
char *to_search_str;
printf("Search for: ");
fgets(to_search_str, 80, stdin);
find_track(to_search_str);
return 0;
}
void find_track(char *search_for) {
int i;
for (i=0; i<5; i++) {
if (strstr(tracks[i], search_for)) {
printf("Track %d: '%s'\n", i, tracks[i]);
}
}
}
The program is supposed to search for a string in every string in the tracks multi dimensional array but the strstr() function in the find_track is always returning null no matter the input (even if we input the a sub string of a string from tracks multi dimensional array). I don't know why this is happening?
EDIT:
After correction
#include <stdio.h>
#include <string.h>
void find_track(char *search_for);
char tracks[][80] = {
"I left my heart in Harvard Med School",
"Newark, Newark - a wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima"
};
int main() {
char to_search_str[80];
printf("Search for: ");
fgets(to_search_str, 80, stdin);
to_search_str[strlen(to_search_str)-1] = '\0';
find_track(to_search_str);
return 0;
}
void find_track(char *search_for) {
int i;
for (i=0; i<5; i++) {
if (strstr(tracks[i], search_for)) {
printf("Track %d: '%s'\n", i, tracks[i]);
}
}
}
Output
Most likely the issue with input via fgets().
You are reading into an uninitialized pointer to_search_str, which does not point to a valid memory location. In this case, you can simply change this to an array, like char to_search_str[80] = {0}; and get done with it.
You need to trim the trailing newline that's stored in the input buffer.
From the man page, (emphasis mine)
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.
A quick way of getting that done is to_search_str[strcspn(to_search_str, "\n")] = 0;, but there are more robust ways mentioned in this other answer
You aren't allocating to_search_str pointer, the char * pointer you pass to fgets as the destination buffer. Being it actually uninitialized, this causes undefined behavior that normally ends with a program crash.
You just need to allocate it, statically or dynamically.
The simplest solution consists in just defining a static array in the stack:
#include <string.h>
#define LEN 80
int main() {
char to_search_str[LEN];
printf("Search for: ");
fgets(to_search_str, LEN, stdin);
/* Remove trailing newline from the string to search */
to_search_str[strcspn(to_search_str, "\n")] = 0;
find_track(to_search_str);
return 0;
}
The size of the array is 80 because you use this number as the size parameter in fgets. Please note the use of a #define for the constant 80, making possible to change it in a easier way.
The dynamic allocation in the heap involves the use of malloc() function (and free() as soon as the array is not needed anymore):
#include <string.h>
#define LEN 80
int main() {
char * to_search_str = malloc(LEN);
printf("Search for: ");
fgets(to_search_str, LEN, stdin);
/* Remove trailing newline from the string to search */
to_search_str[strcspn(to_search_str, "\n")] = 0;
find_track(to_search_str);
free(to_search_str);
return 0;
}
Note: since fgets retains trailing newline ``\n'` in the output buffer, we have to remove it. I used the clever oneliner solution described here.
char *to_search_str; is an uninitialized pointer, writing to it will result in undefined behavior. You have to allocate memory or use an array instead char to_search_str[100]; for example.
Also don't forget that fgets will also read the newline into the buffer, which you have to remove.
This code snippet in main
char *to_search_str;
printf("Search for: ");
fgets(to_search_str, 80, stdin);
invokes undefined behavior because the pointer to_search_str is not initialized and has indeterminate value.
It seems you at least mean
char to_search_str[80];
printf("Search for: ");
fgets(to_search_str, 80, stdin);
The function fgets can append the new line character '\n' to the entered string.
You need to remove it for example the following way
to_search_str[ strcspn( to_search_str, "\n" ) ] = '\0';
The function find_track should be declared at least like
void find_track( const char *search_for);
Though it is a bad idea when a function definition relies on global variables.
Also the approach of finding relevant strings is not good. For example the user can enter a string that contains only one character 'a'. In this case all records will satisfy the condition. You should check that the searched string forms a word (a sequence of characters separated by spaces) in a string in the array.
I am writing a program to intake exactly 5 peoples last names and their votes. Which will display the names of the people entered, the corresponding votes, and also the winner.
I need the names of the people into one array of strings. That is where the program crashes. Not sure if I can modify this to make it work or if I need to redo it.
the malloc function seems to be a recurring fix for this type of problem ?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int TotalVotes(int voteArray[],int size)
{
int Sum=0;
for (int i=0;i<size;i++)
{
Sum+=voteArray[i];
}
return Sum;
}
int Winner(int voteArray[],int size)
{
int max;
max=0;
if (voteArray[1]>voteArray[max])
max=1;
if (voteArray[2]>voteArray[max])
max=2;
if (voteArray[3]>voteArray[max])
max=3;
if (voteArray[4]>voteArray[max])
max=4;
return max;
}
void main()
{
char nameArray[5];
int voteArray[5],Total,winner;
for (int i=0;i<5;i++)
{
voteArray[i]=0;
}
for (int j=0;j<5;j++)
{
printf("Enter the name of the candidate number %d\n",j+1);
scanf("%s",nameArray[j]);
printf("Enter that persons number of votes\n");
scanf("%d",&voteArray[j]);
}
Total=TotalVotes(voteArray,5);
winner=Winner(voteArray,5);
printf("%s\t%s\t%s\n","Candidate","Votes Received","% of Total Votes");
for (int y=0;y<5;y++)
{
printf("%s\t%d\t%0.2f\n",nameArray[y],voteArray[y],(float)voteArray[y]/Total);
}
printf("The Winner of The Election is %s\n",nameArray[winner]);
}
char nameArray[5]; is should be like char nameArray[5][20];
Yeah, in C, strings are represented by character arrays (char* or char[]).
Also, you should get fgets instead of scanf with strings for two reasons:
Fgets helps prevent buffer overflow because it knows the size of the string in advance.
Fgets will always run because it does not leave characters in the input buffer like scanf does.
The prototype for fgets looks somewhat like this (you can use stdin for the FILE pointer to read in from the keyboard, but realize that fgets keeps newlines):
fgets( char *output_variable, unsigned int string_length, FILE *input_file );
Also, if you use scanf, you should do a lot more error checking for invalid input.
I want to take input of a particular part of a string like
"First (helloWorld): last"
From that string I want to take input only "helloWorld" by regular expression. I am using
%*[^(] (%s):"
But that does not serve my purpose. Please somebody help me to solve this problem.
The format specifiers in the scanf family of functions are not generally considered to be a species of regular expression.
However, you can do what you want something like this.
#include <stdio.h>
int main() {
char str[256];
scanf("First (helloWorld): last", "%*[^(](%[^)]%*[^\n]", str);
printf("%s\n", str);
return 0;
}
%*[^(] read and discard everything up to opening paren
( read and discard the opening paren
%[^)] read and store up up to (but not including) the closing paren
%*[^\n] read and discard up to (but not including) the newline
The last format specifier is not necessary in the context of the above sscanf, but would be useful if reading from a stream and you want it positioned at the end of the current line for the next read. Note that the newline is still left in the stream, though.
Rather than use fscanf (or scanf) to read from a stream directly, it's pretty much always better read a line with fgets and then extract the fields of interest with sscanf
// Read lines, extracting the first parenthesized substring.
#include <stdio.h>
int main() {
char line[256], str[128];
while (fgets(line, sizeof line, stdin)) {
sscanf(line, "%*[^(](%127[^)]", str);
printf("|%s|\n", str);
}
return 0;
}
Sample run:
one (two) three
|two|
four (five) six
|five|
seven eight (nine) ten
|nine|
Sorry, no true regular expression parser in standard C.
Using the format in the scanf() family is not a full-fledged regular expression, but can do the job. "%n" tells sscanf() to save the current scanning offset.
#include <stdio.h>
#include <stdlib.h>
char *foo(char *buf) {
#define NotOpenParen "%*[^(]"
#define NotCloseParen "%*[^)]"
int start;
int end = 0;
sscanf(buf, NotOpenParen "(%n" NotCloseParen ")%n", &start, &end);
if (end == 0) {
return NULL; // End never found
}
buf[end-1] = '\0';
return &buf[start];
}
// Usage example
char buf[] = "First (helloWorld): last";
printf("%s\n", foo(buf));
But this approach fails on "First (): last". More code would be needed.
A pair of strchr() calls is better.
char *foo(char *buf) {
char *start = strchr(buf, '(');
if (start == NULL) {
return NULL; // start never found
}
char *end = strchr(start, ')');
if (end == NULL) {
return NULL; // end never found
}
*end = '\0';
return &start[1];
}
Else one needs to use a not-part-of-the C-spec solution.
I make some research in order to look for the sscanf() source code . But I could not find the answer to my question.
when we use sscanf() in this way:
char str[50] = "5,10,15";
int x;
sscanf(str,"%d,%s",&x,str);
Does sscanf() support "recursive" buffer str ?
It doesn't break from self modifying buffer. But to make it (tail)recursive, you'd have to read to the end of the string.
The code fragment:
char str[]="5,10,15";
int a[10]={0},x = 0;
while (sscanf(str,"%d,%s",a+ x++,str)>1);
reads all the integers.
Since this is not really recursive and the string to be read doesn't overwrite the asciiz in the string, I believe this is "safe" in the meaning: only try this at home.
scanf family functions read the format specifiers and do the conversion one-by-one. In your example code:
char str[50] = "5,10,15";
int x;
sscanf(str,"%d,%s",&x,str);
It may work because it reads in an integer first and another c-string. No problem here as original str is over-written with the new value.
But consider the following:
char str[50] = "5 10 15";
int x;
sscanf(str,"%s %d",str, &x);
It first reads 5 from the original value and overwrites str and subsequent format specifier %d will have nothing to read from str as the end of str has been reached due to the nul-termination for the previous %s read.
It's just a counter example to show it's a bad idea and won't work. So you can say this is going to invoke undefined behaviour at some point or cause other problems.
Generally I would advise against this. If it works once it doesn't mean, that it will in all corner cases. To be really sure, you'd have to check sources for the very implementation you are using. If you want it to be portable, forget about it right away, unless you see it written in the libc specs as a guaranteed behaviour. Use strchr() to find the next delimiter and update the string pointer to point at the next character.
The use of strtok() solved an almost identical problem I had, namely, using sscanf() recursively to extract the contents of a char* (a list of double's separated by a space, e.g. "1.00 2.01 ...") into a double array (e.g. array[1]=1.00, array[2]=2.01, ...).
The following might help, if I believe I understand your issue:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
char str[]= "5,10,15";
char* token;
int x[3];
token = strtok(str, ",");
for(int i=0; i<3; i++){
sscanf(token, "%d", &x[i]);
token = strtok(NULL, ",");
}
for(int i=0; i<3; i++) printf("%d\n", x[i]);
return 0;
}
On running, it produces:
5
10
15
See http://www.cplusplus.com/reference/cstring/strtok/ for a useful description of strtok().