C + + GUI programmering med Qt4: Input / Output.

C + + GUI programmering med Qt4: Input / Output.

Dette kapitlet er fra boken.

Dette kapitlet er fra boken.

Dette kapitlet er fra boken & # xF501;

12. Input / Output.

Lese og skrive bin r data lesing og skriving tekst gjennomforing av kataloger embedding ressurser inter-prosess kommunikasjon

Behovet for a lese fra eller skrive til filer eller andre enheter er vanlig for nesten alle applikasjoner. Qt gir utmerket stotte til I / O gjennom QIODevice, en kraftig abstraksjon som inkapsler «enheter» som er i stand til a lese og skrive blokker av byte. Qt inkluderer folgende QIODevice-underklasser:

Tilgang til filer i det lokale filsystemet og i innebygde ressurser.

Oppretter og apner midlertidige filer i det lokale filsystemet.

Leser data fra eller skriver data til en QByteArray.

Kjorer eksterne programmer og handterer interprosess kommunikasjon.

Overforer en strom av data over nettverket ved hjelp av TCP.

Sender eller mottar UDP datagrammer over nettverket.

Overforer en kryptert datastrom over nettverket ved hjelp av SSL / TLS.

QProcess, QTcpSocket, QUdpSocket og QSslSocket er sekvensielle enheter, noe som betyr at dataene kun kan nas en gang, startende fra den forste byten og forloper serielt til siste byte. QFile, QTemporaryFile og QBuffer er random access-enheter, slik at byte kan leses et hvilket som helst antall ganger fra hvilken som helst posisjon; De gir QIODevice :: seek () -funksjonen for a omplassere filpekeren.

I tillegg til enhetsklassene gir Qt ogsa to hoyere nivastromklasser som vi kan bruke til a lese fra og skrive til en hvilken som helst I / O-enhet: QDataStream for bin re data og QTextStream for tekst. Disse klassene tar seg av problemer som bytebestilling og tekstkodinger, slik at Qt-applikasjoner som kjorer pa forskjellige plattformer eller i forskjellige land kan lese og skrive hverandres filer. Dette gjor Qts I / O-klasser mye mer praktisk enn tilsvarende Standard C ++-klasser, som lar disse problemene ga til applikasjonsprogrammereren.

QFile gjor det enkelt a fa tilgang til individuelle filer, enten de er i filsystemet eller innebygd i programmets kjorbare som ressurser. For applikasjoner som ma identifisere hele sett med filer som skal brukes, gir Qt QDir og QFileInfo-klassene, som handterer kataloger og gir informasjon om filene i dem.

QProcess-klassen lar oss lansere eksterne programmer og kommunisere med dem gjennom standardinngang, standardutgang og standard feilkanaler (cin, cout og cerr). Vi kan angi miljovariabler og arbeidskatalog som den eksterne applikasjonen skal bruke. Som standard er kommunikasjon med prosessen asynkron (ikke-blokkering), men det er ogsa mulig a blokkere pa visse operasjoner.

Nettverk og lesing og skriving av XML er slike betydelige emner som vi dekker separat i egne dedikert kapitler (kapittel 15 og kapittel 16).

Lese og skrive bin re data.

Den enkleste maten a laste inn og lagre bin re data med Qt, er a instansere en QFile, for a apne filen, og for a fa tilgang til den gjennom et QDataStream-objekt. QDataStream gir et plattformuavhengig lagringsformat som stotter grunnleggende C ++-typer som int og dobbelt og mange Qt datatyper, inkludert QByteArray, QFont, QImage, QPixmap, QString og QVariant, samt Qt-containerklasser som QList & lt; T & gt; ; og QMap & lt; K, T & gt; .

Slik lagrer vi et heltall, en QImage og en QMap & lt; QString, QColor & gt; i en fil som heter facts.dat:

Hvis vi ikke kan apne filen, informerer vi brukeren og returnerer. Makroen qPrintable () returnerer en const char * for en QString. (En annen tiln rming ville ha v rt a bruke QString :: toStdString (), som returnerer en std :: streng, for hvilken & lt; iostream & gt; har en & lt; & lt; overload.)

