I am new to C programming, so I am having difficulties with the problem below.
I have a text file inp.txt which contains information like the following:
400;499;FIRST;
500;599;SECOND;
670;679;THIRD;
I need to type a number and my program needs to compare it with numbers from the inp.txt file.
For example, if I type 450, it's between 400 and 499, so I need write to the word FIRST to the file out.txt
I have no idea how to convert a character array to an int.
I think you'll want these general steps in your program (but I'll leave it to you to figure out how you want to do it exactly)
Load each of the ranges and the text "FIRST", "SECOND", etc. from the file inp.txt, into an array, or several arrays, or similar. As I said in the comment above, fscanf might be handy. This page describes how to use it - the page is about C++, but using it in C should be the same http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/. Roughly speaking, the idea is that you give fscanf a format specifier for what you want to extract from a line in a file, and it puts the bits it finds into the variables you specify)
Prompt the user to enter a number.
Look through the array(s) to work out which range the number fits into, and therefore which text to output
Edit: I'll put some more detail in, as asker requested. This is still a kind of skeleton to give you some ideas.
Use the fopen function, something like this (declare a pointer FILE* input_file):
input_file = fopen("c:\\test\\inp.txt", "r") /* "r" opens inp.txt for reading */
Then, it's good to check that the file was successfully opened, by checking if input_file == NULL.
Then use fscanf to read details from one line of the file. Loop through the lines of the file until you've read the whole thing. You give fscanf pointers to the variables you want it to put the information from each line of the file into. (It's a bit like a printf formatting specifier in reverse).
So, you could declare int range_start, range_end, and char range_name[20]. (To make things simple, let's assume that all the words are at most 20 characters long. This might not be a good plan in the long-run though).
while (!feof(input_file)) { /* check for end-of-file */
if(fscanf(input_file, "%d;%d;%s", &range_start, &range_end, range_name) != 3) {
break; /* Something weird happened on this line, so let's give up */
else {
printf("I got the following numbers: %d, %d, %s\n", range_start, range_end, range_name);
}
}
Hopefully that gives you a few ideas. I've tried running this code and it did seem to work. However, worth saying that fscanf has some drawbacks (see e.g. http://mrx.net/c/readfunctions.html), so another approach is to use fgets to get each line (the advantage of fgets is that you get to specify a maximum number of characters to read, so there's no danger of overrunning a string buffer length) and then sscanf to read from the string into your integer variables. I haven't tried this way though.
Related
I am trying to read a time like data (i.e. 10:55) from a text file and assign it in such a way that I can do calculations with it. For instance if I want to covert 10 hrs and 55 minutes from 10:55 into minutes:
10*60+55=655. But 10:55 contains ':' so I can't directly assign it to an integer.
So far I was able to solve this problem using atoi(char *ptr)function which coverts the numbers inside a string into integer type data.The code is running just fine but I am not sure how this function works. Because the string "10:55" contains a non numeric value so shouldn't it return junk value immediately when it detects ":".But in my case it returns 10 and then, after I shifted the pointer by 3 places it gives 55.
So it will be really helpful if someone elaborates how this function exactly works.
Finally is there any other way of solving this problem without using atoi() function and only using basic C programming techniques. If yes then please share your code and explanation with me.Thanks a lot for helping. Here is my code:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int hh,mm;
char startingtime[6];
int strtimearr[2];
FILE *start=fopen("start.txt","w");
if(start==NULL)
{
printf("FILE DOES NOT EXIST");
}
printf("START TIME\n");
printf("->HOUR(hh):");
scanf("%d",&hh);
printf("->MINUTE(mm):");
scanf("%d",&mm);
fprintf(start,"%d:%d",hh,mm);
fclose(start);
start = fopen("start.txt", "r");
if(start==NULL)
{
printf("FILE DOES NOT EXIST");
}
fscanf(start, "%s", startingtime);
fclose(start);
//puts(startingtime);
char *s1= startingtime;
strtimearr[0] = atoi(s1);
strtimearr[1] = atoi(s1+3);
printf("Time : %d:%d \nconverted to minute : %d",strtimearr[0],strtimearr[1],(strtimearr[0]*60+strtimearr[1]));
}
It works but is not robust. If the user gives a number greater than 99 for hour or minute, you will write more than 5 bytes to the file. Then at read time you will read past end of startingtime array invoking Undefined Behavious. On the other hand, if hour is less than 10, the minute field will start at position 2 and not 3.
For IO, the rule is be strict when writing and tolerant when reading. So I advise you to use a stricter format for writing: fprintf(start,"%2d:%02d",hh,mm); or fprintf(start,"%02d:%02d",hh,mm); to be consistent for values less than 10.
At read time, you could at least limit size with fscanf(start, "%5s", startingtime); and then decode with sscanf:
int cr = sscanf(startingtime, "%d:%d", strtimearr, strtimearr+1);
Or directly decode at read time with fscanf:
int cr = fscanf(start, "%d:%d", strtimearr, strtimearr+1);
and control that cr is 2 before printing the result.
How to read a time like “10:55” from a text file
You could use fscanf, or combine fgets (or maybe getline(3) or even on Linux readline(3) ...) and sscanf or strtol but then don't forget to test its return value (some int for fscanf etc...). You might be interested by the %n format control string directive to fscanf or sscanf(3) ....
You could, on POSIX or Linux systems, use time(7) facilities. So #include <time.h> then use strptime(3) or getdate(3).
You could read byte by byte and use fgetc. Check for error conditions (at least EOF). Be aware of UTF-8, in 2020 it is almost everywhere ....
Don't forget to check for parsing errors.
A time like 123:456 in your text file probably don't make sense, and you should check for such input mistakes (and probably give at least the line number where such an input does not make sense).
Read at least about errno(3) and perror(3)
Your program should use perror or strerror(3) when fopen fails. Be also aware that stdout is usually line buffered (except in command pipelines). Consider using fflush and/or setvbuf.
Read of course how to debug small programs and what every C programmer should know about undefined behavior. Consider reading the C11 standard n1570 ...
Enable all warnings and debug info when compiling. If you use a recent GCC compiler on your C code, compile it with gcc -Wall -Wextra -g.
The first part of your question, was already answered as a comment.
Regarding the second question, you could use fscanf to read the integer values directly from the file instead of reading a char array and converting it to an int by atoi:
fscanf(start,"%d:%d", &strtimearr[0], &strtimearr[1]);
If fscanf is successful, it returns the count of successfully read items, otherwise EOF is returned.
Making a program that adds user records to text file; so far so good! Yet I ran into a problem that I can not figure out on my own.
int main()
{
FILE *fp;
struct info
{
char name[15];
char surename[15];
char gender[15];
char education[15];
} info;
char c;
int i,j,a;
struct info sem;
beginning:
scanf("%d",&a);
if (a==1)
and at this part if user chooses option one 1, program needs to check all the records per person in txt file and printf me every single person information who has bachelors education.
{
FILE *fp=fopen("info.txt", "r");
char tmp[256]={0x0};
while(fp!=NULL && fgets(tmp, sizeof(tmp),fp)!=NULL)
{
if(strstr(tmp,"bachelors"))
printf("test test");
fprintf(fp, "\n%s %s %s %s %s %s",
sem.name,
sem.surname,
sem.gender,
sem.education,);
}
if(fp!=NULL) fclose(fp);
goto beginning;
}
This code so far detects the word "bachelor" but doesn't want to print out the line where it detected it; any ideas how to solve it? Plus, any suggestions how to make sure program only checks education field and doesn't give me false positive if some one would be named Bachelors?
printf/fprintf procedures use internal buffers to avoid calling the syscall "write" everytime.
I would say that you should add a '\n' character at the end of your string to actually force flushing the buffers writing the test. Moreover your token "bachelors" has to be in the 256 first characters of your file.
Second question depends on how your file is formatted, and you are the only one who knows that.
What output do you actually get? You say it doesn't want to print out the correct lines, but does that mean it prints all lines, no lines, some but not others?
Looking at your program, I would hazard a guess that every line is being printed out. You need braces around if(strstr(tmp,"bachelors")) if you want more than one statement in the body of the if.
EDIT:
In addition to your braces problem, you are attempting to use fprintf to print back to fp, which was opened in read mode (using the r flag). You need to use read/update mode (r+) if you want to modify info.txt. However, this is probably not the way you want to do this.
Firstly, you stated you wanted to "printf" the data, which means printing to standard out. If so then you should use printf instead of fprintf. On the other hand it would seem likely that what you want to do is to read lines from one text file and print data out to another file. In that case you probably want two files.
Even with the above modifications, you will be printing the same data to file each time, since the sem struct is not being updated at all.
Finally, your fprintf format string expects six inputs and you only have four (and an erroneous trailing ,). Why?
All right: So I have a file, and I must do things with it. Oversimplifying, the file has this format:
n
first name
second name
...
nth name
random name
do x⁽¹⁾, y⁽¹⁾ and z⁽¹⁾
random name
do x⁽²⁾, y⁽²⁾, z⁽²⁾
...
random name
do x⁽ⁿ⁾, y⁽ⁿ⁾, z⁽ⁿ⁾
So, the actual details are not important.
The problem is: I'll have to declare a variable n, I have an array name[MAX], and I'll fill this array with the names, from name[0] to name[n-1].
Alright, the problem is: How can I get this input, if I don't know previously how many names do I have?
For example, I could do it just fine if that was an user input, from the keyboard: I would do it like this:
int n; char name[MAX];
scanf( "%d", &n);
int i; for (i = 0; i < n; i++)
scanf( "%s", &N[i]);
And I could go on, do the whole code, but you get the point. But, my input now comes from a file. I don't know how can I get the input, all I can do is to fscanf() the whole file, but since I don't know its size (the first number will determine it), I can't do it. As far as I know (please correct me if that's not true, I am very new to this), we can't use the command "for" and get the numbers gradually as if that was coming from the keyboard, right?
So, the only exit I see is to find a way to read a particular line from the file. If I can do this, the rest is easy. The thing is, how can I do that?
I google'd it, I even found some questions in there, though it didn't make any sense at all. Apparently, reading a particular line from a file is really complicated.
This is from a beginner problem set, so I doubt it is something that complicated. I must be missing something very simple, though I just don't know what it is.
So, the question is: How would you do it, for instance?
How to scan the first number n from the file, and then, scan the others 'n' names, assigning each one to an element in an array (first name = name[0], last name = name[n - 1])?
I would suggest looking into End Of File.
while(!eof(fd))
{
...code...
}
Mind you my C knowledge is rusty, but this should get you started.
IIRC eof returns a value (-1) so that's why you need to compare it to something. Here fd being file descriptor of the file you are reading.
Then after parse of text or count of lines you have your 'n'.
EDIT: Since I'm obviously more tired then I thought(didn't notice your 'n' at the top).
Read first line
malloc for 'n' size array
for loop to iterate names.
Here you go.. I leve compiling and debugging as an exercise for the student.
The idea is to slurp the whole file into a single array if you files are always small.
This is so much more efficient than scanf().
char buf[100000], *bp, *N[1000]; // plenty big
memset( buf, '\0', sizeof buf );
if ( fgets( buf, sizeof(buf), fd ) )
{
int n = 0;
char *bp;
if ( buf[(sizeof buf)-2)] != '\0' )
{ // file too long for buffer
printf( stderr, "trouble: file too large: %d\n", (int)(sizeof buf));
exit(EXIT_FAILURE);
}
// now replace each \n with a \0, remembering where each line is.
for ( bp = buf, bp = strchr( bp, '\n' ); bp++ )
N[n++] = bp;
}
If you want to read any size files you need to read the file in chunks, calloc()ing each chunk before a read, and carefully handling of the line fragments left at the end of the current buffer to move them to the next buffer and then properly continuing you reads.
Unless you have a limit on how many lines you can read the N may need to also be set up in chunks, but this time remalloc() might be your friend.
Since the given format seems to imply that the number of names n is given as the first entry in the file, it would be possible to use the style of reading that the OP describes when reading from stdin. Use fscanf to read the first integer from the file (n), then use malloc to allocate the array(s) for the names, then use a for loop up to n to read the names.
However, I am unsure of the meaning of the example data following that with the do x⁽¹⁾, y⁽¹⁾ and z⁽¹⁾ format. Perhaps I am not understanding part of the question. If it means there are potentially more than n names, then you can use realloc to grow the size of the array. One way of growing the array that is not uncommon is to double the length each time.
I'm using a fprintf function to print to a new file
I'm using the following command to write multiple times:
fprintf(fp, "%-25s %d %.2f %d",temp->data.name, temp->data.day, temp->data.temp, temp->data.speed);
The problem is that sometimes the file gets an extra new line as the first character.
Could this be lelftovers from some buffer, I don't really know...
typedef struct Data {
char name[26];
int day;
int speed;
float temp;
} Data ;
#spatz you were right, I'm kind of new to the string format thing and I was told to make one for a fscanf where I was to expect an undetermined amount of space between the bits of data, here is what I came up with, I'm pretty sure its the source of the problem:
check=fscanf(fp1, "%20c%*[^0-9]%d%*[^0-9]%f%*[^0-9]%d%*[^\n]%*c", name, &day, &temp, &speed);
only the first line get read normally and everything afterwards reads the new line of the previous line.
Can someone please show me the proper way to write this thing?
Rather than calling fscanf() over and over, and hoping that the newlines match up how you want, use fgets() to get one line at a time, parse it using fscanf(), and do error handling on a line-by-line basis. This will be less error-prone, and it sounds like it will clear up your problem with no extra effort.
Your problem is that name starts with a newline, and that newline ends up in the file.
In order to properly parse the file I would have to know its format, but for now I assume it's <string> <int> <int> <float> where the number of spaces between each element may vary.
The format string I would start with is simply "%s%d%d%f", and let fscanf() deal with the whitespace. With this format string I was able to properly parse lines like
foo 3 4 7
If this does not satisfy you feel free to elaborate on the format of the file you are parsing and I'll try to come up with solutions.
I'm in my first year of Computer Sciences and I have to design a procedure that writes to a file with format (fprintf) and displays it with format (fscanf). But I can't get it to run properly; it compiles but when it gets to the fscanf part, it crashes. I've been looking around reference sites, YouTube videos and stuff but I can't get it to work without any success.
Except for the last 2 lines of codes, it does everything great. Its capable of writing the records I enter, in the .txt file. The problem is with the use of fscanf itself.
void write_with_format()
{
char name_of_file[100] = "grades.txt";
FILE *arch;
arch = fopen (name_of_file, "a");
char name[50];
char career[50];
char grades[100];
char total;
printf("Give me the name");
gets(name);
printf("Give me the career");
gets(career);
printf("Give me the grade");
gets(grades);
getchar();
fprintf (arch, "%s,%s,%s\n",name,career,grades);
fscanf(arch,"%s %s %f",&name,&career,&grades);
printf("%s %s %f",name,career,grades);
}
I'd appreciate any help regarding my code or the proper use of fscanf, thank you all.
This line is all wrong:
fscanf(arch,"%s %s %f",&name,&career,&grades);
grades is declared as char grades[100];, ie. a string, however you're trying to read a float into it. Same goes for the printf line below it, you're using %f and telling printf that you're passing a float, however you're passing an array. You also don't need to use the address-of operator (&) when passing arrays to functions, as you have with fscanf.
You should use fclose to flush the buffer and close the file stream once you're done with reading/writing a file.
Back to the fscanf line, what exactly do you expect it to do? The file position inidcator is at the end of the file, just after where you've appended the line produced by fprintf. Check the return value of fscanf and you'll see that it's returning EOF to report an error. The specific error value is stored in errno.
You can use rewind or fseek to set the position to the start of the file or back a certain amount, or you could always reopen the file. I know that I at least wouldn't have my read code in a write_with_format function.
gets is unsafe and should not be used as it has the potential to cause buffer overflows, use fgets(stdin, SIZE...) instead.
Turn up your compiler warnings. If by chance you're using gcc, the flag is -Wall. Just because your code compiles, doesn't mean that it's going to work properly (or at all).
You declare grades as an array of char's but are trying to read into it a float.