Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Virhe tiedostonkäsittelyssä

Sivun loppuun

ByteMan [28.05.2009 08:18:49]

#

eli siis tein ohjelmanpätkän jonka olisi tarkoitus lukea tietoa paloissa tiedostosta ja kirjoittaa se niissä paloissa toiseen tiedostoon.

This be tiedostonlukutesti jossa säädetään jotain kummallista tiedostonlukemisen kanssa sillee hianosti semmosilla bufferijutuilla.

tuo siis on syöte, joka on kokonaisuudessaan tekstitiedostossa yhdellä rivillä
kumma juttu on tulosteessa:

This be tiedostonlukutesti jossaóUv:lû
 säädetään jotain kummallista tióUv:lû
edostonlukemisen kanssa sillee hóUv:lû
ianosti semmosilla bufferijutuilóUv:lû
la.

vika siis on että 4 ensimmäistä riviä päättyvät ylimääräiseen "óUv:lû"
yritin lyhentää bufferin pituutta ja luettavan alueen pituutta mutta ei ollut vaikutusta muuten kuin että merkkijono lyheni ja tuo pääte vaihtui joksikin muuksi

#include <iostream>
#include <string>
#include <fstream>
#include <vector>

using namespace std;

int main(){
    int pituus;
    char * buffer;
    vector<string> blokit;

    ifstream filu;
    filu.open("filulukujuttu.txt", ios::binary);
    filu.seekg(0, ios::end);
    pituus = filu.tellg();
    filu.seekg(0, ios::beg);
    if( pituus<5 ) {return 0;}

    int apu = (pituus/4);
    buffer = new char[apu];

    for(int i = 0; i<4; i++){
        filu.read(buffer, apu);
        blokit.push_back(buffer);
        filu.seekg(apu*(i+1));
    }

    delete [] buffer;//vaihdetaan bufferin pituutta
    int apu2 = (pituus%4);
    buffer = new char[apu2];
    filu.read(buffer, apu2);//ja luetaan viimeinen palanen
    blokit.push_back(buffer);
    filu.close();

    ofstream filu2;//kirjataan tulokset
    filu2.open("filulukujuttutuloste.txt", ios::app);
    for(int j = 0; j <= 5; j++){
        filu2<<blokit[j]<<endl;
    }
    filu2.close();
    delete [] buffer;
    return 0;
}

yritin kommentoida sitä vähän selkeämmäksi..
toinen vika on että se kyllä suorittaa sen "oikein" mutta suoritus päättyy aina ohjelman kaatumiseen.
veikkaan että kyseessä on joku pikkuseikka jota en ole tajunnut/ymmärtänyt tai jotain joten voiko joku kertoa mikä on vikana.

Torgo [28.05.2009 09:15:28]

#

C++ ei ole vahvinta alaani, mutta veikkaisin että kyseessä on c-stringien yleinen ongelma eli null-terminaattorin puuttuminen. Käsittääkseni ifstream::read ei tee lukemalleen syötteelleen mitään muotoilua. Näin ollen kun puskuri täytetään, jää sieltä lopetusmerkki puuttumaan. Stringiksi kirjoitusvaiheessa tuota lopetusmerkkiä etsitään jä nyt se vain sattuu löytymään puskurin jälkeen olevien "óUv:lû "-merkkien perästä.

Kokeilepa kasvattaa puskurin kokoa yhdellä ja lisätä loppuun null.

Teuro [28.05.2009 09:22:09]

#

Hmm tämä tulee nyt ihan hatusta vetämällä, mutta voiko char tyyppiä tallettaa std::string tyyppiseen vectoriin? Vai pitäisikö tuo vectori varata myös char tyyppiseksi. Tuo outo merkki tulee selkeästi juuri rivin lopussa, joten virhe lienee lukemisessa. Muutoinkin miksi luet tiedostoa pätkissä, eikö sitä voi lukeaa kerralla sisään ja tallettaa toiseen tiedostoon sellaisenaan?

Toisessa for silmukassa käyt läpi 6 alkiota, mutta olet lukenut vain 5 miksi näin? Kaatuminen voisi liittyä tuohon, että sohit vektorin ulkopuolelle.

ByteMan [28.05.2009 14:00:15]

#

Oho, hyvä huomio Teuro. Ilmeisesti oli tarkoituksenani laittaa alustus int j = 1; jolloin se <= olisi ollut oikein. Eli huolimattomuusvirhe -.-
seuraavaksi voisin koittaa tuota Torgon ajatusta.

Mutta itse olen ainakin ymmärtänyt std:string:n ja charin toimivan seuraavalla tavalla(ja kyllähän se jotain tulosti noinkin):

char kirjain = "a";
std::string kirjain_str = kirjain;
char toinen_kirjain = kirjain_str.c_str();//tai sitten se oli c_str(kirjain_std); en muista enää kumpi

