Avatar billede emileej Nybegynder
28. december 2001 - 18:28 Der er 27 kommentarer og
1 løsning

Læse linie for linie med fra en fstream

Jeg har vist sovet lidt i timen... Hvordan er det nu liiige at jeg læser fra en fil der er åbnet med en fstream?

E-)mil
Avatar billede dcoder Nybegynder
28. december 2001 - 18:36 #1
ifstream fil(\"filnavn.dat\");

char et[64], to[64], tre[64];

fil.getline(et,sizeof(et));
fil.getline(to,sizeof(to));
fil.getline(tre,sizeof(tre));

cout << et << endl ;
cout << to << endl ;
cout << tre << endl ;

Eller hele filen...

ifstream fil(\"filnavn.dat\");

char linie[64];

while (! fil.eof())
  {
    fil.getline(limie,sizeof(linie));
    cout << linie << endl ;
  }
Avatar billede emileej Nybegynder
28. december 2001 - 18:45 #2
Hvordan finder jeg ud af om filen blev fundet, og hvordan giver jeg \'et\', \'to\' og \'tre\' den eksakte størrelse på den næste linie der skal indlæses?

E-)mil
Avatar billede emileej Nybegynder
28. december 2001 - 18:46 #3
1 ting mere: Hvordan får jeg den samlede fil længde?
Avatar billede dcoder Nybegynder
28. december 2001 - 18:54 #4
Hmm, godt spørgsmål :) Jeg er ikke voldsomt genial til C++ det er lang tid siden jeg har arbejdet med det *g*

Men ellers kan du jo bare sætte \'et\' \'to\' og \'tre\' til et helt vildt højt tal f.eks. 5000, det er nok kun få linier i en fil der er så lange :)

fillængden... tja, hvad med noget i denne stil

ifstream fil(\"filnavn.dat\");

char linie[64];
int laengde;

while (! fil.eof())
  {
    fil.getline(limie,sizeof(linie));
    laengde = laengde + 1;
  }
Avatar billede dcoder Nybegynder
28. december 2001 - 18:56 #5
if (fil.fail()) {
  cout << \"Filen kunne ikke åbnes\";
}
Avatar billede emileej Nybegynder
28. december 2001 - 20:35 #6
>>dcoder

Din løsning er logisk nok, men på den måde bliver en masse ram jo allokeret til ingen ting. Ikke særligt økonomisk, og i hvert fald ikke noget der falder i god jord hos slutbrugeren...

E-)mil
Avatar billede emileej Nybegynder
28. december 2001 - 20:46 #7
Min kode der følger herunder kører i en uendelig løkke - why?

    while(!conf.eof()){
        switch(i){
        case 0:
            servername=new char(50);
            conf.getline(servername,sizeof(servername));
            break;
        case 1:
            loginname=new char(50);
            conf.getline(loginname,sizeof(servername));
            break;
        case 2:
            loginpass=new char(50);
            conf.getline(loginpass,sizeof(servername));
            break;
        case 3:
            imgpath=new char(50);
            conf.getline(imgpath,sizeof(servername));
        case 4:
            imgdest=new char(50);
            conf.getline(imgdest,sizeof(servername));
        default:
            break;
            break;
        }
        i++;
    }

E-)mil
Avatar billede - Nybegynder
29. december 2001 - 16:47 #8
du mangler break i case 3 og 4
Avatar billede emileej Nybegynder
29. december 2001 - 16:53 #9
Det løser stadigt ikke problemet, og desuden ser jeg nu, at linierne ikke loades ordentligt - kun brudstykker fra filen...

E-(mil
Avatar billede kamikaze Nybegynder
30. december 2001 - 12:55 #10
Du tæller \'i\' op uden for løkken. Du når aldrig til i++;, derfor kører løkken uendeligt :-)
Avatar billede kamikaze Nybegynder
30. december 2001 - 12:56 #11
Prøv at skrive:

switch(i++){
//...
}

istedet for???
Avatar billede kamikaze Nybegynder
30. december 2001 - 12:58 #12
UNDSKYLD......Det er da vist helt forkert...jeg var lidt for hurtig der :-)
(<<trækker mine svar tilbage>>)
Avatar billede emileej Nybegynder
30. december 2001 - 13:26 #13
Hehe - helt i orden. Dinne svar er trukket tilbage *gg* :)
Avatar billede mbulow Nybegynder
30. december 2001 - 17:41 #14
Undskyld hvis jeg allerede har postet dette svar, men jeg kan ikke selv se det, så jeg vil tro der er sket en fejl :) (Selvom jeg er knyttet til tråden, så jeg får mail om alle nye indlæg)

Jeg tror din uendelige løkke skyldes, at du i din while-statement kun checker på om du er nået \'EOF\'... Men hvad tror du helt præcist der sker hvis du ikke er nået \'EOF\' når \'i\' bliver større end 4???
HINT: Du har skrevet to \'break\'-statements i din default-sektion, men det første \'break\' siger \'forlad switch blokken\' (Det andet bliver derfor ignoreret), og så gentages while-løkken, uden af du kommer længere frem i filen...
Med andre ord: Du når ALDRIG \'EOF\' hvis ikke du er der inden \'i\' bliver større end 4

ALDRIG og ALDRIG: Det er ikke helt sandt. \'i\' vil selvfølgelig på et tidspunkt passere den størst mulige værdi og starte forfra med værdien 0, men det sker nok ikke inden for en overkommelig tid ;) Hehe

Prøv den her i stedet for:
(jeg er ikke 100% sikker på at der ikke er skrivebøffer, for jeg er ikke i nærheden af en compiler)

bool bContinue = true;
while(!conf.eof() && bContinue){
  switch(i){
      case 0:
        servername = new char[50];
        conf.getline(servername, sizeof(servername));
        break;
      case 1:
        loginname = new char[50];
        conf.getline(loginname, sizeof(loginname));
        break;
      case 2:
        loginpass = new char[50];
        conf.getline(loginpass, sizeof(loginpass));
        break;
      case 3:
        imgpath = new char[50];
        conf.getline(imgpath, sizeof(imgpath));
        break;
      case 4:
        imgdest = new char[50];
        conf.getline(imgdest, sizeof(imgdest));
        break;
      default:
        bContinue = false;  //break i default ikke nødvendig, så ikke inkluderet :)
  }
  i++;
}

PS!!! Jeg vil næsten gå ud fra at, du skulle være ved enden af filen, når du er færdig med \'case 4\', men som du selv siger, får du kun brudstykker af filen, så der må næsten være noget andet galt... (F.eks. at du har fået indsat et ekstra linjeskift til sidst i filen du indlæser, eller noget andet i den retning)
Men prøv at poste noget mere kode, hvis du har mulighed for det (evt. det hele + filformat)
Avatar billede emileej Nybegynder
30. december 2001 - 18:19 #15
Her er det hele:

bool loadconf(const char *filename){
    ifstream conf(filename);
    if(conf.fail()){
        cout << \"fejl: kunne ikke finde konfigurationsfilen.\\n\\n\";
        return false;
    }
    int i=0;
    //conf.width(i)
    while(!conf.eof() && i < 5){
        switch(i){
        case 0:
            servername=new char(50);
            conf.getline(servername,sizeof(servername));
            break;
        case 1:
            loginname=new char(50);
            conf.getline(loginname,sizeof(servername));
            break;
        case 2:
            loginpass=new char(50);
            conf.getline(loginpass,sizeof(servername));
            break;
        case 3:
            imgpath=new char(50);
            conf.getline(imgpath,sizeof(servername));
            break;
        case 4:
            imgdest=new char(50);
            conf.getline(imgdest,sizeof(servername));
            break;
        default:
            break;
            break;
        }
        i++;
    }
    conf.close();
    if(i < 4){
        cout << \"fejl: manglende element i konfigurationsfilen.\\n\\n\";
        return false;
    }
    return true;
}

E-)mil
Avatar billede mbulow Nybegynder
31. december 2002 - 15:28 #16
AHA!!! :))

Jeg HAR fundet ud af hvorfor det er du kun får brudstykker af filen...

du bruger sizeof på et dynamisk allokeret array, i forsøget på at få antallet af elementer i arrayet, og det er der problemet ligger:

sizeof(servername), giver IKKE antallet af elementer (50), men derimod størrelsen på variablen servername... Servername er en 32-bit pointer => 4 bytes...

Hos mig virkede det så snart jeg erstattede \"sizeof(servername)\" med \"50\"

Lige et par spørgsmål (Det kan jo være der er en snedig pointe):
*) Hvorfor gemmer du på default-sektionen, når den ikke laver noget?
  (Ikke at det gør noget, jeg kom bare til at tænke over det, da den ikke har nogen funktion)
*) Hvordan er du kommet frem til at \"new char(50)\" kan bruges som \"new char[50]\"?
  (Det ser ud til at virke fint, jeg kan bare ikke finde det beskrevet nogen steder overhoved)


GODT NYTÅR til jer alle
  mbulow
Avatar billede emileej Nybegynder
31. december 2002 - 16:21 #17
Jeg har rettet på koden, så hver case ligner følgende:

        case 0:
            servername=new char(60);
            conf.getline(servername,strlen(servername));
            break;

Det giver lidt mening nu, men det er stadigt ikke det rigtige jeg får fat i. Det første element er helt korrekt. Det næste bliver det sidste tegn smidt ned på næste linie, og de næste to giver overhovedet ikke meningn.
Den sidste mangler helt.

Svar til dig:
1) Jeg gemmer hvad hvor?
2) Jeg kan bedst lide at bruge paranteserne i stedet for [ og ], da førstnævnte virker logisk \"array constructor\" agtig.

Godt nytår! *<:-)

E-)mil
Avatar billede mbulow Nybegynder
31. december 2002 - 16:59 #18
Svar 1... Hehe ;)

Du har i switch-blokken 5 sektioner:
case 1, case 2, case 3, case 4 og default, og så mente jeg bare hvorfor du havde default-sektionen, når den ikke lavede noget, men skidt med det :) Hehe
(Jeg elsker bare folks reaktioner når jeg engang imellem snakker totalt sort ;) LOL



Nå tid til at være seriøs igen:

Jeg er ked af at sige det, men strlen(servername) er heller ikke brugbar i den situation her.
strlen() fortæller hvor lang strengen er, og IKKE hvor meget hukommelse du har allokeret

Problem:
--------
Du har allokeret et char-array med 50 elementer.
Lad og sige at du har fyldt den med strengen \"www.eksperten.dk\".
Så vil strlen() returnere 16
Altså: Antal tegn og IKKE allokeret hukommelse (som du har brug for)

Forklaring:
-----------
Problemet er jo at du allokerer et array, men du har ingen anelse om hvad det indeholder, før du har kaldt getline().
Strlen() funktionen returnerer antallet af tegn, ved at lede efter et \'\\0\', og returnere \'\\0\'-tegnets position.
Dvs. når arrayet til at starte med indeholder noget ukendt, kan strlen() returnere hvad-som-helst, for du ved IKKE hvornår strlen() vil finde et \'\\0\' (Måske som første tegn, måske 20000 tegn fremme)
Det kan godt være det vil virke når du compilerer i Debug-mode (Hvis den fylder allokeret hukommelse med 0\'er), men det vil helt sikkert fejle i Release-mode (VisualC++)



Det der sker i dit tilfælde er: (Hvis du er så heldig at strlen() første gang returnerer 50)
Lad os sige at du i \'case 0\' indlæser en streng med 3 tegn. F.eks. \"ABC\"
I næste tilfælde (\'case 1\') bruger du IGEN \"strlen(servername)\", og den vil returnere længden af strengen \"ABC\" (Altså altid længen af den samme variabel), og getline() vil blive begrænset til at indlæse 3 tegn, osv. osv.

Nu siger jeg IKKE at det vil løse dit problem at du skriver strlen(<det rigtige variabelnavn>), for det vil det med garanti IKKE. Du skal vide hvor meget hukommelse der er allokeret, og det tror jeg ikke der er en funktion der kan fortælle dig.





Det blev vist noget af en rodet forklaring, så hvis du ikke kan følge mig, skal du bare sige til :)

BOTTOM LINE: Jeg vil tro at den eneste løsning er at du selv holder styr på hvor mange elementer der er plads til i arrayet, og skriver det som argument til getline(), i stedet for at bruge et funktionskald
Avatar billede mbulow Nybegynder
31. december 2002 - 17:12 #19
UPS!!! Hvis ikke du ved det er \'\\0\' tegnet det tegn der bruges til at afslutte en streng:
(Bare hvis jeg nu introducerede dig for et ukendt tegn)

\"E-)mil\"
lagres i hukommelsen som:
E - ) m i l \\0
Avatar billede emileej Nybegynder
01. januar 2002 - 10:40 #20
Så helt skåret ud i pap, skal jeg bruge

sizeof(char) * 60;

i stedet for

srlen(servername);

E-)mil
Avatar billede emileej Nybegynder
01. januar 2002 - 10:45 #21
Nu ser det således ud:

        case 0:
            servername=new char(60);
            conf.getline(servername,sizeof(char) * 60);
            break;

Resultat: Intet bliver loaded *gg*

E-)mil
Avatar billede mbulow Nybegynder
01. januar 2002 - 13:57 #22
Æhh... OK... Nu er det godt nok MIN tur til at være forvirret :/
Jeg har ændret lidt på min kode så jeg tror den ligner det du efterhånden er kommet frem til, og den virker fint...
Den udfører godt nok en ulovlig handling når den forlader loadconf-funktionen (Når den skal destruere ifstream-objectet \"conf\"), men før det sker, har den indlæst ALLE 5 linjer fra filen præcist som de står.

HER ER ALT MIN KODE:
--------------------

#include <fstream>
#include <iostream.h>

using namespace std;

char *servername;
char *loginname;
char *loginpass;
char *imgpath;
char *imgdest;

bool loadconf(const char *filename){
    ifstream conf(filename);
    if(conf.fail()){
        cout << \"fejl: kunne ikke finde konfigurationsfilen.\\n\\n\";
        return false;
    }
    int i=0;
    while(!conf.eof() && i < 5){
        switch(i){
        case 0:
            servername=new char(50);
            conf.getline(servername,sizeof(char) * 50);
            break;
        case 1:
            loginname=new char(50);
            conf.getline(loginname,sizeof(char) * 50);
            break;
        case 2:
            loginpass=new char(50);
            conf.getline(loginpass,sizeof(char) * 50);
            break;
        case 3:
            imgpath=new char(50);
            conf.getline(imgpath,sizeof(char) * 50);
            break;
        case 4:
            imgdest=new char(50);
            conf.getline(imgdest,sizeof(char) * 50);
            break;
        }
        i++;
    }
    conf.close();
    if(i < 4){
        cout << \"fejl: manglende element i konfigurationsfilen.\\n\\n\";
        return false;
    }
    return true;
}

int main(){
    loadconf(\"c:\\\\config.txt\");
    return 0;
}



MIN CONFIG FIL:
---------------
www.eksperten.dk
eksperten login
eksperten password
hej/hej.img
hej/hej2.img

PS!!! Det har nok ikke nogen betydning men hvilken platform/compiler benytter du?
Avatar billede mbulow Nybegynder
01. januar 2002 - 14:32 #23
Æhh... Jeg er ked af at sige det, men hvis du får samme problem som jeg, med at applikationen crasher når den forlader loadconf-funktionen, så prøv lige at ændre dine \"new char(50)\" til \"new char[50]\" alle steder.
Det gjorde udslaget for mig, så det ikke længere crasher, og hvis din kode ligner min, kan det jo være derfor du ikke får noget input fra filen, for det svarer åbenbart (i VisualC++6) IKKE til en allokering af et array med 50 elementer :/
Avatar billede mbulow Nybegynder
01. januar 2002 - 14:53 #24
Det lader faktisk til at \"new char(50)\" overhoved ikke har noget at gøre med at allokere et array:

Jeg har lige tracet de to forskellige kald til new-operatoren, og det viser sig (Hvis ikke jeg har misforstået noget) at \"new char(50)\" kun allokerer plads til EN char (så ved jeg bare ikke hvilken betydning \"(50)\" har), og \"new char[50]\" allokerer plads til 50 elementer som ønsket... Så du må nok vende dig til at bruge [] i stedet for () i den her forbindelse :)
Avatar billede emileej Nybegynder
01. januar 2002 - 15:09 #25
Weee! Det virker =)
Jeg vil bare helst have det såddan, at mine arrays kun bliver allokeret i den størrelse, der er nødvændig for at holde strengen. Kort sagt: Hvordan finder jeg ud af hvor lang den næste linie er UDEN at allokere noget hukommelse?

E-)mil
Avatar billede mbulow Nybegynder
01. januar 2002 - 15:35 #26
Det var da rart at høre :))

Én måde at slippe for at allokere en masse hukommelse som du måske ikke vil få brug for, er at skifte dine \"char*\" ud med \"string\"\'s, de sørger selv for at allokere den hukommelse de har brug for

string-versionen:
-----------------

#include <fstream>
#include <iostream.h>
#include <string>

using namespace std;

string    sServerName;
string    sLoginName;
string    sLoginPass;
string    sImgPath;
string    sImgDest;

bool loadconf(const char *filename){
    ifstream conf(filename);
    if(conf.fail()){
        cout << \"fejl: kunne ikke finde konfigurationsfilen.\\n\\n\";
        return false;
    }
    int i=0;
    while(!conf.eof() && i < 5){
        switch(i){
        case 0:
            getline(conf, sServerName);
            break;
        case 1:
            getline(conf, sLoginName);
            break;
        case 2:
        getline(conf, sLoginPass);
            break;
        case 3:
        getline(conf, sImgPath);
            break;
        case 4:
        getline(conf, sImgDest);
            break;
        }
        i++;
    }
    conf.close();
    if(i < 4){
        cout << \"fejl: manglende element i konfigurationsfilen.\\n\\n\";
        return false;
    }
    return true;
}

int main(){
    loadconf(\"c:\\\\config.txt\");
    return 0;
}



Nu er det jo ikke sikkert det er så fedt for dig at bruge string\'s, men så kan du jo bruge en enkelt string; indlæse en enkelt linje; allokere (med en char*) hukommelse nok til teksten, og kopiere teksten fra string\'en til char*\'eren:

#include <fstream>
#include <iostream.h>
#include <string>

using namespace std;

char    *szServerName;
char    *szLoginName;
char    *szLoginPass;
char    *szImgPath;
char    *szImgDest;
string    sTempString;

bool loadconf(const char *filename){
    ifstream conf(filename);
    if(conf.fail()){
        cout << \"fejl: kunne ikke finde konfigurationsfilen.\\n\\n\";
        return false;
    }
    int i=0;
    while(!conf.eof() && i < 5){
    //Bemærk lige at jeg har flyttet den her linje UD af switch-blokken
    //(Bare så du ikke overser den ;)
    getline(conf, sTempString);
        switch(i){
        case 0:
        szServerName = new char[sTempString.length() + 1];    //Plads til det sidste \'\\0\'
        strcpy(szServerName, sTempString.c_str());
            break;
        case 1:
        szLoginName = new char[sTempString.length() + 1];
        strcpy(szLoginName, sTempString.c_str());
            break;
        case 2:
        szLoginPass = new char[sTempString.length() + 1];
        strcpy(szLoginPass, sTempString.c_str());
            break;
        case 3:
        szImgPath = new char[sTempString.length() + 1];
        strcpy(szImgPath, sTempString.c_str());
            break;
        case 4:
        szImgDest = new char[sTempString.length() + 1];
        strcpy(szImgDest, sTempString.c_str());
            break;
        }
        i++;
    }
    conf.close();
    if(i < 4){
        cout << \"fejl: manglende element i konfigurationsfilen.\\n\\n\";
        return false;
    }
    return true;
}

int main(){
    loadconf(\"c:\\\\config.txt\");
    return 0;
}



Generelt ville jeg nu nok holde mig til den version der kun bruger strings, det er noget nemmere, for så får du en hel masse string relaterede funktioner foræret, og du skal ikke selv huske at deallokere pladsen, for det klarer en string selv :)
Avatar billede mbulow Nybegynder
01. januar 2002 - 15:36 #27
Undskyld den rodede kode, formateringen gik vist lidt i vasken :/ Hehe
Avatar billede emileej Nybegynder
10. februar 2002 - 14:01 #28
Hmmm... Nu virker det, så jeg tror ikk jeg vil kræve mere *gG*

E-)mil
http://privat.eej.dk
http://eej.dk
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester