quick_recipes (779892), страница 18
Текст из файла (страница 18)
Remember that these operators can also leave.96SYMBIAN C++ RECIPESBesides RFileWriteStream and RFileReadStream, there aresome other classes to read/write different types of streams. For example:RDesWriteStream and RDesReadStream for descriptor streams;RDictionaryWriteStream and RDictionaryReadStream for dictionary streams; and some more. All of them are derived either fromRWriteStream or RReadStream.4.1.2.3Read and Write Class Members from and to a File StreamAmount of time required: 15 minutesLocation of example code: \Files\FileStreamRequired library(s): efsrv.lib, estor.libRequired header file(s): f32file.h, s32file.hRequired platform security capability(s): NoneProblem: You want to read/write member variables of a class from/to afile; for example, you have a class that contains employee information.Solution: This problem can be solved by the serialization features of filestreams.
Suppose that we have a class that contains employee information.For simplicity, let’s assume that there are three member variables only:identifier, name and phone number. The identifier here means a uniquenumber, such as employee number. I am using it only to show you thedifference between integer and descriptor handling.const TInt KMaxEmployeeName= 40;const TInt KMaxEmployeeNumber = 20;...class CEmployee: public CBase{public: // New methodsCEmployee();inline void SetIdentifier(TUint32 aIdentifier){ iIdentifier = aIdentifier; }inline TUint32 Identifier() const{ return iIdentifier; }inline void SetName(const TDesC& aName){ iName.Copy(aName); }inline const TPtrC Name() const{ return iName; }inline void SetPhoneNumber(const TDesC& aPhoneNumber){ iPhoneNumber.Copy(aPhoneNumber); }inline const TPtrC PhoneNumber() const{ return iPhoneNumber; }FILE HANDLING97private:TUint32 iIdentifier;TBuf<KMaxEmployeeName> iName;TBuf<KMaxEmployeeNumber> iPhoneNumber;};Externalizing means writing an instance of this class to a file.
Inpractice, you can write the following code to write to a file:writeStream << *employee;Internalizing is the opposite; that is, reading a file and storing the resultinto this object. For example:readStream >> *employee;In order to support internalize/externalize, add the following twomethods to the CEmployee class:void InternalizeL(RReadStream& aStream);void ExternalizeL(RWriteStream& aStream) const;The following code shows the implementation of InternalizeL():void CEmployee::InternalizeL(RReadStream& aStream){aStream >> iIdentifier;aStream >> iName;aStream >> iPhoneNumber;}You can see here how easy it is to write each member variable to thefile. You can use the operator << to write the integer and descriptor.The following code shows the implementation of ExternalizeL():void CEmployee::ExternalizeL(RWriteStream& aStream) const{aStream << iIdentifier;aStream << iName;aStream << iPhoneNumber;}What may go wrong when you do this: When reading a descriptor,make sure that the descriptor has enough space.
If the maximumlength of the descriptor is not enough, the application will leave withKErrOverflow (-9).98SYMBIAN C++ RECIPESDiscussion: After defining ExternalizeL(), we can write an employeeto the stream easily using the operator <<. For example:void CFileStreamAppUi::WriteEmployeeToStreamL(RFs& aFs,const TDesC& aFileName, const CEmployee& aEmployee){// Open the file stream.RFileWriteStream writeStream;User::LeaveIfError(writeStream.Replace(aFs, aFileName, EFileWrite));CleanupClosePushL(writeStream);// Write employee to the stream.writeStream << aEmployee;// Commit the change and close the file.writeStream.CommitL();CleanupStack::PopAndDestroy(&writeStream);}Similarly, after defining InternalizeL(), we can read an employeefrom the stream using the operator >>.
For example:void CFileStreamAppUi::ReadEmployeeFromStreamL(RFs& aFs,const TDesC& aFileName, CEmployee& aEmployee){// Open the file stream.RFileReadStream readStream;User::LeaveIfError(readStream.Open(aFs, aFileName, EFileRead));CleanupClosePushL(readStream);// Read a employee from the stream.readStream >> aEmployee;// Close the stream.CleanupStack::PopAndDestroy(&readStream);}4.1.3 Advanced Recipes4.1.3.1Read from and Write to a File StoreAmount of time required: 30 minutesLocation of example code: \Files\FileStoreRequired library(s): efsrv.lib, estor.libRequired header file(s): f32file.h, s32file.hRequired platform security capability(s): NoneProblem: You want to read from and write to a file using a file store. Forexample, you want to store multiple streams in a single file.Solution: A store is a collection of streams (see Figure 4.1.1).
It can be inthe form of a file store or in-memory store. Furthermore, a store can alsoFILE HANDLINGRoot ID99STORERoot streamStream IDStream ID…Stream 2Stream 1Figure 4.1.1An Example of a File Storeembed other stores. This recipe discusses file store only. Please check theSDK documentation for more information about in-memory store.There are two types of file store:• Direct file store.• Permanent file store.A direct file store implements a file store that cannot be changed onceit has been committed and closed.
It is normally used by an applicationwhere in-memory data is the primary copy. The class for direct file storeis CDirectFileStore.A permanent file store implements a file store in which the existingstream can be changed. It is normally used by an application where datain the store is the primary copy. The class for permanent file store isCDictionaryFileStore.All the file store classes are declared in the s32file.h header file.The library name is estor.lib.Writing to a File StoreIn order to see the difference between file streams and file stores, I amgoing to use the same data structure as the previous example, CEmployee(see Recipe 4.1.3.1).The following code shows how to write an employee to the directfile store.
Note that this example only has one stream, which is the rootstream.void CFileStoreAppUi::WriteEmployeeToStoreL(RFs& aFs,const TDesC& aFileName, const CEmployee& aEmployee){// Create a new file store.CFileStore* store = CDirectFileStore::ReplaceLC(aFs,aFileName, EFileWrite);100SYMBIAN C++ RECIPESstore->SetTypeL(KDirectFileStoreLayoutUid);// Create a new stream on the store.RStoreWriteStream writeStream;TStreamId rootId = writeStream.CreateLC(*store);// Write Employee to the stream.writeStream << aEmployee;// Commit the changes to the stream and close the stream.writeStream.CommitL();CleanupStack::PopAndDestroy(&writeStream);// Set the root identifier of the store to this stream.store->SetRootL(rootId);// Commit the change to the store and close the store.store->Commit();CleanupStack::PopAndDestroy(store);}Like with file streams, the CFileStore::ReplaceLC() methodoverwrites an eventual existing file.
As an alternative, you can useCreateLC() so that you get an error code when the file already exists.The SetTypeL() method sets the type of this store. There are twopossible types:• KPermanentFileStoreLayoutUid• KDirectFileStoreLayoutUid.Alternatively, you can also call the CDirectFileStore::Layout() method, which returns KDirectFileStoreLayoutUid:store->SetTypeL(store->Layout());Similarly, you can call CPermanentFileStore::Layout() to setthe type of permanent file store.What may go wrong when you do this: You need to call CFileStore::SetTypeL() when creating a file store. You don’t need tocall it when opening an existing store for reading.If you call SetTypeL() when opening a store, you will get anerror code, KErrAccessDenied (-21).
On the other hand, if youforget to call SetTypeL() when creating a store, you will get an errorcode, KErrNotReady (-18) in further operations.As explained above, a file store is a collection of streams. There isa root stream in a file store, which can be thought of as the ‘main’FILE HANDLING101stream. Since our example above has only one stream, this streamwill become the root stream. We will take a look at how to storemultiple streams later. Setting the root stream is done by calling theCPersistentstore::SetRootL() method:store->SetRootL(rootId);Note that rootId is the stream identifier that is returned fromRStoreWriteStream::CreateLC().What may go wrong when you do this: If you forget to commit the stream before committing the stores, you will get a KERNEXEC 3panic.Reading from a File StoreThe following shows how to read an employee from the file store:void CFileStoreAppUi::ReadEmployeeFromStoreL(RFs& aFs,const TDesC& aFileName, CEmployee& aEmployee){// Open the store.CFileStore* store = CDirectFileStore::OpenLC(aFs,aFileName, EFileRead);// Get the root stream.
In this case, we have only one stream.RStoreReadStream readStream;readStream.OpenLC(*store, store->Root());// Read the Employee.readStream >> aEmployee;CleanupStack::PopAndDestroy(2, store); // readStream and store}Discussion: The previous example contains only one stream. Let’s makethe example more interesting by introducing more streams. We willadd a new stream that acts like a header file. It contains the followinginformation:• Signature. A unique signature that identifies this file store. It is alwaysa good idea to make sure that the file we are reading is not somerandom file.• Version.
The version number might be needed in the future when weupdate the application.• Number of employees. The number of employees stored in this file.102SYMBIAN C++ RECIPESSince there are two streams, we need to maintain a dictionary thatstores the identifier of both streams. The dictionary itself is another newstream. So, we will have three streams:• root stream, which is a streams dictionary;• header information stream;• employee information stream.The root identifier points to the stream dictionary, which contains a listof identifiers of two other streams. If needed, you can add more streams.For example, you may want to split the employee stream into severaldifferent streams – one stream for marketing employees, one stream fordevelopment employees and one stream for finance employees.void CFileStoreAppUi::WriteEmployeesWithMultiStreamsL(RFs& aFs,const TDesC& aFileName,const RPointerArray<CEmployee>& aArray){// Create a new file store.CFileStore* store = CDirectFileStore::ReplaceLC(aFs,aFileName, EFileWrite);store->SetTypeL(store->Layout());// Write header information.RStoreWriteStream headerStream;TStreamId headerId = headerStream.CreateLC(*store);headerStream << KSignature;headerStream << KVersion;headerStream << (TUint32) aArray.Count();headerStream.CommitL();CleanupStack::PopAndDestroy(&headerStream);// Create a new employee to the store.RStoreWriteStream employeeStream;TStreamId employeesId = employeeStream.CreateLC(*store);// Write Employees to the stream.for (TInt i = 0; i < aArray.Count(); i++){employeeStream << *aArray[i];}employeeStream.CommitL();CleanupStack::PopAndDestroy(&employeeStream);// Create a stream dictionary.CStreamDictionary* dictionary = CStreamDictionary::NewLC();dictionary->AssignL(KHeaderUid, headerId);dictionary->AssignL(KEmployeesUid, employeesId);// Write stream dictionary in the root stream.RStoreWriteStream dictionaryStream;FILE HANDLING103TStreamId rootId = dictionaryStream.CreateLC(*store);dictionaryStream << *dictionary;dictionaryStream.CommitL();CleanupStack::PopAndDestroy(2, dictionary);// Set the root identifier of the store to this stream.store->SetRootL(rootId);// Commit the change to the store and close the store.store->Commit();CleanupStack::PopAndDestroy(store);}Note that we need to store the stream identifier of the employees inthe root stream.Tip: A permanent file store allows you to modify the data, for exampledeleting a stream by calling CPermanentFileStore::DeleteL().If you do this several times, your files may get fragmented.