Symbian OS上的檔案操作和UNICODE轉換

gudesheng發表於2008-01-03
         本文整理總結了一下在Symbian系統上進行檔案操作的一些體會,實現了通過檔案儲存、讀取TBuf和TInt變數的功能,其中涉及到UNICODE和ANSI之間字元的轉換。

       最近研究了一下在Symbian作業系統上的檔案操作,做一下整理,以做回顧交流。
       實現的功能如下:將一個class裡面的成員變數TBuf16  m_iSevrName 和 TInt  m_iPort,通過一個檔案Server.txt儲存和讀取操作。因為Symbian中的TBuf16必須讀取unicode字元,故有兩種方法實現,一是將檔案儲存為UNICODE檔案然後讀取,另外一種就是讀取
ANSI檔案到buffer,由程式實現將buffer轉為UNICODE字元。
1.
寫檔案儲存變數
       TBuf(TBufC)是Symbian系統中表示字元的一種方式,緩衝描述符,包含資料本身和資料的長度,注意此長度是unicode字元的長度,即是以short為單位的。可以通過TBuf的Ptr()成員函式得到該變數字元的地址指標,然後再逐一操作。
FILE* fp=fopen("c://Server.txt","w");
if(fp)
{
     char chBufServ[20];   
     char chBufPort[8];             
     m_iSevrName.Trim();
     int i = 0;
     for(i=0; i         chBufServ[i] = *(m_iSevrName.Ptr()+i);
     chBufServ[i] = '/r';
     chBufServ[i+1] = '/n';
     sprintf(chBufPort,"%d/r/n",m_iPort);
     fwrite(chBufServ,strlen(chBufServ),1,fp);
     fwrite(chBufPort,strlen(chBufPort),1,fp);
     fclose(fp);
}
2.從檔案讀取字元並給變數賦值
      檔案的儲存很簡單,主要是在檔案讀取賦值時把我搞的昏頭轉向^_^。
      先說一下怎麼區分unicode檔案和ANSI檔案:所有Unicode編碼的文字檔案,其檔案頭2個位元組一定是"0xff","0xfe",只要先讀出這兩個位元組,就可以判斷是不是Unicode編碼的檔案了
char cFirst[2];
fread(iFileHandle, cFirst, 2);  

if( psFirst[0] == '/xff' && psFirst[1] == '/xfe' )用這條語句就可以判斷了。
首先以第一種方式實現,直接讀取UNICODE檔案賦值:
1).
FILE* fp=fopen("c://Server.txt","rb");    //Server.txt是UNICODE格式檔案
if(fp)  {
     fseek(fp,0,2);//2即SEEK_SET表示Beginning of file
     int nLen=ftell(fp);                                                                                                                                    
if(nLen<1)
{
     fclose(fp);
     return;
}
fseek(fp,2,0); //注意此處跳過2位元組,即表示UNICODE檔案的符號"0xff","0xfe"
nLen-=2;
wchar_t *wstr=new wchar_t[nLen+1]; 
fread(wstr, nLen, 1, fp);
wstr[nLen]=0;              //加入結束符
fclose(fp);
short * psip=(short*)wstr;
short* token = GetLine(psip);
int i = 0;
while( token != NULL )
{
switch(i)
    {
    case 0:

    {
                m_iSevrName.Append((TUint16*)token,wcslen((wchar_t*)token));
    }
    break;
    case 1:
    {
         int nLen = wcslen((wchar_t*)token);
         char pTmpBuf[5];
         int i = 0;
         for(i=0; i             pTmpBuf[i] = *(token+i);                
         pTmpBuf[i] = 0;                   //加入結束符
         m_iPort = atoi(pTmpBuf);
    }
    break;      
    }
    /* Get next token: */
    token = GetLine(psip);
    i++;
}
}
注意case 0的情況,如果token是short* 型別,則m_iSevrName呼叫Append後,其length()返回的是UNICODE的字元個數,而如果是wchar_t*型別的wstr ,則iSevrName的length()函式返回的是ANSi字元的個數,必須呼叫用SetLength()設定,即:m_iSevrName.Append((TUint16*) wstr,wcslen(wstr));
m_iSevrName.SetLength(m_iSevrName.Length()/2);
(在這個問題上浪費了我不少時間)。
其中上面讀取一行字元的函式GetLine實現如下:
short* GetLine(LPSHORT& pBuf)
{
short* pRet=pBuf;
while(*pBuf && *pBuf!='/r' && *pBuf!='/n') pBuf++;
    *pBuf=0;
     pBuf++;
while(*pBuf &&(*pBuf=='/r' || *pBuf=='/n')) pBuf++;
    if(pRet==pBuf||*pRet==0)
     return NULL;
     return pRet;
}
2)以上是直接讀取UNICODE檔案實現,下面說一下第二種方法,即讀取ANSI檔案到快取,然後轉換為UNICODE字元。Windows系統中比較簡單,呼叫其API函式WideCharToMultiByte、MultiByteToWideChar可以在兩種字元間互相轉換,而在Symbian系統中就沒有那麼簡單了:
FILE* fp=fopen("c://Server.txt","rb");
if(fp)  {
fseek(fp,0,2);
     int nLen=ftell(fp);                                                                                                                                   
if(nLen<1)
     {
     fclose(fp);
     return;
     }
unsigned char* pBuf1=new unsigned char[nLen+1];
fseek(fp,0,0);
fread(pBuf1,nLen,1,fp);
pBuf1[nLen]=0;                           //結束符
fclose(fp);
fp=NULL;
//convert to unicode
TPtrC8 aForeign;
aForeign.Set(pBuf1,nLen);
CCnvCharacterSetConverter* pcc= CCnvCharacterSetConverter::NewLC();
if (pcc->PrepareToConvertToOrFromL(KCharacterSetIdentifierGbk, iEikonEnv->FsSession()) != CCnvCharacterSetConverter::EAvailable)
     {
     CleanupStack::PopAndDestroy();
     delete pBuf1;
     return;
     }
HBufC* iInfoText = HBufC::NewL(aForeign.Length());
TPtr16 ptr = iInfoText->Des();
     TInt aState=CCnvCharacterSetConverter::KStateDefault;
pcc->ConvertToUnicode(ptr, aForeign, aState);
CleanupStack::PopAndDestroy();
nLen*=2;
short* pBuf=(short*)ptr.Ptr();
     short * psip=pBuf;
short* token = GetLine(psip);
     int i = 0;
while( token != NULL )
{ ......                    //下面實現同第一種方法


      注意此轉換方法在Symbian的模擬器上不能執行和除錯,只能在real device上執行,不能除錯對我們程式設計師來說比較難受,如果你選擇了這種方法,那麼就祈禱上帝讓它能夠正確執行吧^_^。

TBuf m_iSevrName;

void SetDefaultChnl(const TDesC& aServName, TInt nRoom)
{

  //   m_iServName = aServName;                                        //這種寫法是錯誤的
       m_iSevrName.Delete(0,m_iSevrName.Length());    //必須先清除原來的內容
       m_iSevrName.Append(aServName);
       m_iRoom = nRoom;
}

利用TDesC為TBuf變數賦值時,必須首先呼叫TBuf的Delete()函式清空內容,否則如果用第一種方法,該TBuf變數不會正確賦值,儘管除錯時跟蹤TBuf變數的Length和iBuf指標內容都顯示是正確的。

1. 簡介

  當我剛開始學習Symbian時,我遇到的第一件事情就是Symbian OS字串的處理和使用問題。要學習Symbian字串的使用,必須費一定腦筋才行。但是一旦你掌握了其中的要領,它就變得容易多了。

  因此,下面我將解釋我是怎樣學習基本的Symbian OS字串處理並對之加以記憶的。

  注意,理解本文的前提是對Symbian作業系統的工作機理有一定了解。

  2. 背景

  你要做的第一件事情是記住字串描述符層次結構圖。這是很重要的,因為以後所有你要使用的五個描述符都派生於某些類,你必須瞭解它們分別是從哪些類派生的,以便確定應該使用哪些特別描述符及其使用場所。本文中我不準備解釋什麼是緩衝描述符及堆描述符的含義,以及可修改的描述符和不可修改的描述符是什麼含義。但是,我相信你必須對上面的術語有足夠的瞭解才行。Symbian描述符層次結構看起來相當絕妙。你可以參考下圖,該圖來源於newlc。

  3. TPtrC用法

  其字面含義是"一個指向不可被操作的資料的指標"。關於TPtrC,首先要記住,它不包含對自己的一些操作函式,而只含有構造器和設定方法。另外,既然它派生於TDesC,它就包含了TDesC的所有功能。

  指標指向資料的方式有下面兩種:

  ·先建立一個空的TPtrC,然後用Set(...)函式把它指向一些資料。

  ·通過使用任何一個過載的建構函式,在構造過程中傳遞資料。

  讓我們通過下面幾個例子來看一下上面的描述:

  例項1:-從TBuf和TbufC中取得TPtrC:

LIT(KText , "Test Code");
TBufC<10> Buf ( KText ); OR(/) TBuf<10> Buf ( KText );
// 使用構造器建立TPtr
TPtrC Ptr (Buf);
//使用成員函式建立TPtr
TPtrC Ptr1;
Ptr1.Set(Buf);

  例項2:-從TText*中取得TPtrC:

  下面的例項使用了TText16:

TText * text = _S("Hello World/n");
TPtrC ptr(text);
// 或
TPtrC Ptr1;
Ptr1.Set(text);
//要只儲存Ttext的一部分,我們可以使用下面的語句
//這個描述符指標將只儲存Hello
TPtrC ptr4(text,5);

  例項3:-從另外一個TPtrC取得TPtrC:

  你可以容易地把一個TPtrC賦值給另一個TPtrC。

TText * text = _S("Hello World/n");
TPtrC ptr(text);
//從另外一個TPtrC取得TPtrC
TPtrC p1(ptr);
//或
TPtrC p2;
p2.Set(ptr);

  例項4:-從TPtrC中取得TText *:

  我們可以通過Ptr()成員使用來從TPtrC中取得TText *。

//設定TPtrC
_LIT(KText,"Test Code");
TBufC<10> Buf ( KText );
TPtrC Ptr1 (Buf);
//取得TText*
TText* Text1 = (TText *)Ptr1.Ptr();

4. TBufC的用法

  上面關於TPtrC的使用舉例,有助於理解TBufC的用法,不過下面有幾個例子是關於如何建立TBufC。

  例項5:-例項化TBufC:

//用文字例項化
_LIT(Ktext, "TestText");
TBufC<10> Buf (Ktext);
//或
TBufC<10> Buf2;
Buf2 = Ktext;
//用現有的TBufC來建立一個新的TBufC
TBufC<10> Buf3(Buf2);

  TBufC一般用於文字資料。對於二進位制資料,應顯示地使用TBufC8。儘管TBufC意味著資料不能被修改(’C’代表Constant:不變的),但是還有兩種方法可以改變資料:

  ·資料可以使用賦值運算子進行替換。

  ·通過使用Des()函式來為緩衝區資料構建一個TPtr可修改的指標描述符。

  讓我們看改變TBufC的上下文的第一步。

  例項6:-改變TBufC的上下文:

//測試用的一些文字
_LIT(Ktext , "Test Text");
_LIT(Ktext1 , "Test1Text");
//生成TPtrC
TBufC<10> Buf1 ( Ktext );
TBufC<10> Buf2 ( Ktext1 );
//改變Buf2的上下文
Buf2 = Buf1;
//建立一個空的TbufC並把它賦給Buf1
TBufC<10> Buf3;
Buf3 = Buf1;

  另一種改變TBufC的上下文內容的方式是使用Des()成員函式。這個成員函式使用TPtr成員返回一個TPtr可修改的指標描述符。TPtr的最大長度是TBufC模板引數的值。所有的操作函式都來源於基類TdesC。

  例項7:-使用Des()來改變TBufC的上下文:

_LIT(Ktext , "Test Text");
_LIT(KXtraText , "New:");
TBufC<10> Buf1 ( Ktext );
TPtr Pointer = Buf1.Des();
// 刪除最後4個字元
Pointer.Delete(Pointer.Length()-4, 4 );
//現在作相應的長度改變
TInt Len = Pointer.Length();
// 新增一個新串
Pointer.Append(KXtraText);
Len = Pointer.Length();
//要完全改變上面的緩衝區,我們可以使用下面的語句:
_LIT(NewText , "New1");
_LIT(NewText1 , "New2");
TBufC<10> Buf2(NewText);
//改變上下文
Pointer.Copy(Buf2);
//或者直接進行字面複製
Pointer.Copy(NewText1);
//所有上面所做的變化實際上改變了Buf1的上下文

  5. 使用堆描述符HBufC

  當我們不知道描述符中資料的大小時,HBufC是可選用的描述符。這裡的’C’代表不變的(constant),這意味著資料是不可改變的,但是它在兩種情況下也可以改變,就象對於TBufC的情況所做的改變一樣。第一種情況是使用賦值運算子,第二種情況是使用可修改的指標描述符,如使用成員函式時的TPtr。在使用HbufC時,要記住兩種情況:

  ·如果你需要把HbufC傳遞給一個用TDesC&作為引數的函式,你只須簡單地取消對HBufC指標的參照即可。

  ·可以通過使用ReAlloc函式來改變堆描述符緩衝區的大小,就象對於TBufC的情況一樣.

  例項8:-HbufC的用法:

//建立一個堆描述符,有兩種方法
//第一種方法使用New(),NewL()或NewLC()之一
//讓我們看一個例子.這裡將構建一個HbufC:所用資料空間為15,但是當前大小是0
HBufC * Buf = HBufC::NewL(15);
// 第二種方法使用
// 現有描述符的Alloc(), AllocL(), 或AllocLC()方法。這個新的堆描述符用描述符的內容自動初始化
_LIT (KText , "Test Text");
TBufC<10> CBuf = KText;
HBufC * Buf1 = CBuf.AllocL();
// 我們檢查一下大小和長度。這裡大小是18,而長度是9
TInt BufSize = Buf->Size();
TInt BufLength = Buf->Length();
// 改變HbufC的指向
_LIT ( KText1 , "Text1");
//使用賦值運算子來改變指向KText1的緩衝區
*Buf1 = KText1;
// 下面通過可修改的指標描述符來改變 資料
TPtr Pointer = Buf1->Des();
Pointer.Delete( Pointer.Length() - 2, 2 );
// 所有能對 TBufC 進行的操作在些都可用
//下面是一個這樣的操作
_LIT ( KNew, "New:");
Pointer.Append( KNew );

  6. TPtr的用法

  我們使用它來取代TBufC和HBufC,因為我最瞭解它。因此,讓我們先記住如何建立TPtr。

  ·用另一個TPtr。

  ·從TBufC,或者通過成員函式Des()使用HbufC來建立。

  ·從一個指向記憶體的外部指標並指定最大長度。

  ·從一個指向記憶體的外部指標並指定資料及其最大長度。

  例項9:-TPtr的用法:

//先讓我們看一下兩種取得TPtr的方法
_LIT(KText, "Test Data");
TBufC<10> NBuf ( KText );
TPtr Pointer = NBuf.Des();
//第一種方法
TPtr Pointer2 ( Pointer );
//第二種方法使用一個記憶體區段,用於儲存資料和最大長度
TText * Text = _S("Test Second");
TPtr Pointer3 ( Text ,11, 12);
// 現在我們看一下,怎麼用TPtr 替換資料,這完全可以通過
//賦值運算子或拷貝函式來實現
_LIT(K1, "Text1");
_LIT(K2, "Text2");
Pointer2 = K1; // 資料是Text1
Pointer.Copy(K2); // 資料是Text2;
// 我們還可以改變資料的長度或把它設定為0
Pointer2.SetLength(2); // 只剩下 Te兩個字元
Pointer2.Zero(); // 把長度設定為0
//可以使用delete 函式來更改資料,如前面的例中所示

  7. TBuf的用法

  在這個資料結構中,其中的資料並不是固定不變的。運算、例項化和賦值都與TBufC一致;對於它的修改操作,與TPtr一致,象複製、刪除、賦值等等。我想這一部分就不用再給出例子了。


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=819709


相關文章