is it possible to access a specific string in a file - c

I've read a file called names.txt which contains firstname and secondname. I printed the names out, just to see, if it's working
for (int counter = 0; counter < 10; counter ++) {
fscanf(names, "%s %s\n", firstname, secondname);
printf("%s%s\n", firstname, secondname);
}
I tried to access a specific string with firstname[x] but this gets me the single char in the firstname
is it possible now, to only print let's say the 7th firstname and secondname of the red file names.txt?

for (int counter = 0; counter < 10; counter ++)
{
fscanf(names, "%s %s\n", firstname, secondname);
if(counter==6)
printf("%s%s\n", firstname, secondname);
}
This code will now print only 7th firstname and secondname,

Yes, if you just want to print the 7th name, put a condition around your printf:
if (counter == 6)
printf("%s%s\n", firstname, secondname);
Now just some general comments on other problems with your approach. Firstly, you are doing no bounds checking on your inputs, which can potentially cause a buffer overflow.
The general recommendation for reading strings from the file is using fgets. That reads an entire line, provided your buffer is large enough. You can then split that line into names using strtok or simply finding the first space with strchr.
Another issue is you're not testing whether the input succeeds. fscanf returns the number of elements successfully read. If that is not equal to 2, you should probably abandon your loop.
Last of all, you're looping exactly 10 times, which makes a somewhat bold assumption as to the contents of the file. Perhaps you want to exit the loop after reading the 7th string. Who knows? But either do that, or loop until reading a line fails.

Related

Why I cannot access the first read char array after reading a series of others in C?

I wanted to read a phrase and a series of numbers/alphabetic separated by ",". I read the first string then I print it (works fine). I read the first string, read 62 of those series and try to print the first string again and is not working. I tried a lot of methods but none working.
char text[1001];
scanf("%[^\n]", text);
for (int i = 1; i <= 62; i++) {
char alpha[3] = {0}, lit_orig, lit_replace;
scanf("%s", alpha);
lit_orig = alpha[0];
lit_replace = alpha[2];
}
printf("\n%s", text);
Input example:
Example text here!
a,H b,j c,6 d,I e,2 f,R g,5 h,t i,h j,k k,m l,f m,D n,F o,1 p,0 q,c r,G s,n t,N u,e v,B w,r x,U y,p z,A A,8 B,X C,S D,P E,T F,a G,M H,d I,K J,L K,3 L,C M,i N,9 O,E P,w Q,o R,z S,4 T,O U,q V,V W,J X,x Y,Z Z,u 0,l 1,y 2,W 3,s 4,Q 5,g 6,v 7,7 8,b 9,Y
Output example: There is no output.
I did expect it to print just fine the first string called "text" but it is not working. I tried even clearing out the buffer using getchar() but no results (this is what other websites said it would work). Can you explain what is wrong with my code?
Taking into account this code snippet
lit_orig = alpha[0];
lit_replace = alpha[2];
where there is used the index equal to 2 it seems that in the call of scanf
scanf("%s", alpha);
you are entering three characters. As a result the terminating zero character '\0' is written outside the array alpha in the memory occupied by the array text. As a result the array text contains an empty string.
You need to declare the array alpha at least like
char alpha[4] = {0},
And use the format string in the call of scanf the following way
scanf("%3s", alpha);
Your alpha is too small for what you are doing.
Change to char alpha[5] for a quick fix.
Insert an output inside your loop to be aware of what is happening.
printf ("(%4s)", alpha);
Then fine tune your access and use of alpha.
Note the "()" I added to the debug output, to make sure that you see whether/where white space is. I think it might surprise you.

Trying to read formatted file in C

i'm trying to read a formatted file in C this is a sample line of the file:
SURNAME;NAME;MINUTES'SECONDS''ONE-HUNDREDTHS OF A SECOND
I wrote this code:
while(!feof(fp))
{
fscanf(fp,"%[^;]s",surname);
fscanf(fp,"%c",&c);
fscanf(fp,"%[^;]s",name);
fscanf(fp,"%c",&c);
fscanf(fp,"%d'%d''%d",&min,&sec,&sec_cent);
fscanf(fp,"\n");
}
It works well with the name and surname strings, but it doesn't extract the time MINUTES'SECONDS''ONE-HUNDREDTHS OF A SECOND and i don't know why
Can someone help me?
There are a couple of things you may want to change in your code:
Always check the return value of scanf (or fscanf) to see if all the data were read.
Don't use feof() to control a loop.
You don't need to extract the '\n' with scanf, unless you are going to use getline() afterwards.
You don't need the s too, with that format specifier, but you should prevent buffer overflows limiting the number of chars read for each field.
You can use the modifier %*c to skip a char (the ;).
This should be fine:
int min = 0, sec = 0, sec_cent = 0;
char name[128], surname[128];
while ( fscanf(fp, "%127[^;]%*c%127[^;]%*c%2d'%2d''%2d", surname, name,
&min, &sec, &sec_cent) == 5 ) {
// do something...
}

How to assign a string value

Can I do something like the code below to get a persons name into the firstname string?
printf("First Name? ");
scanf("%s", &firstname[11]);
yup. This stores the input in firstname, starting at index 11.
No. You should not do it this way. It is unsafe, in exactly the same way gets is unsafe.
You should instead do something like this. (I am assuming that you really do want to write to firstname starting at character position 11, which is what &firstname[11] does. If firstname is 11 bytes long and you want to write starting at position 0, you would simply use firstname and remove the various occurrences of 11 + below.)
char inbuf[80];
size_t n;
fputs("First Name? ", stdout);
fgets(inbuf, sizeof inbuf, stdin);
n = strlen(inbuf);
if (n == 0) {
fputs("No name entered\n", stderr);
exit(1);
}
if (inbuf[n-1] != '\n') {
fputs("Name too long\n", stderr);
exit(1);
}
inbuf[--n] = '\0';
if (n == 0) {
fputs("No name entered\n", stderr);
exit(1);
}
if (11 + n >= sizeof firstname) {
fputs("Name too long\n", stderr);
exit(1);
}
memcpy(&firstname[11], inbuf, n);
firstname[11 + n] = '\0';
If you reaction is that that looks like a giant pain in the behind, all I can say is, welcome to C programming. All of that is in fact necessary for robustness in the face of arbitrarily malformed input.
You should also reflect upon Falsehoods Programmers Believe About Names and then redesign your database accordingly. (Most importantly in this context: people do not necessarily divide their names into "first" and "last" and "middle" components; many people have first names that require more than 10 bytes to represent.)
EDIT: Bugs in example code should now all be corrected. Serve me right for doing memory arithmetic in my head without testing it.
The short answer to your question is, yes you can.
The long answer (and questions that go with it)...
What's the declaration of firstname?
What's in the first 11 places of filename?
Are you trying to limit the the limit the length of the user input to 11 characters and that was the syntax you thought would work?
I suspect that you are trying to limit the firstname to be at most 11 characters. The way to solve this is:
char firstname[12];
scanf("%11s", firstname);
However, as Zach pointed out, this will be problematic if you have a first name like "Mary Kay". scanf will stop at the first white space and you will end up with a first name that contains just "Mary". The better approach is to use fgets.
fgets(firstname, 12, stdin);
That should work if there is nothing else in a line.
If you expect to read the data from a file and there are other fields in a line, then you will have to deal with that additional complexity. Say you have first name, last name, and age in a line. Your input file could look something like:
John, Deer, 59
Mary Kay, Smith, 42
If your input file contains data like above, you can't use fgets to pick out the first name using fgets(firstname, 12, infile);. You will have to use fgets to read the entire line, then parse the line to extract all the relevant data.

Reading values from CSV file into variables

I am trying to write a simple piece of code to read values from a CSV file with a max of 100 entries into an array of structs.
Example of a line of the CSV file:
1,Mr,James,Quigley,Director,200000,0
I use the following code to read in the values, but when I print out the values they are incorrect
for(i = 0; i < 3; i++) /*just assuming number of entries here to demonstrate problem*/
{
fscanf(f, "%d,%s,%s,%s,%s,%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
}
Then when I print out the first name, the values are all assigned to the first name:
for(j = 0; j < 3; j++) /* test by printing values*/
{
printf("Employee name is %s\n", inArray[j].firstName);
}
Gives ames,Quigley,Director,200000,0 and so on in that way. I am sure it's how i format the fscanf line but I can't get it to work.
Here is my struct I'm reading into:
typedef struct Employee
{
int ID;
char salutation[4];
char firstName[21];
char surName[31];
char position[16];
int sal;
int deleted;
} Employee;
This is because a string %s can contain the comma, so it gets scanned into the first string. There's no "look-ahead" in the scanf() formatting specifier, the fact that the %s is followed by a comma in the format specification string means nothing.
Use character groups (search the manual for [).
const int got = fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID,
inArray[i].salutation, inArray[i].firstName,
inArray[i].surName, inArray[i].position, &inArray[i].sal,
&inArray[i].deleted);
And learn to check the return value, since I/O calls can fail! Don't depend on the data being valid unless got is 7.
To make your program read the entire file (multiple records, i.e. lines), I would recommend loading entire lines into a (large) fixed-size buffer with fgets(), then using sscanf() on that buffer to parse out the column values. That is much easier and will ensure that you really do scan separate lines, calling fscanf() in a loop will not, since to fscanf() a linefeed is just whitespace.
Might as well post my comment as an answer:
%s reads a full word by default.
It finds the %d, the integer part, then the ,, and then it has to read a string. , is considered valid in a word (it is not a whitespace), so it reads until the end of the line (there is no whitespace until then), not until the first comma... And the rest remains empty. (From this answer)
You have to change the separator with specifying a regex:
fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
Instead of %s, use %[^,], which means "grab all chars, and stop when found a ,".
EDIT
%[^,]s is bad, it would need a literal s after the end of the scanset... Thanks #MichaelPotter
(From Changing the scanf() delimiter and Reading values from CSV file into variables )

reading multiple variable types from single line in file C

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.

Resources