当前位置:编程学习 > VC++ >>

用VC++访问文本文件

初一看这个标题,稍微做过C++编程的人都不认为这是什么困难的事情,但细细想起来还是有些需要注意的。其中我想最主要的就是文本文件的格式了,我们目前常见的有下面这四种,也就是Windows的记事本所能保存的那四种格式了:

\

1,ANSI,也是我们最最常见的文本文件格式,在中文操作系统中,使用默认的GBK编码,而港台用的繁体中文操作系统则默认使用Big5码,简体中文操作系统打开Big5码的文本文件,会显示乱码,反之亦然,ANSI是ASCII的超集,所以英文总是能正常显示,英文占一个字节,中文占两个字节,所以光凭文件大小是不知道字符数的。

2,Unicode(Little endian,LE),这是Windows默认的Unicode编码,每个字符都是占据两个字节,全球统一,所以Unicode编码的文本文件都不会显示乱码,只可能由于缺乏字体的支持而显示出小方块(默认是小方块,也可能是别的)。准确说Unicode的字符并不一定是占据两个字节,但在Windows环境下这么认为是没有任何问题的。

3,Unicode(Big endian,BE),同上,唯一不同的是字节序,貌似这是Mac系统默认的编码格式。比如“中”字的Unicode(LE)编码是“2D 4E”,而Unicode(BE)的编码这是“4E 2D”。

4,UTF-8,和Unicode编码是一一对应的关系,并且兼容ASCII,所以UTF-8编码的文本文件同ANSI编码的那样,英文总是能正常显示,而它每个字符所占据的字节也是不确定的,可能占据一到六个字节,和Unicode不同,UTF-8并没有字节序一说,所以它往往被用作文本传输的标准格式,实现文本的跨平台传输。

别的我知道的还有UTF-16等格式,由于用得少,就不提了。另外,对于以上各类格式,如果有必要,还要区分Windows版,Unix版和Mac版,它们的关键区别在于对换行的理解,Windows版的换行其实是“回车字符”+“换行字符”,也就是0x0D+0x0A,而Unix版的只有“换行符”0x0A,Mac版的只有“回车符”0x0D,真是有趣极了。

那么,当你试图打开一个文本文件的时候,你会以哪种格式去“阅读”它呢?这让我想起以前我曾经工作过的一家公司,把一个任务交给我,就是做文件分类,其中有一个类型就是文本文件,这十分让我头疼,因为文本文件没有固定的格式,所以只好根据一些字符来判断,这个是不一定准的。通常,为了区分文本文件的格式,文本文件编辑器通常都会给文本文件添加一个叫BOM的标志,BOM是Byte Order Marked的缩写:

ANSI:没有BOM,直接是内容。
UNICODE(LE):FF FE
UNICODE(BE):FE FF
UTF-8:EF BB BF

所以我们可以根据这些特征来判断一个文本文件的编码格式。

那如果一个Unicode文件没有BOM,我们把它判定为ANSI格式的,岂不是乱了套?那是肯定的,但有些比较高级的文本编辑器,如UltraEdit,就有智能识别文本格式的功能,即便文本文件缺乏BOM,但这个我们就不讨论了。

也许你要问了:“有那么繁琐么?我只是想用C运行库获取文本内容。”在Windows环境下,目前还算比较简单了,VC++2005的运行库已经支持读取Unicode和UTF-8格式的文本文件,而下面我给出一个简单的例子,是读取一个UTF-8格式的文本文件的。

 

#include "stdafx.h"
#include <windows.h>

