I have a file like this one:
1234 Homer 18.5
1223 Bart 25.5
9341 Lisa 30.0
3420 Marge 28.4
8730 Abram 26.7
1876 Barns 27.8
1342 Smiters 23.0
7654 Milhouse 29.7
How can i get the first part ( for example 1234 ) of each line?
And how can i get the name ( for example Homer ) of each line?
I wrote this code below:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
char ch[25];
int i, num;
FILE *fp;
fp = fopen("studenti.txt","r"); // read mode
if( fp == NULL )
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
printf("The contents of numeri.txt file are :\n");
for(i = 0; i < 25; i++){
while( ( ch[i] = fgetc(fp) ) != EOF ){
if(!(ch[i] >= 'A' && ch[i] <= 'Z') && !( ch[i] >= 'a' && ch[i] <='z')){printf("%c",ch[i]);}
}}
fclose(fp);
return 0;
}
How can do that??
This is what fscanf function is for:
int n;
char name[25];
float x;
FILE* fp = ...
while (fscanf(fp, "%d%24s%f", &n, name, &x) == 3) {
// Do something with the data you just read:
printf("int=%d name='%s' float=%f\n", n, name, x);
}
Several things to note about the above:
fscanf returns the number of items it read from the file. Continue calling fscanf while it returns 3
%24s means "a string of up to 24 characters in length". name has 25 characters, because the last one is used for null termination
int and float parameters are passed to fscanf with an ampersand, because fscanf needs a pointer. String, on the other hand, takes no ampersand, because it's equivalent to a pointer.
If you are sure of the text format, the simplest may be to use fscanf.
int num;
char name[1024];
float grade;
fscanf(fp, "%d %s %f", &num, name, &grade);
Be aware, if the name is longuer than 1024 chars, you will have buffer overflows. If the format is not sure, you need to check the return code of fscanf (see the man page).
The only thing you ever read from files is bytes.
The first step is to check if the bytes are valid characters, and convert the bytes into characters if necessary. This is not necessarily simple. If the bytes are supposed to be ASCII, then you might only need to check if the bytes are valid ASCII (e.g. not less than or equal to zero and not above 0x80; and possibly not control characters like "delete" or "vertical tab").
However, where names are involved it's extremely unlikely that ASCII is adequate. This means you want something like UTF-8. In that case, at a minimum you need to check if the bytes are valid (variable length) UTF-8 sequences; in addition to checking for invalid characters (like "delete" or "vertical tab").
More complicated is if you simply don't know what the bytes are. There are ways to auto-detect the character encoding (but it's heuristics not 100% reliable).
The second step is parsing. Parsing typically has 2 equally important goals. The first goal is to convert the characters into a more easily processed form - e.g. like maybe a structure with 3 fields (an integer, string and float) representing each line of characters. The second goal of parsing is reporting any errors to the user in an easily understood manner.
For example, maybe the first number on each line must be a 4 digit code (like "0123"); and if there's only 3 digits (like "123") then you want to generate an error (e.g. "ERROR: CourseID too short on line 5 of file 'foo.txt'") so that it's easy for the user to know exactly what the problem is and easy for the user to fix it.
Note: I don't think I've ever seen code that uses fscanf() that is close to (what I consider) acceptable. There's almost never useful/descriptive error messages.
Related
The input file will have the name of a lake on one line, then a comma, then the volume of that lake in units of hundreds of cubic miles. The next line will contain the name of another lake, a comma, then it's volume, etc, etc. Each line of the file contains exactly the name of a lake, a comma, and a float value for volume.The name of a lake may contain more than one word; for example "dead horse lake", but it will be on one formatted line. The volume may have decimals in it, as 16.9. Your program will use a subroutine that takes as arguments the name of a lake and its volume. This subroutine will then print out the name of the lake on one line to the screen, followed by a set of consecutive asterisks denoting the volume in units of hundreds of cubic miles on the next line rounded to the nearest hundred cubic miles. For example, if the lake is 15.6 cubic miles in volume, the subroutine will print out 16 asterisks on the line following the name of the lake.
Right now my program only reads the first line and displays the name and asterisk, but any other information in my lakes.txt file is not read and the program terminates. Could someone please tell me what I am doing wrong? The original project was one without the comma, but the prof. decided to add a comma in there. I just changed the %19 to %20 and added a comma in the brackets. I do not know what difference it made, but it worked. I would like to understand why.
I'm new to SO. Sorry if my text is a little off. I'm a new programmer and I would really love to understand and become a good programmer. The more elaborate and in depth your answers are, the more helpful they are to me. Thank you for your help!
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
void asterisks_code( char lake[257], float vol );
int main()
{
char lake[257], tempt[100];
float vol;
FILE *fp;
fp = fopen( "lakes.txt", "r" );
if( fp == NULL) {//starts 1st if statement
printf( "File does not exist.");
return 0;
}//closes 1st if statement
while( ( fgets( tempt, sizeof (tempt), fp ) ) != NULL ) {
if (sscanf(tempt, " %19[A-Za-z, ]%f", lake, &vol) != 2) {//starts 2nd if statement
fprintf(stderr, "Unexpected data\n");
break;
}//closes 2nd if statement
asterisks_code( lake, vol );
}//closes while loop
fclose( fp );
return 0;
}//closes main function
void asterisks_code( char lake[257], float vol )
{//start of asterisks_code function
int counter;
printf( "%s\n", lake );
for( counter = 0; counter < roundf(vol); counter++ ) {//start of for loop
printf( "*" );
}//closes for loop
printf( "\n" );
}//closes asterisk_code function
I feel for you. Your professor has misguided you from the very start.
First of all, fgets( tempt, sizeof (tempt), fp ) doesn't guarantee that a full line will be read from fp. If sizeof tempt bytes are read and there are more left in the line, the leftover lines will be left on the stream for the next call to fgets. I've written solutions to that problem in another answer, but in all honesty you don't need fgets. You could use it if you wanted to, but all you need to solve this exercise is fscanf and fgetc.
I thoroughly advise reading only one element at a time using fscanf... at least until you've read and fully understood the fscanf manual (it's very lengthy, suggesting that it's quite complex and easy to get wrong... hmmmm!). Even so, you should still at least read it before you try to use it.
Start by writing a function to read your 19-byte wide field...
int read_lake_name(FILE *fp, char *lake) {
int n = fscanf(fp, "%19[^,]", lake);
int left_over = 0;
fscanf(fp, "%*[^,]%n", &left_over);
if (left_over > 0) {
puts("WARNING: Lake name was longer than 19 bytes and has been discarded...");
}
fgetc(fp);
return n;
}
If more than 19 bytes are provided, there will be some left-over on the stream. Those bytes need to be discarded. Notice the * in the second fscanf, and that there's no corresponding array argument? That's an example of assignment suppression using fscanf; the left-over bytes will be read and discarded, rather than assigned into an array. The %n directive instructs fscanf to assign the number of bytes fscanf has read into left_over, which tells us whether or not the lake's name was truncated. Following that, fgetc is called to discard the remaining comma.
This code is great at reading (and truncating) 19 bytes of user input, up to the next comma, but it won't read the volume. Once you've read those up-to-19-bytes and discarded the comma (and any trailing data prior to the comma), you'll need to read a floating point value, then discard anything prior to the newline, and discard the newline (of course)...
int read_lake_volume(FILE *fp, float *v) {
int n = fscanf(fp, "%f", v);
int left_over = 0;
fscanf(fp, "%*[^\n]%n", &left_over);
if (left_over > 0) {
puts("WARNING: Lake volume contained trailing invalid characters which have been discarded...");
}
fgetc(fp);
return n;
}
Do you notice any similarities here? With these two virtually identical functions it should be trivial to construct a working loop, but here goes:
while (read_lake_name(fp, name) == 1 && read_lake_volume(fp, &vol) == 1) {
write_asterisks(name, vol);
}
I am reading data from a file, retrieving how many columns and rows I have ( data file ), everything of so far. Now I am trying to read the values one by one and store the values in to a 2D array (double). I get the values as char using getc but when I try to use atoi or atof to convert the values from char to double i get strange values.
double ther[j][number];
char c;
int tim=0,nther=0;
FILE *fp3 = fopen("data.txt", "r");
c = getc(fp3) ;
while (c!= EOF)
{
ther[tim][nther]=atoi(&c);
printf("%lf", ther[tim][nther]);
nther++;
c = getc(fp3);
if(nther==number)
{
tim++;
nther=0;
}
tim=0;
}
fclose(fp3);
any suggestion?… (i keep searching). Sorry well i have a file data.txt and this file has rows an columns of numbers:
1 2 3 4 5
6 7 8 9 10
So i need to store the data into a 2D array: Thanks to the answers i have some ideas like: use an string, then divide all values and stored every one of them. 1st line is string-> array[0][0], array[0][1], etc. Then move to the other row and do the same.
Until now i get some numeric values but none of them are stored at the data.txt.
Firstly, it should be int c;. The getc() function has to be able to return a different value for all possible chars, and also a different value EOF (which signifies end-of-file, or stream error).
If you use char c;, then you must end up with EOF being converted to the value of a legal character, so you can't tell the difference between the two cases. In other words, your code might act like it hit EOF when in fact it just hit that particular character.
Moving on, atoi(&c) is not correct. If you read the documentation for the atoi function -- this is always a good idea when using functions -- you will find that it expects a string as input. However, &c is not a string. It is the address of a single char.
So your options are:
construct a string and pass it to atoi
don't use atoi
You didn't say what you were expecting to happen; but if you want to convert the character you read in of '3' to be the integer 3 then you can write: ther[tim][nther] = c - '0';.
However you should also have some code to handle cases when you read something that was not a number.
If you wanted to read in more than one character at a time (e.g. "31" -> 31) then the approach of using getc is not appropriate - you'll need to switch to using a function that reads multiple characters at once, such as fscanf or fgets.
Finally, you set tim to 0 every single loop iteration, so the tim++ never has any lasting effect, maybe this was an oversight.
You can read a double from a file using fscanf. An example would be:
double d;
fscanf("%f", &d);
fscanf can do much more than that so I'd recommend checking the documentation.
The method you're using gets a single character (as Matt McNabb pointed out - incorrectly). A character is 1 byte and can store the values 0-255. You're reading in text mode since you don't specify a mode with fopen (it is generally considered a good practice to explicitly ask for either binary "rb/wb" or text "rt/wt" mode). This means you read the ASCII value of a single digit.
So let's say the file contains:
7.2, 3.0, 1.0
The first call to getc(fp3) would get the character '7' which would have a value of 55 for ASCII. Obviously this is not going to give you what you're looking for.
Instead you could use:
double d[3];
fscanf("%f, %f, %f" &d[0], &d[1], &d[2]);
Which would get the three float values in the file.
#include <stdio.h>
int main(){
int j = 2;
int number = 5;
double ther[j][number];
double d;
int tim=0,nther=0;
FILE *fp3 = fopen("data.txt", "r");
while (fscanf(fp3, "%lf", &d)==1){
ther[tim][nther]=d;
printf("%g ", ther[tim][nther++]);
if(nther==number){
++tim;
nther=0;
printf("\n");
}
}
{
int r, c;
for(r=0;r<tim;++r){
for(c=0;c<number;++c){
printf("%f ", ther[r][c]);
}
printf("\n");
}
}
return 0;
}
I am getting the user to input 4 numbers. They can be input: 1 2 3 4 or 1234 or 1 2 34 , etc. I am currently using
int array[4];
scanf("%1x%1x%1x%1x", &array[0], &array[1], &array[2], &array[3]);
However, I want to display an error if the user inputs too many numbers: 12345 or 1 2 3 4 5 or 1 2 345 , etc.
How can I do this?
I am very new to C, so please explain as much as possible.
//
Thanks for your help.
What I have now tried to do is:
char line[101];
printf("Please input);
fgets(line, 101, stdin);
if (strlen(line)>5)
{
printf("Input is too large");
}
else
{
array[0]=line[0]-'0'; array[1]=line[1]-'0'; array[2]=line[2]-'0'; array[3]=line[3]-'0';
printf("%d%d%d%d", array[0], array[1], array[2], array[3]);
}
Is this a sensible and acceptable way? It compiles and appears to work on Visual Studios. Will it compile and run on C?
OP is on the right track, but needs adjust to deal with errors.
The current approach, using scanf() can be used to detect problems, but not well recover. Instead, use a fgets()/sscanf() combination.
char line[101];
if (fgets(line, sizeof line, stdin) == NULL) HandleEOForIOError();
unsigned arr[4];
int ch;
int cnt = sscanf(line, "%1x%1x%1x%1x %c", &arr[0], &arr[1], &arr[2],&arr[3],&ch);
if (cnt == 4) JustRight();
if (cnt < 4) Handle_TooFew();
if (cnt > 4) Handle_TooMany(); // cnt == 5
ch catches any lurking non-whitespace char after the 4 numbers.
Use %1u if looking for 1 decimal digit into an unsigned.
Use %1d if looking for 1 decimal digit into an int.
OP 2nd approach array[0]=line[0]-'0'; ..., is not bad, but has some shortcomings. It does not perform good error checking (non-numeric) nor handles hexadecimal numbers like the first. Further, it does not allow for leading or interspersed spaces.
Your question might be operating system specific. I am assuming it could be Linux.
You could first read an entire line with getline(3) (or readline(3), or even fgets(3) if you accept to set an upper limit to your input line size) then parse that line (e.g. with sscanf(3) and use the %n format specifier). Don't forget to test the result of sscanf (the number of read items).
So perhaps something like
int a=0,b=0,c=0,d=0;
char* line=NULL;
size_t linesize=0;
int lastpos= -1;
ssize_t linelen=getline(&line,&linesize,stdin);
if (linelen<0) { perror("getline"); exit(EXIT_FAILURE); };
int nbscanned=sscanf(line," %1d%1d%1d%1d %n", &a,&b,&c,&d,&lastpos);
if (nbscanned>=4 && lastpos==linelen) {
// be happy
do_something_with(a,b,c,d);
}
else {
// be unhappy
fprintf(stderr, "wrong input line %s\n", line);
exit(EXIT_FAILURE);
}
free(line); line=NULL;
And once you have the entire line, you could parse it by other means like successive calls of strtol(3).
Then, the issue is what happens if the stdin has more than one line. I cannot guess what you want in that case. Maybe feof(3) is relevant.
I believe that my solution might not be Linux specific, but I don't know. It probably should work on Posix 2008 compliant operating systems.
Be careful about the result of sscanf when having a %n conversion specification. The man page tells that standards might be contradictory on that corner case.
If your operating system is not Posix compliant (e.g. Windows) then you should find another way. If you accept to limit line size to e.g. 128 you might code
char line[128];
memset (line, 0, sizeof(line));
fgets(line, sizeof(line), stdin);
ssize_t linelen = strlen(line);
then you do append the sscanf and following code from the previous (i.e. first) code chunk (but without the last line calling free(line)).
What you are trying to get is 4 digits with or without spaces between them. For that, you can take a string as input and then check that string character by character and count the number of digits(and spaces and other characters) in the string and perform the desired action/ display the required message.
You can't do that with scanf. Problem is, there are ways to make scanf search for something after the 4 numbers, but all of them will just sit there and wait for more user input if the user does NOT enter more. So you'd need to use gets() or fgets() and parse the string to do that.
It would probably be easier for you to change your program, so that you ask for one number at a time - then you ask 4 times, and you're done with it, so something along these lines, in pseudo code:
i = 0
while i < 4
ask for number
scanf number and save in array at index i
E.g
#include <stdio.h>
int main(void){
int array[4], ch;
size_t i, size = sizeof(array)/sizeof(*array);//4
i = 0;
while(i < size){
if(1!=scanf("%1x", &array[i])){
//printf("invalid input");
scanf("%*[^0123456789abcdefABCDEF]");//or "%*[^0-9A-Fa-f]"
} else {
++i;
}
}
if('\n' != (ch = getchar())){
printf("Extra input !\n");
scanf("%*[^\n]");//remove extra input
}
for(i=0;i<size;++i){
printf("%x", array[i]);
}
printf("\n");
return 0;
}
Alright I've been at this all day and can't for the life of me get this down, maybe you chaps can help. I have a file that reads as follows
1301,105515018,"Boatswain","Michael R.",ABC, 123,="R01"
1301,103993269,"Castille","Michael Jr",ABC, 123,="R03"
1301,103993267,"Castille","Janice",ABC, 123,="R03"
1301,104727546,"Bonczek","Claude",ABC, 123,="R01"
1301,104731479,"Cruz","Akeem Mike",ABC, 123,="R01"
1301,105415888,"Digiacomo","Stephen",ABC, 123,="R02"
1301,106034479,"Annitto Grassis","Susan",ABC, 123,="R04"
1301,106034459,"Als","Christian",ABC, 123,="R01"
And here is my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME 15
#define MAX_SUBSEC 3
#define N 128
//void printArr(struct *students);
struct student{
int term;
int id;
char lastname[MAX_NAME];
char firstname[MAX_NAME];
char subjectname[MAX_SUBSEC];
int catalog;
char section[MAX_SUBSEC];
}students[10];
int main(){
int term;
int id;
char lastname[MAX_NAME];
char firstname[MAX_NAME];
char sub[MAX_SUBSEC];
int cat;
char sec[MAX_SUBSEC];
char fname[N];
FILE *inputf;
printf("Enter the name of the text file: ");
scanf("%123s",fname);
strcat(fname,".txt");
inputf = fopen(fname,"r");
if (inputf == NULL){
printf("I couldn't open the file for reading.\n");
exit(0);
}
//TROUBLE HERE!
fscanf(inputf, "%d,%d,%[^,]s", &students[0].term, &students[0].id,students[0].lastname);
printf("%d\n", students[0].term);
printf("%d\n", students[0].id);
printf("%s\n", students[0].lastname);
/*for (j = 1 ; j <= 10-1 ; j++){
for(k = 0 ; k <= 10-2 ; k++){
if(students[k] > students[k+1]){
temp = students[k];
students[k] = students[k+1];
students[k+1] = temp;
}
}
}*/
fclose(inputf);
system("pause");
return 0;
}
void printArr(int a[], int tally){
int i;
for(i = 0 ; i < tally ; i++){
printf("%d ", a[i]);
}
printf("\n");
}
My objective is to take each one of those values in the text file and input it to where it belongs in the struct and subsequently the struct array, but I can't get passed the first 2 ints.
Getting the lastname string, because it is a max of 15 characters, it spills over into the first name string right after it and takes what remaining characters it needs in order to fill up the lastname char array. Obviously I do not want this. As you can see I have tried strtok but it doesnt do anything, not sure what I have to do though as I have never used it before. Also have tried just including all the variables into fscanf statement, but I either get the same output, or it becomes a mess. As it is, I am extremely lost, how do I get these values into the variables they belong?!
EDIT: updated my code, I have gotten a little farther but not much. I can now print out just the last name but can not more farther from there, I cant get to the firstname string or any of the variables beyond it.
What you have there is a CSV file with quoted strings, and so I would recommend you use a CSV parser (or roll your own) rather than trying to do it all with scanf (since scanf cannot deal with quotes, e.g. commas within quoted strings). A quick Google search turns up libcsv.c which you may be able to use in your project.
With the fscanf format string "%d,%d,\"%[^\"]\",\"%[^\"]\",%[^,],%d,=\"%[^\"]\"" we can read a whole line's data. Besides, you have to define
char lastname[MAX_NAME+1];
char firstname[MAX_NAME+1];
char subjectname[MAX_SUBSEC+1];
int catalog;
char section[MAX_SUBSEC+1];
— the +1 to account for the terminating '\0' character.
I have a question for you... If you want to know how to use a diamond cutter, do you try it and see, or do you consult the manual? The problem here isn't the result of your choice, but your choice itself. Believe it or not, I have answered these questions so often that I'm tired of repeating myself. The answer is all in the manual.
Read the POSIX 2004 scanf manual — or the POSIX 2008/2013 version — and the answer this question and you'll have some idea of what you're not doing that you should be. Even fscanf code should use assert as a debugging aid to ensure the number of items read was correct.
%[^,]s It seems as though there's a mistake here. Perhaps you meant %[^,]. The %[ format specifier is a different format specifier to the %s format specifier, hence in the presumably mistaken code there are two directives: %[^,] and s. The s directive tells scanf to read an 's' and discard it.
1.There is a syntax error in
while(result != NULL){
printf(".....);
......
}
}//error
fscanf(inputf, "%s", lastname); can't read a line ,fscanf will stop when it comes across an space
fscanf reads one line at a time, and you can easily capture the contents of each line because your file is formatted pretty nicely, especially due to the comma separation (really useful if none of your separated values contain a comma).
You can pass fscanf a format like you're doing with "%d" to capture an int, "%s" to capture a string (ends at white space, be weary of this when for example trying to find a name like "Annitto Grassis, which would require 2 %s's), etc, from the currently read line of the file. You can be more advanced and use regex patterns to define the contents you want captured as chars, such as "Boatswain", a sequence comprised chars from the sets {A-Z}, {a-z}, and the {"}. You'll want to scan the file until you reach the end (signified by EOF in C) so you can do such and capture the contents of the line and appropriately assign the values to variables like so:
while( fscanf(inputf, "%d,%d,%[\"A-Za-z ],%[\"A-Za-z .]", &term, &id, lastname, firstname) != EOF) {
.... //do something with term, id, lastname, firstname - put them in a student struct
}
For more about regex, Mastering Regex by Jeff Friedl is a good book for learning about the topic.
I am currently attempting to read in Hex values from a text file.
There can be multiple lines of Hex's and each line can be as long as needed:
f53d6d0568c7c7ce
1307a7a1c84058
b41af04b24f3eb83ce
Currently, I put together a simple loop to read in the Hex values into an unsigned char line[500] with fscanf as such:
for(i=0; i < 500; i++)
{
if (fscanf(fp, "%02x", &line[i]) != 1) break;
}
At the current moment, this only reads in the first line. As well, it is definitely not the best approach to just throw in a random 500 there to read.
I was assuming I could use sscanf with fgets or something of that nature. But I was unsure if this would be the best approach.
If anyone could help point me in the right direction, I would greatly appreciate it.
You're on the right track with fgets() and sscanf(); that will let you size everything appropriately. If your data is really in that format, sscanf() might be overkill; you could just write a quick conversion loop yourself and save all those variadic function calls.
Note that sscanf is slow (library calls, memory usage, and overkill). Moreover it isway too dangerous (b/c of possible buffer overrun).
You probably would get better results with your own parser. It may show as a bigger source code but it gives you the chance to control and expand your code exactly as needed, without compromising security and speed.
The usual way is to accumulate the hex digits one by one as you read them and build up the corresponding integer:
hexDigit = one letter from "0123456789ABCDEF" remapped to a number within 0-15
accumulating_number= accumulating_number * 16 + hexDigit
Here is a tiny standalone parser as a full example. It accepts lower and upper case and it ignores any non hex character (so you can use space or commas for better readability in the source):
#include <stdio.h>
#define SPLIT_CHAR_SIZE 8 // size of the hex numbers to parse (eg. 6 for RGB colors)
void do_something_with(unsigned int n)
{
printf("%08X ",n);
}
int main(int argc, char** argv)
{
FILE* fp= (argc!=2) ? stdin : fopen(argv[1],"r");
if(!fp) { fprintf(stderr,"Usage: %s fileToRead\n", argv[0]); return(-1); }
unsigned int i=0, accumulator=0;
char c;
while(!feof(fp)) // you could parse a c-string via fscanf() to handle other file contents
{
c= fgetc(fp);
// The "<<4" gives room for 4 more bits, aka a nibble, aka one hex digit, aka a number within [0,15]
if(c>='0' && c<='9')
accumulator= (accumulator<<4) | (c - '0');
else if(c>='a' && c<='f') // lower case
accumulator= (accumulator<<4) | (c - 'a' + 10);
else if(c>='A' && c<='F') // upper case
accumulator= (accumulator<<4) | (c - 'A' + 10);
else
continue; // skip all other (invalid) characters
// When you want to parse more than one hex number you can use something like this:
if(++i % SPLIT_CHAR_SIZE == 0)
{
do_something_with(accumulator);
accumulator= 0; // do not forget this
}
}
printf("\n");
return 0;
}
If you give this parser it the following (somehow weird) file content:
ca 53,
FF 00
aa bb cc dd
then the function do_something_with() will output this:
CA53FF00 AABBCCDD