Permalänk

C fgets och annat djävluskap

Har en uppgift och skall läsa in en fil, hitta specifika saker (namn, ålder osv) i strängar i denna fil, har även fått en struct med förnamn, efternamn ålder, men lyckas ej få det att göra som jag vill. Har läst in filen, men förstår ej hur jag skall lyckas söka i den, någon som kan hjälpa, behövs mer info?

Permalänk
Medlem
Skrivet av lesscool10:

Har en uppgift och skall läsa in en fil, hitta specifika saker (namn, ålder osv) i strängar i denna fil, har även fått en struct med förnamn, efternamn ålder, men lyckas ej få det att göra som jag vill. Har läst in filen, men förstår ej hur jag skall lyckas söka i den, någon som kan hjälpa, behövs mer info?

Mer information behövs.
Vad är formatet på filen?
Den struct du har fått, motsvarar den hur information ligger lagrad i filen, eller är den något du skall fylla i?

Permalänk
Keeper of Traditions

Visa gärna hur långt du lyckats komma själv också så man kan ge hintar om hur du bör fortsätta, wrappa koden i code-taggar så blir det formaterat. Antar att det är en skoluppgift och tanken är ju att du ska lösa den själv, trots allt

Visa signatur

|| AMD Ryzen 9800X3D || Asus RTX 4070 TI Super TUF || Samsung 990 PRO 4TB || Kingston Fury CL30 64GB || Corsair RM 850x || Antec P183 || Asus G-Sync RoG Swift PG279Q || Dell XPS 15 ||

The Force is like Duct Tape, it has a light side, a dark side, and holds the universe together.

Permalänk

@Dunder: wrappa koden i code taggar, va?

Permalänk
Keeper of Traditions
Skrivet av lesscool10:

@DunderKlumpen: wrappa koden i code taggar, va?

Ja, när du klistrar in koden här i forumet,

Så blir det rätt formaterat med indentering och så

Visa signatur

|| AMD Ryzen 9800X3D || Asus RTX 4070 TI Super TUF || Samsung 990 PRO 4TB || Kingston Fury CL30 64GB || Corsair RM 850x || Antec P183 || Asus G-Sync RoG Swift PG279Q || Dell XPS 15 ||

The Force is like Duct Tape, it has a light side, a dark side, and holds the universe together.

Permalänk

@Dunder: Hur gör jag det?

Permalänk
Hedersmedlem
Skrivet av Dunder:

Ja, när du klistrar in koden här i forumet,

Så blir det rätt formaterat med indentering och så

Eftersom forumet än så länge är begränsat och inte riktigt visar hur man gör det kan vara vara snyggt att visa att det görs med

@lesscool10
Den här snutten kan kopieras exakt som den ser ut:
[code]
Här skriver man sin kod...
[/code]

Permalänk
Keeper of Traditions
Skrivet av Shimonu:

Eftersom forumet än så länge är begränsat och inte riktigt visar hur man gör det kan vara vara snyggt att visa att det görs med

Just ja, jag hade för mig att det fanns som knapp när man skrev sitt meddelande, likt knapparna för bilder osv, men den saknas ser jag nu. Sorry!

Visa signatur

|| AMD Ryzen 9800X3D || Asus RTX 4070 TI Super TUF || Samsung 990 PRO 4TB || Kingston Fury CL30 64GB || Corsair RM 850x || Antec P183 || Asus G-Sync RoG Swift PG279Q || Dell XPS 15 ||

The Force is like Duct Tape, it has a light side, a dark side, and holds the universe together.

Permalänk

@Dunder:

#include "import_register.h" #include <string.h> #include <stdio.h> void print_data(personal_data *data); int main (int argc, char** argv){ struct personal_data person1; strncpy(person1.first); strncpy(person1.last); strncpy(person1.age); strncpy(person1.sex); strncpy(person1.town); int person_count; personal_data *data = import_data (&person_count, "./personal_data.dat"); int meny; int i = 0; while (1 > i){ printf("Register of personal data:\n\n" ); printf("Make a choice:\n\n" ); printf("1. Print all data\n" ); printf("2. Find person first name\n" ); printf("3. Find person last name\n" ); printf("4. Find person from age\n" ); printf("5. Find person from sex\n" ); printf("6. Find person from town\n" ); printf("7. Quit\n" ); printf("Enter your choice " ); scanf("%d",&meny ); switch (meny) { case 1: printf("%s\n", person1.first ); break; case 2: printf("Enter first name\n" ); case 3: printf("Enter lastname\n" ); case 4: printf("Enter age\n" ); break; case 5: printf("Enter sex\n" ); break; case 6: printf("Enter town\n" ); case 7: i = 2; break; default: printf("Not a valid option\n" ); i = 2; } } free_data(data, person_count); return 0; }

Permalänk
Permalänk
Hedersmedlem
Skrivet av lesscool10:

npnp

Permalänk
Medlem

@lesscool10 Bra att du bifogar kod, och gör det på rätt sätt, men för att kunna hjälpa dig behöver du dela med dig av all kod; du har utelämnat innehållet i filen import_register.h, likaså hade vi eventuellt kunna ha nytta av innehållet i filen personal_data.dat.

Permalänk

@suzieq:
import_register.h:

#ifndef _IMPORT_REGISTER_ #define _IMPORT_REGISTER_ #include <stdlib.h> #include <string.h> #include <stdio.h> typedef struct personal_data { char* first; char* last; int age; char* town; int sex; } personal_data; personal_data *import_data(int *count, char *filename); void free_data(personal_data *data, int person_count); #endif

personal_data.dat

Anders:Andersson:35:Fagersta:Man Henrik:Eriksson:21:Stockholm:Man Erik:Mansson:56:Kiruna:Man Thor:Ragnarsson:73:Solna:Man Samuel:Magnusson:19:Furulund:Man Marcus:Asp:36:Ekeby:Man Henrik:Mansson:58:Kiruna:Man Anders:Orre:45:Solna:Man Gustaf:Jansson:86:Ekeby:Man August:Kopparberg:29:Fagersta:Man Emelie:Gabrielsson:17:Sundsvall:Kvinna Camilla:Norsk:42:Ekeby:Kvinna Josefine:Jansson:58:Solna:Kvinna Evelina:Eriksson:36:Stockholm:Kvinna Julia:Andersson:19:Stockholm:Kvinna Camilla:Vass:27:Fagersta:Kvinna Inga:Vener:63:Kiruna:Kvinna Sofia:Eriksson:75:Furulund:Kvinna Susanne:Dafgard:21:Sundsvall:Kvinna Sofia:Jolle:54:Ekeby:Kvinna

Permalänk
Medlem

@lesscool10 Skriver kanske lite väl mycket kod, men det lättaste sättet att lära sig programmering är att läsa och skriva kod, istället för att läsa förklaringar och tappa bort sig i facktermer. Eftersom jag inte vet hur import_register.c ser ut så har jag gjort några antaganden, och därför inte heller kunnat testa koden, men med lite tur så fungerar det och kan förhoppningsvis vara till hjälp.

#include <stdio.h> #include <string.h> #include "import_register.h" void print_person(int index, personal_data *data); void print_all_data(int count, personal_data *data); void search_by_first(char *first, int count, personal_data *data); void search_by_last(char *last, int count, personal_data *data); void search_by_age(int age, int count, personal_data *data); void search_by_town(char *town, int count, personal_data *data); void search_by_sex(int sex, int count, personal_data *data); int main(int argc, char *argv[]) { int count; /* håller reda på antalet element i vektorn 'data' */ personal_data *data; /* vektorn 'data' som lagrar alla personer */ int run; /* variabel för while loopen */ int choice; /* variabel för menyval, ålder och kön */ char string[BUFSIZ]; /* sträng för sökning av namn och stad */ /* när programmet exekveras är det som du säkert vet funktionen 'main' som körs först. när man exekverar ett program i kommandotolken (eller terminalen som den kallas i linux) kan man ange parametrar, som till exempel när du anropar kompilatorn; $ gcc -ansi -pedantic -Wall main.c import_register.c där '-ansi', '-pedantic', '-Wall', 'main.c' och så vidare alla är sådana parametrar. antalet sådana parametrar lagras som du kanske vet i variabeln 'argc' samtidigt som de olika parametrarna, vilka utgörs av strängar, lagras i vektorn 'argv'. även om inga parametrar anges när ett program exekveras så kommer 'argc' alltid vara lika med 1, och 'argv[0]' innehåller i sin tur programmets sökväg och namn. för att kunna specificera filen du vill läsa, istället för att hårdkoda filens namn i koden, så kan du använda denna möjlighet på följande sätt: */ if (argc == 1) { printf("Usage: %s <file>\n", argv[0]); return 0; } if ((data = import_data(&count, argv[1])) == NULL) { printf("Failed to import data from %s\n", argv[1]); return 0; } /* nu loopar vi menyn med mer lättförståelig kod; så länge 'run' är sant det vill säga har ett värde annat än 0 (i detta fallet 1) så körs loopen om och om igen: */ run = 1; while (run) { printf("Register of personal data.\n" "Make a choice:\n" " 1. Print all data\n" " 2. Search by first name\n" " 3. Search by last name\n" " 4. Search by age\n" " 5. Search by town\n" " 6. Search by sex\n" " 7. Exit\n" "Enter choice: "); scanf("%d", &choice); /* 'getchar' är nödvändig för att fånga radbrytet som 'scanf' lämnar kvar i inmatningsströmmen (från ENTER-trycket)... */ getchar(); /* nu anropar vi rätt funktion eller funktioner baserat på användarens val. observera att det är viktigt med 'break' efter varje case (förutom efter default som alltid ska vara det sista alternativet längst ner i switch-satsen). om man missar ett 'break' efter exempelvis 'case: 1' så kommer koden automatiskt fortsätta till 'case: 2', vilket betyder att användaren som bara ville få all data utskriven också gör en sökning efter förnamn. oberserva att vi inte använder 'scanf' för inmatning a strängar, detta då scanf inte bryr sig om hur stor sträng- vektorn är (i detta fallet 'string'). detta är ett stort problem då man på så sätt kan skriva data till minne som inte är avsatt för strängen. istället används 'fgets' tillsammans med 'strcspn' för inmatning, där 'strcspn' används för att ta bort radbrytet (från ENTER-trycket). */ switch (choice) { case 1: print_all_data(count, data); break; /* viktigt... */ case 2: printf("Enter first name: "); fgets(string, BUFSIZ, stdin); string[strcspn(string, "\n\r")] = '\0'; search_by_first(string, count, data); break; /* ...med... */ case 3: printf("Enter last name: "); fgets(string, BUFSIZ, stdin); string[strcspn(string, "\n\r")] = '\0'; search_by_last(string, count, data); break; /* ...'break'... */ case 4: printf("Enter age: "); scanf("%d", &choice); search_by_age(choice, count, data); break; /* ...efter... */ case 5: printf("Enter town: "); fgets(string, BUFSIZ, stdin); string[strcspn(string, "\n\r")] = '\0'; search_by_town(string, count, data); break; /* ...varje... */ case 6: printf("Enter sex (0 = Man, 1 = Woman): "); scanf("%d", &choice); search_by_sex(choice, count, data); break; /* ...'case'... */ case 7: run = 0; break; /* ...förutom... */ default: printf("Invalid choice\n"); break; /* ...här. men det ser bra ut. */ } } free_data(data, count); return 0; } void print_person(int index, personal_data *data) { /* skriv ut all information om en specifik person. eftersom variabeln 'sex' i strukturen 'personal_data' är av typen 'int' så gör jag antagandet att män har värdet 0, och kvinnor värdet 1, detta beror dock helt och hållet på hur funktionen 'import_data' är kodad. */ printf("%s\n%s\n%d\n%s\n%s\n\n", data[index].first, data[index].last, data[index].age, data[index].town, ((data[index].sex == 0)) ? "Man" : "Woman"); return; } void print_all_data(int count, personal_data *data) { /* skriv ut all information om alla personer, i detta fallet i bakvänd ordning. */ while (count--) print_person(count, data); return; } void search_by_first(char *first, int count, personal_data *data) { /* loopa igenom alla personer i 'data' och skriv ut all information om personerna med samma förnamn som eftersöks. observera att 'strcmp' är shiftlägeskänslig, dvs. 'andreas' är inte samma sak som 'ANDREAS'. */ while (count--) if (strcmp(first, data[count].first) == 0) print_person(count, data); return; } void search_by_last(char *last, int count, personal_data *data) { /* TODO */ return; } void search_by_age(int age, int count, personal_data *data) { /* TODO */ return; } void search_by_town(char *town, int count, personal_data *data) { /* TODO */ return; } void search_by_sex(int sex, int count, personal_data *data) { /* TODO */ return; }

Dold text
Permalänk

@suzieq: Programmet kompilerar och printar Usage: ./personuppgifter <file>

Permalänk
Medlem

@lesscool10: Om du läser koden och kommentarerna så borde det framgå att programmet ger dig möjligheten att specificera vilken fil du vill läsa; istället för att programmet alltid och endast försöker läsa filen vid namn personal_data.dat så ber programmet dig specificera vilken fil du vill läsa, där filens namn anges som en parameter. När du ska starta programmet personuppgifter så skriver du dess sökväg och namn i kommandotolken/terminalen följt av ett mellanslag och därefter filens sökväg och namn. Om de ligger i samma mapp kan det se ut såhär:

$ ./personuppgifter ./personal_data.dat

Detta var bara ett förslag från min sida och givetvis något du kan ersätta med din tidigare kod; byt i så fall ut raderna:

if (argc == 1) { printf("Usage: %s <file>\n", argv[0]); return 0; } if ((data = import_data(&count, argv[1])) == NULL) { printf("Failed to import data from %s\n", argv[1]); return 0; }

mot raden:

data = import_data(&count, "./personal_data.dat");

Permalänk

@suzieq: Ber om ursäkt, va nog lite stressad när jag läste, nu funkar det, dock är alla "sex" woman, hur skulle jag kunna fixa de? I import_register.h är sex = 1 woman och sex = 2 man, om den informationen är av nytta

Permalänk
Medlem

@lesscool10: Då behöver du göra en ändring i funktionen void print_person(int index, personal_data *data); närmare bestämt ändra raden nedan så att den jämför med 2 istället för 0. Alternativt jämföra med 1 och byta plats på "Man" och "Woman".

((data[index].sex == 0)) ? "Man" : "Woman");

Denna rad är en förkortning av en if-else-sats; för att ta ett lättare exempel; istället för att skriva

if (something_is_true()) variable = 123; else variable = 456;

så kan man skriva

variable = (something_is_true()) ? 123 : 456;

Permalänk

@suzieq: tack så obeskrivligt mycket för hjälpen, men vad är /* TODO */? då den inte ser ut som en kommentar, även fast den är i en kommentar?

Permalänk
Medlem

@lesscool10: TODO är mycket riktigt bara en kommentar, de engelska orden to do, alltså att göra. Jag har alltså inte skrivit klart de funktionerna just för att de är så lika de andra, så jag tänkte att du borde fixa det själv Om TODO har en annan färg eller liknande så är det bara för att din textredigerare väljer att markera det; det är vanligt att man skriver TODO-kommentarer för att hålla koll på kod som inte är färdig.

Permalänk

@suzieq: hahaha yet again var jag lite för stressad, men hur gör jag med sex och age? Eftersom de inte är char får jag ett error om att det är en incompatible integer to pointer conversion, de funkar på last och town då de är char, men ej på age och sex

Permalänk
Medlem

@lesscool10: Förstår det som att du försöker använda funktionen strcmp() för att jämföra dina heltal? Nu förstår jag mycket väl att du är nybörjare, men om du tänker efter så är det egentligen väldigt logiskt. strcmp står för string compare, det är alltså en funktion för att jämföra strängar. En sådan funktion är nödvändig för att en sträng inte är en så kallad native typ (det är inte en inbyggd datatyp som exempelvis int); en sträng är en vektor/array av typen char, dvs. en massa char-variabler som ligger i följd efter varandra i minnet. För att jämföra strängar med varandra måste man ju därför kolla så att varje sådan char-variabel i den ena strängen är lika med motsvarande char-variabel i den andra strängen, något man vanligtvis gör med en loop. Eftersom det är vanligt förekommande att man behöver jämföra strängar så finns det redan en sådan funktion i standardbiblioteken i form av strcmp().

Så hur gör man då för att jämföra enstaka variabler av så kallad native typ? Jo du använder ju förstås bara operatorn ==.

... if (age == data[count].age) ...

Permalänk

@suzieq: Är verkligen grovt nybörjare med detta, tack så obeskrivligt mycket för hjälpen!

Permalänk
Skrivet av lesscool10:

@suzieq: Är verkligen grovt nybörjare med detta, tack så obeskrivligt mycket för hjälpen!

Du har fått mycket hjälp av @suzieq, så det är bra att du säger tack.

Programmeringen handlar mycket om erfarenhet och om du väl löst ett problem (typ, hur hittar jag ett visst element i en tabell) så kommer det hjälpa dig när du skall lösa liknande problem i framtiden (kanske, hitta minsta elementet i en tabell). Därför tycker jag att suzieq har gett dig lite för mycket färdig kod och att det varit bättre för dig om du fått lösa mer av problematiken på egen hand.

Men, men, jag kan ha fel, så därför undrar jag vad har du lärt dig av att få en nästan färdig lösning i tråden? Kan du, till exempel, hitta den yngsta personen i filen?