I really need your help with my program. I'm a newbie in C and need to create a linked list in my project.
I use xcode on macOS.
My code is here:
typedef struct person
{
char name[500];
char telnum[16];
char wohnort[500];
struct person *next;
}person;
person *head=NULL;
void einlesen()
{
person *p;
char name[30];
char telnum[16];
char wohnort[30];
//char buff1[15], buff2[15];
int cnt=0;
FILE *fp=fopen("Data2.txt", "r");
if(fp==NULL)
{
printf("File konnte nicht geoeffnet werden!\n");
exit(1);
}
while(fscanf(fp," %29[A-z ]\t%s\t%s", &name, &telnum, &wohnort)!=EOF)
{
cnt++;
printf("Datei gefunden!\nDaten:%s, %s, %s\n", name, telnum, wohnort);
if(head == NULL){
head = (person*)malloc(sizeof(person));
strcpy(head->name, name);
strcpy(head->telnum, telnum);
strcpy(head->wohnort, wohnort);
head->next = NULL;
}
else{
p = head;
while(p->next != NULL)
{
p = p->next;
}
p->next = (person*)malloc(sizeof(person));
strcpy(p->next->name,name);
strcpy(p->next->telnum,telnum);
strcpy(p->next->wohnort,wohnort);
p->next->next = NULL;
}
}
printf("Die Daten von %d Personen wurden eingelesen!\n\n", cnt);
fclose(fp);
}
void addieren()
{ char name[30];
char telnum[16];
char wohnort[30];
person *p = head;
printf("Bitte den Namen der hinzufuegenden Person eingeben:\n");
scanf(" %29s", name);
while(getchar() != '\n');
printf("\nBitte die Telefunnumer der hinzufuegenden Person eingeben:\n");
scanf(" %15s", telnum);
while(getchar() != '\n');
printf("\nBitte den Wohnort der hinzufuegenden Person eingeben:\n");
scanf(" %29s", wohnort);
while(getchar() != '\n');
if(p==NULL) return;
while(p->next)
{
p=p->next;
}
p->next = (person*)malloc(sizeof(person));
strcpy(p->next->name, name);
strcpy(p->next->telnum, telnum);
strcpy(p->next->wohnort, wohnort);
p->next->next=NULL;
}
and i have this in my main function:
int main()
{
person *p1=head;
einlesen();
addieren();
while(p1 !=NULL)
{
printf("Namen: %s\n", p1->name);
p1=p1->next;
}
return 0;
}
It's already read the data from the file and I wanted to add some new records to it and the print to the screen. This returns 0 and doesn't really print the list on the screen.
Later I need to add more functions to my program, like:
-deletefromlist
-modify element
-search in the linked list
-save to file
-and a menue
I see no particular question, so I'll start by pointing out some errors, and perhaps when you've added a question (which ends in a question mark) you can ping me for an update.
while(fscanf(fp," %29[A-z ]\t%s\t%s", &name, &telnum, &wohnort)!=EOF)
Let me just start off by saying I'm a huge advocate of reading the manual before using a function for the first time. In fact, I'm pretty sure it's expected of us, as programmers. Granted, we must learn which manuals to seek, because the internet is a dangerous place and people do occasionally lie. The OpenGroup manuals are good. For example, if you type into Google "Opengroup fscanf", you'll find the manual for fscanf, which will answer many questions I', sure...
The manual states that all whitespace is treated the same, as a directive which attempts to read and discard as much whitespace as possible. Hence, a plain space has the same meaning to \t in a format specifier (at least, in your context)... so if your question is something along the lines of "Why are all whitespaces treated the same?" then the answer is "because that's what the manual says it should do."
The fixed width field you're reading into can fail to translate, in a scenario the manual also describes where-by the first character tested failed to match the criteria ([A-z ], which looks highly suspect, and you'll probably realise by reading the manual that this isn't what you want). That would result in a return value (from scanf) of 0, which, to your code, looks like a successful read.
Here's an idiomatic guideline: Let's first count the number of objects that you want to read into... Here they are, copied verbatim from your code: &name, &telnum, &wohnort... There are three of them.
Now, take that number, three, and compare it to what fscanf returns... If these two numbers match, then you can use all three arguments safely (though as I note below, they may not necessarily contain the values that you expect them to contain).
You also shouldn't use the & (address-of) operator here for name, telnum or wohnort. The address-of operator on a char [10] array would result in a char (*)[10] expression, where-as you most certainly want a plain vanilla char * expression. Hence, the correct thing to do is to let Cs implicit "array expression to pointer expression" conversion do that for you.
Below is probably closer to what you want. My advice regarding the [A-z ] thing is that you list out each of the characters you want to include in those brackets, or use [^...] if you want to list out each of the characters you want to exclude. To be clear, there are no ranges in C format strings, in case that's what your question was about. e.g.:
while(fscanf(fp," %29[qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM ] %s %s", name, telnum, wohnort)==3)
Note that it isn't quite there, yet. There's the potential for 29 bytes of a 32 byte input to be read into that fixed width field, leaving three characters on the stream. Perhaps they're not space, in which case your whitespace directive will silently fail, and those remaining three characters will be treated as telnum.
You should try to read just one item per call to scanf, if only until you understand the corresponding manual.
No collection should ever require n calls to malloc to store n objects. If you manage to complete this (no doubt self-assigned) task, I suggest making it your next priority to allocate your list as multiple nodes per call to malloc... or better yet, the convenience you get not having to free() every string fscanf returns, you could benefit from the same technique; expect caller to allocate the list, passing you all the storage you need, as well as items to add, remove, etc from the list. All the function does is modify the objects passed in.
while(getchar() != '\n');
Above you recognise that EOF can occur... what do you think getchar() will return when that happens? Not '\n', that's for sure, and it'll keep returning that (not `'\n') value indefinitely, hence an infinite loop. Trying to solve this problem complicates the loop a bit; you need storage for the character, e.g.:
int c;
while ((c = getchar()) != EOF && c != '\n');
The clearer alternative to that is actually to use fread, e.g.
char c;
while (fread(&c, 1, 1, stdin) && c != '\n');
Nonetheless, better yet, is using fscanf, according to how the manual explains (there are many other useful functionalities for you to read about A.S.A.P.). Do you think you can remember these two lines, after typing them ten or fifteen times in a row?
scanf("%*[^\n]");
getchar();
Man... your code should always check malloc return values before they're used. How do we know your malloc calls aren't returning NULL, leading to a question which you haven't asked?
You are creating a new person inside the function addieren instead of receiving the one you created on the main function. You need to receive it as an argument, something like that
int addieren(person **head) { ... }
And on the main you should make
addieren(&p1);
Notice the double pointer so you can pass the pointer and modify it inside of the function. Also you probably should return an int to confirm either the operation was succesfull or not
Related
I have the following code which reads from a given input file into and then into struct I have made.
OFFFile ReadOFFFile(OFFFile fileData, FILE* srcFile)
{
int nvert, nfaces;
fscanf(srcFile, "%s\n");
fscanf(srcFile, "%d %d %s\n", &nvert, &nfaces);
fileData.nvert = nvert;
fileData.nfaces = nfaces;
fileData.vertices = (int *) malloc(fileData.nvert * sizeof(float));
fileData.triFaces = (int *) malloc(fileData.nfaces * sizeof(int));
// Print to check correct size was allocated
printf("%d\n", (fileData.nvert * sizeof(float)));
printf("%d\n", (fileData.nfaces * sizeof(int)));
int i;
float ftemp1, ftemp2, ftemp3;
int itemp1, itemp2, itemp3;
fscanf(srcFile, "%f", &ftemp1);
printf("%lf", ftemp1);
fscanf(srcFile, "%f", &ftemp2);
// fscanf(srcFile, " %lf", &ftemp3);
/* for (i = 0; i < nvert; ++i)
{
fscanf(srcFile, "%f %f %f\n", &ftemp1, &ftemp2, &ftemp3);
fileData.vertices[i].x = ftemp1;
fileData.vertices[i].y = ftemp2;
fileData.vertices[i].z = ftemp3;
}
*/
return(fileData);
}
The problem I am having is with the whole last section that is currently in quotes (The 2 fscanf lines above it are me attempting to test). If I have just one float being read it works fine, but when I add the second or third the whole function fails to even run, although it still compiles. I believe it to be caused by the negative sign in the input, but I don't know how I can fix it.
The data is in the form
OFF
4000 7000 0
0.8267261981964111 -1.8508968353271484 0.6781123280525208
0.7865174412727356 -1.8490413427352905 0.7289819121360779
With the floats continuing on for 4000 lines (hence for loop). These are the structs I have made
typedef struct
{
float x;
float y;
float z;
} Point3D;
typedef struct
{
int face1;
int face2;
int face3;
} triFace;
typedef struct
{
int nvert;
int nfaces;
Point3D *vertices;
triFace *triFaces;
} OFFFile;
Text dump of another file with a lot less lines, also does not work in the for loop. Only using this for testing. https://justpaste.it/9ohcc
Your main problem is the first line in the readOFFFile function:
fscanf(srcFile, "%s\n");
This tries to read a string (presumably the string OFF on the first line of the file), but you don't give fscanf any place to store the string, so it crashes. (As an aside, your compiler really should have warned you about this problem. If it didn't, it's old-fashioned, and there are lots of easy mistakes that it's probably not going to warn you about, and learning C is going to be much harder than it ought to be. Or perhaps you just need to find an option flag or checkbox to enable more warnings.)
You can tell fscanf to read and discard something, without storing it anywhere, using the * modifier. Here's a modified version of your program, that works for me.
void ReadOFFFile(OFFFile *fileData, FILE* srcFile)
{
fscanf(srcFile, "%*s");
if(fscanf(srcFile, "%d %d %*s", &fileData->nvert, &fileData->nfaces) != 2) {
exit(1);
}
fileData->vertices = malloc(fileData->nvert * sizeof(Point3D));
fileData->triFaces = malloc(fileData->nfaces * sizeof(triFace));
int i;
for (i = 0; i < fileData->nvert; ++i)
{
if(fscanf(srcFile, "%f %f %f", &fileData->vertices[i].x,
&fileData->vertices[i].y,
&fileData->vertices[i].z) != 3) {
exit(1);
}
}
}
I have made a few other changes. The other fscanf call, that reads three values but only stores two, also needs a * modifier. I check the return value of fscanf to catch errors (via a crude exit) if the input is not as expected. I got rid of the \n characters in the fscanf calls, since they're not necessary, and potentially misleading. I got rid of some unnecessary temporary variables, and I had the readOFFFile function accept a pointer to an OFFFile structure to fill in, rather than passing and returning it.
Here is the main program I tested it with:
int main()
{
OFFFile fd;
FILE *fp = fopen("dat", "r");
ReadOFFFile(&fd, fp);
for (int i = 0; i < fd.nvert; ++i)
printf("%f %f %f\n", fd.vertices[i].x, fd.vertices[i].y, fd.vertices[i].z);
}
This is still an incomplete program: there are several more places where you need to check for errors (opening the file, calling malloc, etc.), and when you do detect an error, you need to at least print a useful error message before exiting or whatever.
One more thing. As I mentioned, those \n characters you had in the fscanf format strings were unnecessary and misleading. To illustrate what I mean, once you get the program working, have it try to read a data file like this:
OFF 2 0
0 0.8267261981964111
-1.8508968353271484 0.6781123280525208
0.7865174412727356 -1.8490413427352905 0.7289819121360779
Totally malformed, but the program reads it without complaint! This is one reason (one of several dozen reasons, actually) why the scanf family of functions is basically useless for most things. These functions claim to "scan formatted data", but their definition of "formatted" is quite loose, in that they actually read free-form input, generally without any regard for line boundaries.
For some advice on graduating beyond scanf and using better, more reliable methods for reading input, see this question. See also this section and this section in some online C programming course notes.
The line:
fscanf(srcFile, "%s\n");
is invoking undefined behavior. The compiler should warn you about that. Once you've invoked UB, there's no point in speculating further about what is happening.
It's not clear to me what you intended that line to do, but if you use %s in a scanf, you need to give it a valid place to write data. You should always check the value returned by scanf to ensure that you have actually read some data, and you should never use "%s" without a width modifier. Perhaps you want something like:
char buf[256];
if( fscanf(srcFile, "%255s ", buf) == 1 ){
/* Do something with the string in buf */
}
From your comment, it seems that you are intending to use that scanf to skip a line. I strongly recommend using a while(fgetc) loop instead of scanf to do that. If you do want to use scanf, you could try something like fscanf(srcFile, "%*s\n"), but beware that it will stop at the first whitespace, and not necessarily consume an entire line. You could also do fscanf(srcFile, "%*[^\n]%*c"); to consume the line, but you are really better off using a fgetc in a while loop.
Addressing title question:
"How do I read multiple floats from one line of a file"
...with suggestions for a non-scanf() approach.
Assuming the file is opened, (and a file pointer) fp is obtained ) , the first two lines are already handled, and values into ints, say the lines value is converted to int lines;
And given your struct definition (modified to use double to accommodate type compatibility in code below):
typedef struct
{
double x;
double y;
double z;
} Point3D;
In a function somewhere here is one way to parse the contents of each data line into the 3 respective struct values using fgets(), strtok() and strtod():
char delim[] = " \n";
char *tok = NULL;
char newLine[100] = {0};
Point3D *point = calloc(lines, sizeof(*point));
if(point)
{
int i = 0;
while(fgets(newLine, sizeof newLine, fp))
{
tok = strtok(newLine, delim);
if(tok)
{
if(parseDbl(tok, &point[i].x))
{
tok = strtok(NULL, delim);
if(tok)
{
if(parseDbl(tok, &point[i].y))
{
tok = strtok(NULL, delim);
if(tok)
{
if(!parseDbl(tok, &point[i].z))
{
;//handle error
}else ;//continue
}else ;//handle error
}else ;//handle error
}else ;//handle error
}else ;//handle error
}else ;//handle error
i++;//increment for next read
}//end of while
}else ;//handle error
Where parseDbl is defined as:
bool parseDbl(const char *str, double *val)
{
char *temp = NULL;
bool rc = true;
errno = 0;
*val = strtod(str, &temp);
if (temp == str)
rc = false;
return rc;
}
Im still considered a beginner in c and i started learning about files. i already built a blank file. Every time i compile this program, the file is still blank. Need help!!
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * x;
char name[25];
printf("enter your name: ");
scanf("%s", &name);
x = fopen("x1.txt", "w");
if(x = NULL)
{
printf("Unable to open the file");
}
else
{
fprintf(x, "%s\n", name);
printf("date has been entered successfully to the file");
fclose(x);
}
return 0;
}
Thank you
A file existed, and contained my name, after making the following changes and rebuilding/running the program:
(see comments in line for reasons)
Change:
if(x = NULL)//assignment - as is, this statement will always evaluate
//to false, as x is assigned to NULL.
To:
if(x == NULL)// comparison - this will test whether x is equal to NULL without changing x.
Change: (this was key to your file not being populated)
scanf("%s", &name);//the 'address of' operator: '&' is not needed here.
//The symbol 'name' is an array of char, and is
//located at the address of the first element of the array.
To:
scanf("%s", name);//with '&' removed.
Or better:
scanf("%24s", name);//'24' will prevent buffer overflows
//and guarantee room for NULL termination.
Yet one more method to address the comment about not using scanf at all...:
char buffer[25];//create an additional buffer
...
memset(name, 0, 25);//useful in loops (when used) to ensure clean buffers
memset(buffer, 0, 25);
fgets(buffer, 24, stdin);//replace scanf with fgets...
sscanf(buffer, "%24s", name);//..., then analyze input using sscanf
//and its expansive list of format specifiers
//to handle a wide variety of user input.
//In this example, '24' is used to guard
//against buffer overflow.
Regarding the last method, here is a page detailing the versatility of handling user input strings using sscanf.
#include<stdio.h>
#include<stdlib.h>
typedef struct{
char cStdName[50];
int nStdNum;
char cStdClass[4];
float dStdAvg;
}student;
student* students;
int cmp(const void* a, const void* b);
void main() {
int num = 0,i=0;
FILE *f;
printf("Number of students:");
scanf("%d", &num);
students = (student*)malloc(num*sizeof(student));
for(i=0;i<num;++i){
student* ptr = students+i*sizeof(student);
printf("Name:");
scanf("%s", ptr->cStdName);
printf("Num");
scanf("%d", &ptr->nStdNum);
printf("Class:");
scanf("%s", ptr->cStdClass);
printf("Grade:");
scanf("%f", &ptr->dStdAvg);
}
f = fopen("bin.bin","wb");
fwrite(&num,sizeof(int),1,f);
fwrite(students,sizeof(student),num,f);
fclose(f);
system("pause");
}
This is supposed to output the number of students and all the structure 'array' in a binary file and it works with 1 student. But when I add >=2 people, the file looks like this:
http://i.imgur.com/LgL8fUa.png
If I add only 1 student, there is still some of this Windows path nonsense:
http://i.imgur.com/s7fm9Uv.png
It is OK though, the program that reads the file ignores everything after the NULL(I mean, for the first char array).
I think the problem is somewhere in the for() loop and the pointer juggling but I can't tell where.
student* ptr = students + i * sizeof(student);
In C, pointer arithmetic already includes sizeof(student). You will read past the end of your arrray.
student* ptr = students + i;
However, you'll notice accessing to ptr is the same as accessing to students[i].
There are a few issues with your code:
First of all, as Kirilenko said, you should use students[i] in your code. In your case, students + i * sizeof(student) goes out of bounds.
It's never a good idea to use fwrite with an array of structs. That's because the compiler may add some space between the members of a struct (padding), which means that when you pass an array of structs to fwrite, the padding bytes (which will contain garbage) will be printed.
The same also applies to the char array members in your struct. All unused bytes will contain garbage, which will be printed when you use fwrite. It's better to use strlen to determine how many read characters each char array contains.
Here's how I'd write the students' array to a file:
void write(students* array, int len, FILE* out)
{
int i;
fwrite(len, 1, sizeof(len), out);
for (i = 0; i < len; i++){
fwrite(array[i]->cStdName, 1, strlen(array[i]->cStdName), out);
fwrite(array[i]->nStdNum, 1, sizeof(array[i]->nStdNum), out);
fwrite(array[i]->cStdClass, 1, strlen(array[i]->cStdClass), out);
fwrite(array[i]->dStdAvg, 1, sizeof(array[i]->dStdAvg), out);
}
}
Kirilenko's answer should solve your immediate problem, but there are errors ranging from trivial to severe on nearly every line of this program. Depending on what your larger goal is, you might not need to fix all of them, but I'm going to write them all down anyway, to illustrate the size of the iceberg.
void main() {
int main(void). The int is an absolute requirement. In C (not C++) writing () for a function argument list means the arguments are unspecified, not that there are no arguments; technically you can get away with it here because this is a function definition, but it's bad style.
The opening curly brace of a function definition always goes on a line by itself, even if all other opening braces are cuddled.
int num = 0,i=0;
Inconsistent spacing. Initializations are unnecessary.
printf("Number of students:");
Some style guides prefer fputs("Number of students", stdout); when you're not using printf's formatting capabilities. But some compilers can do the transformation for you, and it's not a big deal.
scanf("%d", &num);
Never use scanf, fscanf, or sscanf, because:
Numeric overflow triggers undefined behavior. The C runtime is allowed to crash your program just because someone typed too many digits.
Some format specifiers (notably %s, which you use later in this program!) are unsafe in exactly the same way gets is unsafe, i.e. they will cheerfully write past the end of the provided buffer and crash your program (this particular program doesn't look security-sensitive to me, but one should always code as if one's programs are at least somewhat dangerous that way).
They make it extremely difficult to handle malformed input correctly.
The correct way to read a single nonnegative number from the user is like this:
unsigned long getul(void)
{
char buf[80], *endp;
unsigned long val;
for (;;) {
fgets(buf, 80, stdin);
val = strtoul(buf, &endp, 10);
if (endp != buf && *endp == '\n')
return val;
if (buf[strlen(buf)] != '\n')
while (getchar() != '\n')
/* discard */;
fprintf(stderr, "*** Enter one nonnegative integer, smaller than %lu.\n",
ULONG_MAX);
}
}
You can get away with a fixed-size buffer here because nobody's ULONG_MAX is so large that it doesn't fit in 80 characters.
students = (student*)malloc(num*sizeof(student));
You should use calloc here, so that when you go to write your structures to disk, they aren't full of junk. (Or write custom serializers as suggested by Alexandros, that will also avoid the problem.)
for(i=0;i<num;++i){
Preferred style is for (i = 0; i < num; i++) {. In addition to the spacing, use i++ instead of ++i so that i appears in the same position in all three expressions; this makes it easier to read.
student* ptr = students+i*sizeof(student);
student* ptr = students + i; as discussed elsewhere.
printf("Name:");
scanf("%s", ptr->cStdName);
See comments above. You want another helper function, like so:
void getstr(char *buf, size_t len)
{
size_t n;
for (;;) {
fgets(buf, len, stdin);
n = strlen(buf);
if (n < len && buf[n] == '\n') {
memset(buf+n, 0, len-n);
return;
}
while (getchar() != '\n')
/* discard */;
fprintf(stderr, "*** Enter no more than %lu characters.",
(unsigned long)(len-1));
}
}
...
scanf("%f", &ptr->dStdAvg);
And here you need another helper function. getf is exactly the same as getul except it uses strtod, and of course the error message is a little different.
f = fopen("bin.bin","wb");
Isn't that an awfully generic file name? There should probably be a way for the user to specify it.
fwrite(&num,sizeof(int),1,f);
Your file format needs a magic number.
fwrite(students,sizeof(student),num,f);
You are writing binary data to disk in CPU-endian order. That may not be a problem for this application, but be aware that you might have a cross-platform compatibility headache down the road. (Personally, for what it looks like you're doing, I would use a textual serialization such as JSON, or a simple no-daemon database such as sqlite.) See Alexandros' answer for more potential problems with this file format.
It's rarely a problem when writing to files on disk, and this isn't the sort of program whose output gets piped somewhere, but still, I'm'a mention that fwrite does not guarantee to write all the data you provide it. Technically you have to call fwrite in a loop like this:
size_t r, n = sizeof(student) * num;
char *p = (char *)students;
while (n > 0) {
r = fwrite(p, 1, n, f);
if (r == 0) break; /* write error */
n -= r;
p += r;
}
For this to work correctly you must do the multiplication yourself and pass 1 for the second argument to fwrite; otherwise a short write may end in the middle of an "element of data" and you have no way of knowing that this has happened.
fclose(f);
Check for write errors before closing the file.
if (ferror(f) || fclose(f)) {
perror("bin.bin");
return 1; /* unsuccessful exit */
}
...
system("pause");
Just return 0. Programs that make you hit a key to exit are batch-unfriendly.
The pointer assignment can be outside for loop once and you can increment the pointer at the end of for loop. Try this.
Ok. Here is an attempt to explain what is happening, the ptr is pointing to first element in students list of struct types and when you increment the ptr at the end of the for loop, it points to the next student in the list.
---
students = (student*)malloc(num*sizeof(student));
student* ptr = students;
for(i=0;i<num;++i){
printf("Name:");
----
----
ptr++;
}
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.
So i have this big code ( so wont be able to put the entire thing here).
But at a point i have this.
while(ptr1!=NULL)
{
printf("%sab ",ptr1->name);
puts(ptr1->name);
ptr1=ptr1->next;
}
Now my ptr1 point to a an entry of the array of a structure( each entry being a linked list), and the structure was populated from a file.
Now in this loop it prints
FIRSTab FIRST
SECONDab SECOND
THIRD
Now why doesnt my THIRD GETS PRINTED TWICE?
Also if i do
printf(" %s",ptr1->name); // i.e. one space before %s
I get
THIRDD
Putting 2 spaces before %s gives me
THIRDRD
3 spaces gives
THIRDIRD
And so on.
Also if i try to do strcmp(ptr1->name,"THIRD") i wont get the correct comparison for THIRD.
Why??
Here is how i populated my structure.
// G is the structure, fp is passed as argument to function.
//THe file format is like this.
//FIRST SECOND THIRD
//NINE ELEVEN
//FOUR FIVE SIX SEVEN
// and so on.
int i=0,j=0,k=0;
char string[100];
while(!feof(fp))
{
if(fgets(string,100,fp))
{
G[i].index=i;
k=0;j=0;
//\\printf("%d",i);
//puts(string);
node *new=(node*)malloc(sizeof(node));
new->next=NULL;
G[i].ptr=new;
node* pointer;
pointer=G[i].ptr;
while(string[j]!='\n')
{
if(string[j]==' ')
{
pointer->name[k]='\0';
k=0;
node *new=(node*)malloc(sizeof(node));
new->next=NULL;
pointer->next=new;
pointer=pointer->next;
j++;
}
else
{
pointer->name[k++]=string[j];
j++;
}
}
pointer->name[k]='\0';
i++;
}
Your third string probably contains the characters THIRD followed by \r (carriage return). Why it contains this can only be determined by knowing the contents of the file and how your read it.
It is likely that you are either working on a system that uses a single newline character as a line terminator (but the file you are opening comes from a system that uses a carriage return and newline pair) or that the file pointer that you were passed (fp) was opened in binary mode.
If you can't change the file pointer to be opened in text mode then a quick fix might be to change this condition while(string[j]!='\n') to while(string[j]!='\n' && string[j] != '\r'), although you might want a more robust solution that handles multiple whitespace characters.