14.1 文件类和函数

wxWidgets提供了一系列的平台无关的文件处理功能.在概览所有文件函数之前,我们先来看看几个主要的类.

wxFile和wxFFile

wxFile类可以用来处理底层的文件输入输出.它封装了常用的用于操作整数文件标识符的标准C操作(打开关闭,读写,移动游标等),但是和标准C不同的是,它使用wxLog类来报告错误并且在析构函数中自动关闭文件.而wxFFile则提供缓冲的输入输出操作,内部使用的是FILE类型的指针.

你可以通过下面的方法创建一个wxFile对象:使用默认构造函数然后调用Create或者Open函数;或者直接在构造函数中指明文件名和打开模式(wxFile::read, wxFile::write或wxFile::read_write);或者直接使用已经存在的整数文件描述符(相关构造函数或者默认构造函数加 Attach函数).Close函数关闭当前使用的文件,文件也将在析构函数中视需要进行关闭.

从文件中读取数据使用Read函数,参数为一个void和一个缓冲区大小,返回实际读取的数值大小或者 wxInvalidOffset如果读取过程发生错误.使用Write函数将指定大小的void类型的缓冲区写入文件,如果你希望写操作立即写到物理文件,你需要使用Flush函数.

Eof函数用来检测当前的文件指针是否位于文件结尾的位置(而wxFFile的Eof函数只有在其读操作越过了文件结尾的时候才返回True).你可以用Length函数返回文件的长度.

Seek和SeekEnd函数将文件指针移动到相对于文件开始或者文件结尾的一个偏移位置.Tell函数返回wxFileOffset类型的当前文件指针位置,这个类型在支持64位操作系统上是64位整数,否则是32位整数.

Access函数是一个静态函数,用来判断某个文件是否可以以指定的模式打开,而Exists函数则用来判断指定的文件是否存在.

下面的代码演示了怎样使用wxFile打开一个文件并且将其内容读取到一个数组中:

#include "wx/file.h"
if (!wxFile::Exists(wxT("data.dat")))
    return false;
wxFile file(wxT("data.dat"));
if ( !file.IsOpened() )
    return false;
//文件大小
wxFileOffset nSize = file.Length();
if ( nSize == wxInvalidOffset )
    return false;
// 将所有内容读取到一个数组中
wxUint* data = new wxUint8[nSize];
if ( fileMsg.Read(data, (size_t) nSize) != nSize )
{
    delete[] data;
    return false;
}
file.Close();

下面的代码则演示了怎样将一个文本框的所有内容写入到文件中:

bool WriteTextCtrlContents(wxTextCtrl* textCtrl,
                           const wxString& filename)
{
    wxFile file;
    if (!file.Open(filename, wxFile::write))
        return false
    int nLines = textCtrl->GetNumberOfLines();
    bool ok = true;
    for ( int nLine = 0; ok && nLine < nLines; nLine++ )
    {
        ok = file.Write(textCtrl->GetLineText(nLine) +
                        wxTextFile::GetEOL());
    }
    file.Close();
    return ok;
}

wxTextFile

wxTextFile提供了一种非常直接的方式来以行为单位读取和写入小型的文本文件.

使用Open函数将这个文本文件读取到内存中并且以行为单位进行分割,使用Write函数写回到文本文件.你可以使用GetLine函数或者直接按照数组的方式操作某个特定的行.或者使用GetFirstLine,GetNextLine和GetPrevLine进行遍历.AddLine 和InsertLine用来增加新行,RemoveLine用来移除特定的行,Clear函数则用来清空所有的行.

下面的例子演示了将文本文件中的每一行都增加一个前导文本的方法:

#include "wx/textfile.h"
void FilePrepend(const wxString& filename, const wxString& text)
{
    wxTextFile file;
    if (file.Open(filename))
    {
        size_t i;
        for (i = 0; i < file.GetLineCount(); i++)
        {
            file[i] = text + file[i];
        }
        file.Write(filename);
    }
}

wxTempFile

wxTempFile是wxFile的一个派生类,它使用临时文件来写入数据,数据在Commit函数被调用之前不会被写入.如果你需要写一些用户数据,你可以将其写在临时文件里,它的好处是:如果遇到突然的断电或者应用程序不可知错误或者其它大的错误时,临时文件不会对磁盘上的文件系统造成任何伤害.

提示:文档/视图框架在创建一个输出流然后调用SaveObject的时候没有使用临时文件.你可以尝试重载其 DoSaveDocument函数,在其中构建一个wxFileOutputStream并且让其使用一个临时文件wxTempFile对象,在全部数据写完以后,调用Sync函数和Commit函数将其写入临时文件.

wxDir

wxDir是一个轻便的等价于Unix上的open/read/closedir函数的类,它支持枚举目录中的所有文件.wxDir既支持枚举目录中的文件,也支持枚举目录中的子目录.它还提供了一个灵活的递归枚举文件的方法Traverse函数,和另外一种简单的方法: GetAllFiles函数.

首先,你需要调用Open函数打开一个目录(或者通过构造函数直接赋值),然后调用GetFirst函数,参数为一个指向字符串类型的指针用来接收找到的文件名,一个可选的文件通配符(默认为所有文件)和一个可选的选项.然后调用GetNext函数直到其返回False.文件通配符可以是固定的文件名以及包含"*(匹配任意字符)"和"?"(匹配单个字符)的通配符.选项参数可选的值为:wxDIR_FILES(所有文件), wxDIR_DIRS(所有文件夹),wxDIR_HIDDEN(隐藏文件)以及wxDIR_DOTDOT("."和"..")以及它们的组合,默认值为除了最后一项的所有文件.

下面是一个例子:

#include "wx/dir.h"
wxDir dir(wxGetCwd());
if ( !dir.IsOpened() )
{
    // 如果遇到这个情况,wxDir已经显示了一个出错信息.
    // 所以直接返回就可以了
    return;
}
puts("Enumerating object files in current directory:");
wxString filename;
wxString filespec = wxT("*.*");
int flags = wxDIR_FILES|wxDIR_DIRS;
bool cont = dir.GetFirst(&filename, filespec, flags);
while ( cont )
{
    wxLogMessage(wxT("%s\n"), filename.c_str());
    cont = dir.GetNext(&filename);
}

如同上面注释中说的那样,如果wxDir打开的时候出现错误,将会弹出一个错误消息,如果想禁止这个消息,你可以临时通过设置wxLogNull的方法,如下所示:

{
    wxLogNull logNull;
    wxDir dir(badDir);
    if ( !dir.IsOpened() )
    {
        return;
    }
}

wxFileName

wxFileName用来处理文件名.它可以分解和组合文件名,还提供了很多额外的操作,其中某些为静态函数.下面演示了一些例子,更多的功能请参考wxWidgets的手册:

#include "wx/filename.h"
// 使用字符串创建文件名
wxFileName fname(wxT("MyFile.txt"));
// Normalize,在windows平台上这个函数的动作包括
// 确保文件名为长文件名格式
fname.Normalize(wxPATH_NORM_LONG|wxPATH_NORM_DOTS|wxPATH_NORM_TILDE|
                wxPATH_NORM_ABSOLUTE);
// 返回全路径
wxString filename = fname.GetFullPath();
// 返回相对于当前目录的路径
fname.MakeRelativeTo(wxFileName::GetCwd());
// 文件存在吗?
bool exists = fname.FileExists();
// 另外一个文件存在吗?
bool exists2 = wxFileName::FileExists(wxT("c:\\temp.txt"));
// 返回文件名的名称部分
wxString name = fname.GetName();
// 返回路径部分
wxString path = fname.GetPath();
// 在windows系统上返回相应的短路径文件名,其它平台上
// 返回本身.
wxString shortForm = fname.GetShortPath();
// 创建一个文件夹
bool ok = wxFileName::Mkdir(wxT("c:\\thing"));

File Functions

下表列出了一些有用的静态文件操作函数,它们定义在头文件wx/filefn.h中.请同时参考wxFileName类,尤其是其中的静态函数部分,比如wxFileName::FileExists函数.

wxDirExists(dir) 是否目录存在. 参考wxFileName::DirExists
wxConcatFiles(f1, f2, f3) 将f1和f2合并为f3, 成功时返回True.
wxCopyFile(f1, f2, overwrite) 拷贝f1到f2,可选择是否覆盖已存在的f2.返回Bool型
wxFileExists(file) 测试是否文件存在. 参考wxFileName::FileExists
wxFileModificationTime(file) 返回文件修改时间(time_t类型). 参考wxFileName::GetModificationTime,它返回wxDateTime类型
wxFileNameFromPath(file) 返回文件全路径的文件名部分. 推荐使用wxFileName::SplitPath函数
wxGetCwd() 返回当前工作目录. 参考wxFileName::GetCwd
wxGetdiskSpace (path, total, free) 返回指定路径所在的磁盘的全部空间和空闲空间,空间为wxLongLong类型.
wxIsAbsolutePath(path) 测试指定的路径是否为绝对路径.
wxMkdir(dir, permission=777) 创建一个目录,其父目录必须存在,可选指定目录访问掩码.返回bool型
wxPathOnly(path) 返回给定全路径的目录部分.
wxRemoveFile(file) 删除文件,成功时返回True.
wxRenameFile(file1, file2) 重命名文件,成功时返回True.如果需要进行文件拷贝,则直接返回False.
wxRmdir(file) 移除空目录,成功时返回True.
wxSetWorkingDirectory(file) 设置当前工作目录,返回bool型.

wxWidgets同样提供了许多函数来封装标准C函数,比如: wxFopen, wxFputc和wxSscanf,这些函数没有在手册中记录,不过你应该可以在include/wx/wxchar.h中找到它们.

另外一个有用的宏是wxFILE_SEP_PATH,它代表了不同平台上的路径分割符,比如在windows平台上它代表"\",而在Unix平台上则代表"/".