//test_utf8.txt的内容是四个汉字:“中文测试”
//一共占据15个字节,分别是:
//EF BB BF E4 B8 AD E6 96 87 E6 B5 8B E8 AF 95
//其中“EF BB BF”为BOM(Byte Order Mark),之后每个汉字占3个字节
int _tmain(int argc, _TCHAR* argv[])
{
    WCHAR szDataAll[64];
    FILE* pf = _wfopen(L"test_utf8.txt", L"r,ccs=utf-8");
    if (pf!=NULL)
    {
       long pos = ftell(pf);          //3
 
       ZeroMemory(szDataAll, sizeof(szDataAll));
       fread(szDataAll, 2, 1, pf);
       pos = ftell(pf);               //9
       OutputDebugStringW(szDataAll); //中
 
       ZeroMemory(szDataAll, sizeof(szDataAll));
       fread(szDataAll, 2, 1, pf);
       pos = ftell(pf);               //11
       OutputDebugStringW(szDataAll); //文
 
       ZeroMemory(szDataAll, sizeof(szDataAll));
       fread(szDataAll, 2, 1, pf);
       pos = ftell(pf);               //13
       OutputDebugStringW(szDataAll); //测
 
       ZeroMemory(szDataAll, sizeof(szDataAll));
       fread(szDataAll, 2, 1, pf);
       pos = ftell(pf);               //15
       OutputDebugStringW(szDataAll); //试
 
       fclose(pf);
    }
    return 0;
}
需要注意的是,使用fopen的时候,记得使用其宽字符版_wfopen,另外,注意fopen的第二个参数“ccs=utf-8”,是“ccs”而不是“css”,写错的话是无效的,这样就能直接把UTF-8的文本读进来,而不用管BOM,也不需要额外的转换,直接就已经是Unicode编码了。

注意上面我使用了ftell来测试文件指针的位置,看起来文件指针的行为确实有些怪异,貌似ftell使用起来不灵了,这个时候,这是我们要注意的一个地方;另一个要注意的地方就是fread的第二个参数,我写了2,其实指的是读进来的Unicode编码的字节数,要读一个字符,那就写2,读两个字符,那就写4,而不是UTF-8的3个字节一个汉字的这种长度。

如果你要读取一个Unicode(LE)的文本文件,将fopen的“ccs=utf-8”参数改为“ccs=unicode”即可。

这都是你已经知道了文件格式的前提下,所使用的方法,如果文件格式未知,你还得手工判断一下,先用“_wfopen(L"abc.txt", L"rb")”这种方式打开文件,再读取头几个字节来分析。

遗憾的是,“ccs=utf-8”这种参数并不是C的标准,这是Microsoft VC++的功能,并且我发觉Windows Mobile平台不能这样用,so,下面我就只好完全自己动手丰衣足食了,总的思路就是:判断文件格式,根据格式类型和该格式类型的标准,读取一定字符数目(究竟读取多少字节,要计算),然后利用Windows的API,MultiByToWideChar将其转为Unicode,当然了,如果文件就是Unicode(LE)的话,处理掉BOM就可以直接读取了,如果是Unicode(BE)的话,得倒一下字节序。

下面给出我实现的类的代码。

这是头文件TxtReader.h:

#pragma once

#include <windows.h>
#include <stdio.h>

enum
{
    TXT_TYPE_NONE = 0,
    TXT_TYPE_ANSI,
    TXT_TYPE_UNICODE_LE,
    TXT_TYPE_UNICODE_BE,
    TXT_TYPE_UTF8
};

class CTxtReader
{
public:
    CTxtReader(void);
    ~CTxtReader(void);

    BOOL Open(WCHAR* pFileName);
    void Close();
    BOOL Read(WCHAR* pBuff, DWORD dwToRead, DWORD& dwRead);
    LONG Tell();

protected:
    FILE* m_pFile;
    INT m_iType;
    CPINFO m_codepage;
    INT m_iMaxLeadBytePairNum;

    BOOL NeedNextByte(BYTE byFirstByte);
};
 这是CPP文件TxtReader.cpp:

#include "TxtReader.h"

CTxtReader::CTxtReader(void)
{
    m_pFile = NULL;
    m_iType = TXT_TYPE_NONE;

    GetCPInfo(CP_ACP, &m_codepage);

    m_iMaxLeadBytePairNum = 0;
    int i;
    for(i=0; i<5; i++)
    {
        if(m_codepage.LeadByte[i*2]==0 && m_codepage.LeadByte[i*2+1]==0)
            break;
        ++m_iMaxLeadBytePairNum;
    }
}

CTxtReader::~CTxtReader(void)
{
    Close();
}

BOOL CTxtReader::Open(WCHAR* pFileName)
{

补充:软件开发 , Vc ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,