Hvis filen apnes, oppretter vi en QDataStream og angir versjonens nummer. Versjonsnummeret er et heltall som pavirker maten Qt datatyper er representert (grunnleggende C + + datatyper er alltid representert pa samme mate). I Qt 4.3 er det mest omfattende formatet versjon 9. Vi kan enten hardkode konstanten 9 eller bruke QDataStream :: Qt_4_3 symbolsk navn.

For a sikre at tallet 0x12345678 er skrevet som et usignert 32-biters heltall pa alle plattformer, kaster vi det til quint32, en datatype som garanteres a v re noyaktig 32 bits. For a sikre interoperabilitet standardiserer QDataStream standard pa big endian; Dette kan endres ved a ringe setByteOrder ().

Vi trenger ikke a eksplisitt lukke filen, siden dette gjores automatisk nar QFile-variabelen ikke er i bruk. Hvis vi vil verifisere at dataene faktisk er skrevet, kan vi ringe flush () og sjekke returverdi (sant pa suksess).

Koden for a lese tilbake dataene spegler koden vi pleide a skrive den:

QDataStream-versjonen vi bruker til lesing er den samme som den vi brukte til skriving. Dette ma alltid v re tilfelle. Ved a hardkodende versionsnummeret garanterer vi at applikasjonen alltid kan lese og skrive dataene (forutsatt at den er kompilert med Qt 4.3 eller senere Qt-versjon).

QDataStream lagrer data pa en slik mate at vi kan lese den somlost. For eksempel er en QByteArray representert som et 32-bit byte telling etterfulgt av bytes selv. QDataStream kan ogsa brukes til a lese og skrive rabyte uten noen byte-tellerhoved, ved a bruke readRawBytes () og writeRawBytes ().

Feilhandtering nar du leser fra en QDataStream er ganske enkelt. Strommen har en status () -verdi som kan v re QDataStream :: Ok, QDataStream :: ReadPastEnd eller QDataStream :: ReadCorruptData. Nar en feil har oppstatt, vil & gt; & gt; Operatoren leser alltid null eller tomme verdier. Dette betyr at vi ofte ofte kan lese en hel fil uten a bekymre deg om mulige feil og kontrollere statusen () pa slutten for a se om det vi leser var gyldig.

QDataStream handterer en rekke C ++ og Qt datatyper; Den komplette listen er tilgjengelig pa http://doc.trolltech.com/4.3/datastreamformat.html. Vi kan ogsa legge til stotte for vare egne tilpassede typer ved a overbelaste & lt; & lt; og & gt; & gt; operatorer. Her er definisjonen av en egendefinert datatype som kan brukes med QDataStream:

Slik kan vi implementere & lt; & lt; operator:

For a sende ut en maleri, sender vi bare to QString s og en quint32. Pa slutten av funksjonen returnerer vi strommen. Dette er et vanlig C ++-idiom som lar oss bruke en kjede av & lt; & lt; operatorer med en utgangsstrom. For eksempel:

Implementeringen av operator & gt; & gt; () ligner pa operator & lt; ():

Det er flere fordeler a gi streaming operatorer for egendefinerte datatyper. En av dem er at det tillater oss a streame containere som bruker den egendefinerte typen. For eksempel:

Vi kan like lett lese i containere:

Dette ville resultere i en kompilatorfeil hvis maleri ikke stottet & lt; & lt; eller & gt; & gt; . En annen fordel ved a tilby streaming operatorer for egendefinerte typer er at vi kan lagre verdier av disse typene som QVariant s, noe som gjor dem mer brukbare, for eksempel av QSettings. Dette virker forutsatt at vi registrerer typen ved hjelp av qRegisterMetaTypeStreamOperators & lt; T & gt; () pa forhand, som forklart i kapittel 11 (s. 292).

Nar vi bruker QDataStream, tar Qt seg av a lese og skrive hver type, inkludert containere med et vilkarlig antall elementer. Dette lindrer oss fra behovet for a strukturere hva vi skriver og fra a utfore noen form for analyse av hva vi leser. Var eneste forpliktelse er a sikre at vi leser alle typer i noyaktig samme rekkefolge som vi skrev dem, slik at Qt kunne handtere alle detaljer.

QDataStream er nyttig bade for vare egne tilpassede filformater og for standard bin re formater. Vi kan lese og skrive standard bin re formater ved hjelp av streaming operatorene pa grunnleggende typer (for eksempel quint16 eller float) eller ved hjelp av readRawBytes () og writeRawBytes (). Hvis QDataStream brukes bare til a lese og skrive grunnleggende C ++ datatyper, trenger vi ikke engang a ringe setVersion ().

Sa langt har vi lastet inn og lagret data med streamens versjon hardkodet som QDataStream :: Qt_4_3. Denne tiln rmingen er enkel og trygg, men den har en liten ulempe: Vi kan ikke dra nytte av nye eller oppdaterte formater. For eksempel, hvis en senere versjon av Qt la til et nytt attributt til QFont (i tillegg til punktstorrelsen, familien, etc.) og vi kodet versjonsnummeret til Qt_4_3, ville det attributtet ikke bli lagret eller lastet. Det er to losninger. Den forste tiln rmingen er a legge inn QDataStream-versjonsnummeret i filen:

(MagicNumber er en konstant som unikt identifiserer filtypen.) Denne tiln rmingen sikrer at vi alltid skriver dataene ved hjelp av den nyeste versjonen av QDataStream, uansett hva som skjer. Nar vi kommer til a lese filen, leser vi stream-versjonen:

Vi kan lese dataene sa lenge stromversjonen er mindre enn eller lik den versjonen som brukes av programmet; Ellers rapporterer vi en feil.

Hvis filformatet inneholder et eget versjonsnummer, kan vi bruke det til a utlede stromversjonsnummeret i stedet for a lagre det eksplisitt. For eksempel, anta at filformatet er for versjon 1.3 av var soknad. Vi kan da skrive dataene som folger:

Nar vi leser det tilbake, bestemmer vi hvilken QDataStream-versjon som skal brukes basert pa programmets versionsnummer:

I dette eksemplet angir vi at enhver fil som er lagret med versjoner for 1.3 av applikasjonen, bruker datastrom versjon 4 (Qt_3_0), og at filene lagret med versjon 1.3 av applikasjonen, bruker datastrom versjon 9 (Qt_4_3).

I sammendraget er det tre retningslinjer for handtering av QDataStream-versjoner: hardkoding av versjonsnummeret, eksplisitt skriving og lesing av versionsnummer, og bruk av forskjellige hardkodede versjonsnumre avhengig av programmets versjon. Enhver av disse retningslinjene kan brukes til a sikre at data skrevet av en gammel versjon av et program kan leses av en ny versjon, selv om den nye versjonen kobler seg til en nyere versjon av Qt. Nar vi har valgt en policy for a handtere QDataStream-versjoner, er lesing og skriving av bin re data ved hjelp av Qt bade enkel og palitelig.

Hvis vi vil lese eller skrive en fil pa en gang, kan vi unnga a bruke QDataStream og i stedet bruke QIODevice’s write () og readAll () funksjoner. For eksempel:

I linjen der readAll () kalles, blir hele innholdet i inngangsfilen lest inn i en QByteArray, som deretter sendes til write () -funksjonen som skal skrives til utdatafilen. A ha alle dataene i en QByteArray krever mer minne enn a lese elementet etter gjenstand, men det gir noen fordeler. For eksempel kan vi deretter bruke qCompress () og qUncompress () for a komprimere og komprimere dataene. Et mindre minne-sultent alternativ til a bruke qCompress () og qUncompress () er QtIOCompressor fra Qt Solutions. En QtIOCompressor komprimerer strommen den skriver og dekomprimerer strommen den leser, uten a lagre hele filen i minnet.

Det er andre scenarier hvor tilgang til QIODevice direkte er mer hensiktsmessig enn a bruke QDataStream. QIODevice gir en titt () -funksjon som returnerer de neste data bytes uten a flytte enhetens posisjon, sa vel som en ungetChar () -funksjon som «unreads» en byte. Dette fungerer bade for tilfeldig tilgangsutstyr (for eksempel filer) og for sekvensielle enheter (for eksempel nettverksstikk). Det er ogsa en seek () -funksjon som setter enhetens posisjon, for enheter som stotter tilfeldig tilgang.

Bin re filformater gir den mest allsidige og mest kompakte maten a lagre data pa, og QDataStream gjor det enkelt a fa tilgang til bin re data. I tillegg til eksemplene i denne delen, sa vi allerede bruk av QDataStream i kapittel 4 for a lese og skrive regnearkfiler, og vi vil se det igjen i kapittel 21, der vi bruker det til a lese og skrive Windows-markorfiler.