I'm trying to read a file using a structure, seems all fine until I face a string with spaces, let me give you an example.
In my file I've got text like this:
Luca 21 Università di Palermo 22.3
I can read all the row, but when I arrive at the third element which is the university, it reads only the "Università" and not the rest. I've tried using [^\t\n] but then it reads the next information also and I don't want this. This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct{
char nome[30];
int eta;
char universita[30];
double media;
}Studente;
int main(int argc, char** argv){
FILE* fp;
Studente s;
if((fp=fopen("studenti.txt", "r ")) == NULL){
perror("fopen");
exit(1);
}
char buffer[200]; //tipicamente 200 caratteri stanno su una riga
// fgets(buffer, 200, fp); //facciamo una prima lettura
/*while(!feof(fp)){
sscanf(buffer, " %s %d %s %f", &s.nome, &s.eta, &s.universita, &s.media); //assegnamo il valore della stringa contenuta nel buffer ai tre indirizzi di memoria
printf("Lo studente si chiama: %s\n", s.nome);
printf("Ha %d anni\n", s.eta);
printf("Frequenta %s\n", s.universita);
printf("Ha una media del o\n", s.media);
fgets(buffer, 200, fp); //continuiamo a leggere
}*/
while(fgets(buffer, sizeof(buffer), fp)){
sscanf(buffer, " %s %d %s %f", s.nome, &s.eta, s.universita, &s.media); //assegnamo il valore della stringa contenuta nel buffer ai tre indirizzi di memoria
printf("Lo studente chiama: %s\n", s.nome);
printf("Ha %d anni\n", s.eta);
printf("Frequenta %s\n", s.universita);
printf("Ha una media del %lf\n", s.media);
printf("\n");
}
fclose(fp);
return 0;
}
The output that I want is: The name is X He is y He goes at z His score is k
Thanks in advance.
CodePudding user response:
Assuming:
- Student name does not include spaces.
- University name may contain spaces but does not include digits.
- Average (media) does not start with a dot, such as
.12
.
Then would you please try:
#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//#include <ctype.h>
#define FILENAME "studenti.txt"
typedef struct {
char nome[30];
int eta;
char universita[30];
double media;
} Studente;
/*
* removes trailing spaces of str by modifying the array str
*/
char *trimright(char *str)
{
char *p;
for (p = str; *p != '\0'; p );
for (--p; p - str >= 0 && (*p == ' ' || *p == '\t') ; p--) {
*p = '\0';
}
return str;
}
int main(int argc, char **argv)
{
FILE *fp;
Studente s;
char buffer[200]; // tipicamente 200 caratteri stanno su una riga
int n; // count the number of elements
if ((fp = fopen(FILENAME, "r")) == NULL) {
perror(FILENAME);
exit(1);
}
while (fgets(buffer, sizeof(buffer), fp)) {
n = sscanf(buffer, "%s %d %[^0-9] %lf", s.nome, &s.eta, s.universita, &s.media);
if (n == 4) {
printf("Lo studente chiama: %s\n", s.nome);
printf("Ha %d anni\n", s.eta);
printf("Frequenta %s\n", trimright(s.universita));
printf("Ha una media del %f\n", s.media);
} else {
printf("Possible format error with: %s", buffer);
}
printf("\n");
}
fclose(fp);
return 0;
}
The identifier %[^0-9]
makes sscanf
read characters other than digits.
then the parsed string s.universita
may contain trailing space character(s).
The function trimright
is applied to trim them. If you don't mind including
trailing space characters, you don't need to use the function.
If you want to allow digits in the university name, please let me know.
CodePudding user response:
Use strrchr
to find the last space in the record.
From that pointer, try to scan a double.
Try to scan a string and an integer from the record. The %u
specifier will store the number of characters processed by the scan.
Subtract the pointers to get the number of characters to the last space.
Subtract the number of characters processed to get the the number of characters in the remaining sub-string.
strncpy
the sub-string, making sure to set the zero terminator.
#include <stdio.h>
#include <string.h>
typedef struct{
char nome[30];
int eta;
char universita[30];
double media;
}Studente;
int main ( void) {
char buffer[200] = "Luca 21 Università di Palermo 22.3";
char *space = NULL;
int count = 0;
Studente s = { "", 0, "", 0.0};
if ( ( space = strrchr ( buffer, ' '))) { // pointer to last space
if ( 1 == sscanf ( space, "%lf", &s.media)) {
if ( 2 == sscanf ( buffer, ")s%d %n", s.nome, &s.eta, &count)) {
int length = (int)(space - buffer) - count;
if ( length > 0 && length < sizeof s.universita) {
strncpy ( s.universita, buffer count, length);
s.universita[length] = 0;
printf ( "%s\n", s.nome);
printf ( "%d\n", s.eta);
printf ( "%s\n", s.universita);
printf ( "%f\n", s.media);
}
else {
fprintf ( stderr, "Sub-string is too long\n");
}
}
else {
fprintf ( stderr, "Could not scan string and integer\n");
}
}
else {
fprintf ( stderr, "Could not scan double\n");
}
}
else {
fprintf ( stderr, "Could not find a space\n");
}
return 0;
}