Datan lukemiseen pätkissä on puolestaan perin mainio selitys: jos haluat lukea ohjelmaan sisälle sanotaanko vaikka bittikartan, niin käytännössä kaikki data on yhdellä rivillä, jolloin sen käsittelystä tulee hankalahkoa.(edit. varsinkin kun ilman mitään säätöä jos koetat lukea bittikartan std::vector<string> niin ohjelma kaatuu, uskohuviksesi olen kokeillut =P)
toiseksi mainittakoot erilaiset salausmenetelmät joissa tarvitaan tietynkokoisia palasia dataa, jolloin ei ole mitään hyötyä lukea 133 bittistä datamäärää kerralla jos tarvitsee vain 64 bittiä.
ja vielä kolmanneksi erilaiset datansiirtosovellukset lähettävät tietoa lohkoissa sen sijaan että laitettaisiin koko datapalikka kerralla.
eli perusteluita kyllä löytyy =P

Teuro [28.05.2009 14:10:59]

#

ByteMan kirjoitti:

Oho, hyvä huomio Teuro. Ilmeisesti oli tarkoituksenani laittaa alustus int j = 1; jolloin se <= olisi ollut oikein. Eli huolimattomuusvirhe -.-
seuraavaksi voisin koittaa tuota Torgon ajatusta.

Tuolla tavalla kaatuu samalla tavalla, koska osoitat edelleen alkioon indeksiltään 5.

ByteMan [28.05.2009 14:20:17]

#

Joo, siksi korjasinkin sen (int j = 0; j<5; j++) ennen kuin ohjelma ehti kaatua ^^
se nullin lisääminen voisi muuten olla ihan jees muta kun en näköjään osaa tehdä sitä oikein.. nyt se lisää sen merkkisekasotkun paikalle "o"

eq [28.05.2009 15:17:10]

#

ByteMan kirjoitti:

Mutta itse olen ainakin ymmärtänyt std:string:n ja charin toimivan seuraavalla tavalla(ja kyllähän se jotain tulosti noinkin):

char kirjain = "a";
std::string kirjain_str = kirjain;
char toinen_kirjain = kirjain_str.c_str();//tai sitten se oli c_str(kirjain_std); en muista enää kumpi

Ei (monellakin tavalla).

char on yhden tavun kokoinen kokonaislukutyyppi. "a" on merkkijonoliteraali, käytettäessä toimii osoittimena (vain-luku-tyyppisen) merkkijonon alkuun (const char *) - sitä ei voi sijoittaa char-muuttujaan, esimerkiksi koska kyseistä muunnosta (const char * -> char) ei ole määritelty.

std::string on C++-merkkijonoluokka, joka helpottaa merkkijonojen kanssa työskentelyä (automaattinen muistinhallinta, etc.) Sen jäsenfunktio c_str() palauttaa osoittimen (vain-luku-tyyppiseen) C-tyyliseen nollalla päätettyyn merkkijonoon (const char *) - jota ei jälleen voi sijoittaa char-muuttujaan.

Esimerkiksi:

const char* merkkijono  = "a";
std::string merkkijono2 = merkkijono;
const char* mjono = mjono.c_str(); // mahdollinen riski: mjono ei ole enää kelvollinen
                                   // jos merkkijono2:a muutetaan

/* char * -tyyppinen merkkijono (esim.) */
char* rw_merkkijono = new char[merkkijono2.size() + 1];
strcpy(rw_merkkijono, mjono.c_str());

Teuro kirjoitti:

Hmm tämä tulee nyt ihan hatusta vetämällä, mutta voiko char tyyppiä tallettaa std::string tyyppiseen vectoriin? Vai pitäisikö tuo vectori varata myös char tyyppiseksi.

Nopealla vilkaisulla en löytänyt yhtään char-tyyppistä muuttujaa aloituspostin koodista - yksi char * siellä tosin oli. Standardia en sanasta sanaan osaa ulkoa, mutta käsittääkseni tapahtuu stringin implisiittinen konstruktio tuolla char *:lla - mihin tahansa merkkiin osoittavasta muuttujasta ei tosin saa olla kyse, vaan kyseessä on oltava C-tyylinen merkkijono, jonka päädystä löytyy nollatavu.

Torgo [28.05.2009 16:13:58]

#

ByteMan kirjoitti:

se nullin lisääminen voisi muuten olla ihan jees muta kun en näköjään osaa tehdä sitä oikein.. nyt se lisää sen merkkisekasotkun paikalle "o"

Esim näin:

int apu = (pituus/4);
buffer = new char[apu+1];
buffer[apu] = 0;

ByteMan [28.05.2009 17:11:23]

#

Kiitän. Lähti toimimaan.
@eq:: lähinnä tarkoitin juuri tuota samaa toiminnallisuutta mitä sinäkin esitit. En tarkoittanut toiminnalla sitä mitä standardissa lukee std:stringin olevan vaan juurikin sitä, että voit tehdä sijoituksen jolla charilla toteutettu merkkijono siirretään/kopioidaan/whatsoever sinne string muuttujaan ihan standardifunktioilla =)


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta