最近研究了一下在Symbian操作系统上的文件操作,做一下整理,以做回顾交流。
实现的功能如下:将一个class里面的成员变量TBuf16<KMaxServerNameLen> 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<m_iSevrName.Length(); 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<wcslen((wchar_t*)token); 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<MaxLen> 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指针内容都显示是正确的。
LIT(KText , "Test Code"); TBufC<10> Buf ( KText ); OR(/) TBuf<10> Buf ( KText ); // 使用构造器创建TPtr TPtrC Ptr (Buf); //使用成员函数创建TPtr TPtrC Ptr1; Ptr1.Set(Buf); |
TText * text = _S("Hello World/n"); TPtrC ptr(text); // 或 TPtrC Ptr1; Ptr1.Set(text); //要只存储Ttext的一部分,我们可以使用下面的语句 //这个描述符指针将只存储Hello TPtrC ptr4(text,5); |
TText * text = _S("Hello World/n"); TPtrC ptr(text); //从另外一个TPtrC取得TPtrC TPtrC p1(ptr); //或 TPtrC p2; p2.Set(ptr); |
//设置TPtrC _LIT(KText,"Test Code"); TBufC<10> Buf ( KText ); TPtrC Ptr1 (Buf); //取得TText* TText* Text1 = (TText *)Ptr1.Ptr(); |
//用文字实例化 _LIT(Ktext, "TestText"); TBufC<10> Buf (Ktext); //或 TBufC<10> Buf2; Buf2 = Ktext; //用现有的TBufC来创建一个新的TBufC TBufC<10> Buf3(Buf2); |
//测试用的一些文字 _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; |
_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的上下文 |
//创建一个堆描述符,有两种方法 //第一种方法使用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<n> 进行的操作在些都可用 //下面是一个这样的操作 _LIT ( KNew, "New:"); Pointer.Append( KNew ); |
//先让我们看一下两种取得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 函数来更改数据,如前面的例中所示 |