quick_recipes (779892), страница 17
Текст из файла (страница 17)
For example:"myfile.dat" – "!:\private\A1234567\myfile.dat"This means that the file will be installed to the drive selected by theuser. It can be C: if the user selects the phone memory. It can be D: orE: if the user selects the memory card or hard drive.That is why it is not recommended to use a hard-coded drive letterin the source code.How do we add a drive letter that is the same as the application’s driveletter? Firstly, we need to get the full path of the application and extractthe drive letter from it:// Get the private path of the application.RBuf privatePath;privatePath.CreateL(KMaxFileName);CleanupClosePushL(privatePath);User::LeaveIfError(fileServer.PrivatePath(privatePath));FILE HANDLING89// Get the application’s full name.TParsePtrC parseAppPath(Application()->AppFullName());_LIT(KNameAndExt, "hello.txt");TParse parse;User::LeaveIfError(parse.Set(parseAppPath.Drive(), &privatePath,NameAndExt));Suppose that the application is installed on the E: drive and theSID of the application is 0xA1234567; parse will have the value of‘e:\private\A1234567\example.txt’.The first two lines get the private path of the application.
There isnothing special here. They are the same as the previous examples.The next line gets the full application name using CEikApplication::AppFullName(). The return value of AppFullName() is inthe form of ‘c:\sys\bin\myapp.exe’.Note that this example uses CEikAppUi::Application() to geta pointer to the instance of CEikApplication. It also means that youcan only write the code above in one of the AppUi methods.
If you wantto write it in another class, you need to find a way to get the instance ofAppUi. For example:CEikAppUi* appUi = static_cast<CEikAppUi*>(CCoeEnv::Static()->AppUi());TParsePtrC parseAppPath(appUi->Application()->AppFullName());It is also possible to get the application filename using the RProcess::FileName() method.
The result is the same as CEikApplication::AppFullName().Tip: Any classes derived from TParseBase can be used to extractfilename components, such as drive letter, path, name and extension.There are three classes that are commonly used to do extraction; thatis, TParse, TParsePtr and TParsePtrC.The TParse class stores the full filename in the type of TFileName.It means this class requires 512 bytes in the stack memory to store thefilename.
The TParsePtr and TParsePtrC classes use less spacein the stack memory because they store a reference to the filename.We recommend using TParsePtr or TParsePtrC if you only wantto parse a filename. Use TParse only when you need to construct afilename from its components.The following code shows how to use TParsePtrC:LIT(KFileName, "c:\\folder\\subfolder\\myfile.dat");TParsePtrC parse(KFileName);90SYMBIAN C++ RECIPESTPtrC drive(parse.Drive());// "c:"TPtrC drivePath(parse.DriveAndPath()); // "c:\folder\subfolder\"TPtrC name(parse.Name());4.1.2.2// "myfile"TPtrC nameExt(parse.NameAndExt());// "myfile.dat"TPtrC ext(parse.Ext());// ".dat"TPtrC path(parse.Path());// "\folder\subfolder\"Read from and Write to a File StreamAmount of time required: 30 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 variables such as integers or descriptorsfrom/to a file using a file stream rather than the RFile API.Solution: RFile provides some methods to read and write binary data.You can use it to write a variable, such as an integer or a floatingpoint number, but you need to convert them to descriptors.
There is aneasier way to write variables by using a stream. RFileReadStreamand RFileWriteStream are the two classes for reading and writing the stream, respectively (they were discussed briefly in Chapter 3,Section 3.14).Writing to a File StreamThe following example shows how to write an integer to a file:void CFileStreamAppUi::WriteInt32ToStreamL(RFs& aFs,const TDesC& aFileName, TInt32 aValue){// Open the file stream.RFileWriteStream writeStream;User::LeaveIfError(writeStream.Replace(aFs, aFileName, EFileWrite));writeStream.PushL();// Write a TInt32 to the stream.writeStream.WriteInt32L(aValue);// Commit the change and close the stream.writeStream.CommitL();CleanupStack::PopAndDestroy(&writeStream);}FILE HANDLING91The first three lines create a new instance of a stream and then push itto the cleanup stack.RFileWriteStream writeStream;User::LeaveIfError(writeStream.Replace(fileServer, aFileName,EFileWrite));writeStream.PushL();If you don’t want to overwrite an existing file, use RFileWriteStream::Create().
It will return an error code when the file alreadyexists.Tip: You can use BaflUtils::EnsurePathExistsL() to createone or more folders if they do not already exist, for example:BaflUtils::EnsurePathExistsL(iCoeEnv->FsSession(), KFileName);The method is declared in the bautils.h header file and thecorresponding library is bafl.lib.The sample code above uses RWriteStream::PushL() to pushitself on to the cleanup stack.
An alternative way to push the stream tothe cleanup stack is by calling CleanupClosePushL(). For example:CleanupClosePushL(writeStream);The next line of WriteInt32ToStreamL() writes an integer value,aValue, to the file:writeStream.WriteInt32L(aValue);The writing statement above can be replaced by the << operator:writeStream << aValue;Note that the << operator can also leave. It means you have to pushany objects created before this statement on to the cleanup stack.What may go wrong when you do this: The << operator does notsupport the TInt type. If you do something like the following code:TInt x = 10;writeStream << x;92SYMBIAN C++ RECIPESyou will get an error message during compilation saying that there issomething wrong in \epoc32\include\s32strm.inl. If you getan error message from this header file, check the usage of the <<operator.
You may have used it to write unsupported types, such asTInt.Also note that the << operator may leave, and must be treated as aleaving function call.What may go wrong when you do this: If you forget to call RWriteStream::Release(), there will be a memory leak. Always makesure that you release the stream when you are not using it anymore.You can usually detect a memory leak by exiting your application from the emulator. The emulator should panic with an ALLOCmessage.Reading from a File StreamThe following code reads the integer written with the example above:TInt32 CFileStreamAppUi::ReadInt32FromStreamL(RFs& aFs,const TDesC& aFileName){// Open the file stream.RFileReadStream readStream;User::LeaveIfError(readStream.Open(aFs, aFileName, EFileRead));CleanupClosePushL(readStream);// Read a TInt32 from the stream.TInt32 value;readStream >> value;// Close the stream and return the read TInt32.CleanupStack::PopAndDestroy(&readStream);return value;}Reading and writing descriptors can be done in the same way.
Forexample:TBuf<KMaxBuffer> buffer;readStream >> buffer;Note that you don’t need to store the length of the descriptor to thebuffer. The << operator will store the length for you.FILE HANDLING93Appending to a File StreamWhen you open a file using RFileWriteStream::Open(), the filepointer is set to the beginning of the file. It means if you write somethingto the stream, the current data will be overwritten.To avoid this problem, the file pointer has to be moved to the end offile. It can be done by calling the MStreamBuf::SeekL() method.How can we get an instance of MStreamBuf? RFileWriteStreamhas a method called Sink() that returns MStreamBuf*. Similarly,RFileReadStream has a method called Source() that returnsMStreamBuf*.The following code shows how to append a new integer to a filestream:void CFileStreamAppUi::AppendInt32ToStreamL(RFs& aFs,const TDesC& aFileName, TInt32 aValue){// Open the file stream.RFileWriteStream writeStream;TInt err = KErrNone;if (BaflUtils::FileExists(iCoeEnv->FsSession(), aFileName)){err = writeStream.Open(aFs, aFileName, EFileWrite);}else{err = writeStream.Create(aFs, aFileName, EFileWrite);}User::LeaveIfError(err);writeStream.PushL();// Move the file pointer to the end of file.writeStream.Sink()->SeekL(MStreamBuf::EWrite, EStreamEnd, 0);// Write a TInt32 at the end of file.writeStream.WriteInt32L(aValue);// Commit the change and close the file.writeStream.CommitL();CleanupStack::Pop(&writeStream);writeStream.Release();}There are two major differences between this function and the previouswriting function, WriteInt32ToStreamL():• The method that is used to open the file.
It uses RFileWriteStream::Open() if the file already exists; or RFileWriteStream::Create() if the file does not exist yet.94SYMBIAN C++ RECIPES• There is a call to MStreamBuf::SeekL() to move the file pointerto the end of the file.There are several different versions of SeekL(). The one that is usedby the method above is the following:TStreamPos SeekL(TMark aMark, TStreamLocation aLocation, TInt aOffset=0);The aMark parameter indicates the type of mark, whether read orwrite. The file stream maintains independent read and write pointers.They can be manipulated differently.The aLocation parameter is the location in the stream on which thecalculation of the new position is based.
There are three different possiblevalues for aLocation, that is:• EStreamBeginning. The seek position is calculated relative to thebeginning of the stream.• EStreamEnd. The new position is calculated relative to the end ofthe stream. It is the one used in the example above.• EStreamMark.
The new position is calculated relative to the existingmark in the stream.The aOffset parameter is the offset value.Detecting End of FileThe previous reading example reads one integer only. How do we readmore integers until EOF (End of File)? Unfortunately, RFileReadStreamand RFileWriteStream do not provide a method to check EOF. Thereare two ways of reading more integers:• Store the number of integers in the beginning of the file. It will giveinformation on how many times you have to read the stream. You canuse a simple for statement to read the entire file.• Call MStreamBuf::SizeL() to get the file size and then callMStreamBuf::TellL() to check whether the file pointer hasreached EOF or not.The following code shows how to read integers from the stream untilEOF. All the integers are stored in an array.void CFileStreamAppUi::ReadAllTInt32FromStreamL(RFs& aFs,const TDesC& aFileName, RArray<TInt>& aArray){FILE HANDLING95// Open the file stream.RFileReadStream readStream;User::LeaveIfError(readStream.Open(aFs, aFileName, EFileRead));CleanupClosePushL(readStream);// Get the EOF position.TStreamPos eofPos(readStream.Source()->SizeL());// Get the current position of file pointer.TStreamPos currentPos(0);// Read all TInt2’s until EOF.while (currentPos < eofPos){TInt32 value;readStream >> value;aArray.Append(value);currentPos = readStream.Source()->TellL(MStreamBuf::ERead);}CleanupStack::PopAndDestroy(&readStream);}Discussion: The RFileWriteStream provides several variants of thewriting methods for different data types, that is:• Integer: WriteInt8L(), WriteInt16L(), WriteInt32L(),WriteUint8L(), WriteUint16L(), WriteUint32L().• Floating point: WriteReal32L(), WriteReal64L().• Descriptor: WriteL().
There are several variants of WriteL() fordifferent purposes. Please check the SDK documentation for detailedinformation.• Other stream: WriteL().Similarly, the RFileReadStream also provides several variants ofthe reading methods:• Integer: ReadInt8L(), ReadInt16L(), ReadInt32L(), ReadUint8L(), ReadUint16L(), ReadUint32L().• Floating point: ReadReal32L(), ReadReal64L().• Descriptor: ReadL().There are several variants of ReadL() for different purposes. Please check the SDK documentation for detailedinformation.• Other stream: ReadL().It is also possible to read from or write to a file stream using the >>and << operators.