Essential Qt 第十二章 文本文件的读写

                在C/C++语言中,有一大堆关于本地文件读写的函数,比如C语言提供了至少30个以上的IO函数来支持文件的读写,而C++则使用fstream等类来完成,当然关于C++的IO相关部分足可以写出好几部大头书。在Qt中也提供了很多和文件读写相关的IO类,这里只能简单的介绍其中的一小部分。关于文件,window下区分文本文件和二进制文件,而linux下则不做区分,而Qt为了则提供了QTextStream和QDateStream两个类,分别用于文本文件和二进制文件的IO,这一章简单介绍下QTextStream类,这样可以完成前面ReadMe程序中“文件”菜单上的功能。
                在Qt中提供了和文件相关的类QFile,这个类用于文件的操作,另外还有一个类QFileInfo则提供了大量和文件相关成员函数,比如path()函数用于返回文件路径,isDir()返回该文件(名)是否问目录,isFile()则返回是否问文件,等等
                回到QFile类,对于这个类来说,用来创建一个新文件很简单
QFile F(FilePath);
F.open(QIODevice::ReadWrite);//注释1
F.close();//注释2
注释1: 这里使用了成员函数open()来打开一个文件,参数是文件打开的方式,如果熟悉C语言的fopen()函数,类似与参数r,r+w,w等,该枚举值QIODevice::OpenModeFlag在QIODevice中定义
注释2: 这里使用了close()函数来关闭打开的文件,一般来说Qt的QFile类操作文件不需要来调用这个close()函数,QFile对象在离开作用域后会自动关闭,但比较特殊的是创建文件,Qt创建文件的方法有些“特别”,如上所示,先打开一个文件,然后再显式的关闭,这样就创建了一个新文件,当然前提是确保上面的FilePath不存在,如果需确认,可以先调用exist()函数确定这个文件是否已经存在
                  文本文件的IO需要用到QTextStream类,下面的代码是向一个文件写数据
QFile F(FilePath);
QTextStream FDate(&F);  //注释1
F.open(QIODevice::WriteOnly);
FDate<<"This is Qt.";  //注释2
注释1:这里生成了一个“文本文件的流”,这是我起的名字,因为我找不到更合适的词汇来称呼他,(只写)打开文件后,可以对这个流进行写操作,这里将"This is Qt"写入流,从而达到写入文件的目的
注释2: 这里QTextStream使用操作符"<<"来进行数据输入,这个C++的iostream类很相似,如果要读取文本文件也很简单,只需要打开文件是的参数使用读,然后用">>"把数据读到相应的对象里即可,QTextStream类还提供了一些函数来用于对数据的读写readLine()用于逐行读取文件的内容,readAll()则用于把文件的内容一次性读取等

               接下来我们将完成前面ReadMe文件中尚未完成的“文件”菜单功能。首先是“打开”功能,在ReadMe.h中添加一个私有槽void OpenExistFile()来实现这个功能,该槽函数具体实现如下
void ReadMe::OpenExistFile()
{
  CurrentFilePath = QFileDialog::getOpenFileName(this,tr("打开文件"),".",tr("Text(*.txt)"));//注释1
  if(CurrentFilePath.isEmpty())  //注释2
  {
    QMessageBox::warning(this,tr("空文件"),tr("该文件为空或不存在"),QMessageBox::Yes);
    return;
  }
  QFile F(CurrentFilePath);
  QTextStream FDate(&F);
  F.open(QIODevice::ReadWrite|QIODevice::Text);
  MainEditWindow->setText(FDate.readAll()); //注释3
}
注释1: 变量CurrentFilePath在ReadMe.h中定义的一个QString对象,这个对于用于保持当前编辑的文本的(包含完成路径)文件名。这里使用了QFileDialog的静态函数来打开一个文件,这个函数会创建一个以来本地系统的文件打开对话框,使用的第一个参数为该对话框的父对象,第二个参数为对话框标题,第三个参数为对话框出现的默认路径,这里使用"."来表示(程序的)当前目录,如果希望使用其他目录作为默认目录,则可以用一个QString来替换这个".".第四个参数是这个对话框需要打开的文件类型,基本格式为 文件类型(*.文件后缀),以这个例子为例,如果你还希望他能打开.cxx和.cpp的文本文件,则第四个参数可以写出tr(Text(*.txt*.cpp*.cxx);这个函数返回值是打开的文件,包含完成路径名的文件名,类型为QString,如果用户在对话框中点击取消或者之间点击"X"关闭了对话框,则该函数返回一个空的QString,
注释2: 用过判断文件名是否为空可以知道用户有没有打开文件
注释3: 这里使用了上面的利用QTextStream类来读取文件的信息,成员函数readAll()会把文件所有内容都读取到内存中,由于纯文本文件一般都比较小,所以不会有什么问题,但如果是其他类型的数据,使用readAll()就要慎重考虑内存的大小。

           然后是“保存”功能具体实现为
void ReadMe::SaveCurrentFile()
{
  if(CurrentFilePath.isEmpty())  //注释1
    SaveAsCurrentFile();
  else
  {
    QFile F(CurrentFilePath);
    QTextStream FDate(&F);
    F.open(QIODevice::WriteOnly);
    FDate<<(MainEditWindow->toPlainText());  //注释2
    MainEditWindow->document()->setModified(false);  //注释3
  }
}
注释1: 这里有个特殊情况需要考虑,第一次打开程序,然后输入了诺干字符,此时由于没有打开任何文件,所欲用于保存当前编辑文件名的变量此时为空,出现这种情况是点击保存则必须使用“另存为”的功能,“另存为”稍后实现
注释2: 这里使用QTextStream来实现文件的写功能      
注释3: QTextEdit的成员函数document()将编辑的内容作为一个QDocument类来返回,这个类提供了很多和文本相关的函数,其中setModified()函数可以将设置文本没有过改动,或者有过改动,在这个例子中,文件保存后就将编辑的文本设置为“未改动”状态。而函数isModified()函数则返回文本有无改动   

          然后是"另存为"功能实现
void ReadMe::SaveAsCurrentFile()
{
  CurrentFilePath = QFileDialog::getSaveFileName(this,tr("文件另存为"),".",tr("Text(*.txt)"));//注释1
  if(CurrentFilePath.isEmpty()) //注释2
  {
    QMessageBox::warning(this,tr("保存失败"),tr("保存文件失败"),QMessageBox::Yes);
    return;
  }
  QFile F(CurrentFilePath);
  QTextStream FDate(&F);
  F.open(QIODevice::WriteOnly);
  FDate<<(MainEditWindow->toPlainText());
  MainEditWindow->document()->setModified(false);//注释3
}
注释1: 这里使用了QFileDialog的另一个静态函数,该函数生产一个以来本地系统的保存文件对话框,函数的参数和返回值和打开功能中的QFileDialog::getOpenFileName()函数一样
注释2: 这里同样需要判断下保存文件对话框有没有选取到正确的文件,用户点击取消或直接关闭对话框同样会返回空值
注释3: 在另存为文件,再次调用setModified()函数将文本设置为“未改动”

              最后是稍显复杂的“新建”功能
void ReadMe::CreateNewFile()
{
  if(MainEditWindow->document()->isModified() and !(MainEditWindow->toPlainText().isEmpty())) //注释1
  {
    int buttons = QMessageBox::warning(this,tr("文件未保存"),tr("原文件尚未保存,是否需要保存?"),QMessageBox::Yes|QMessageBox::No);
    if(buttons == QMessageBox::Yes)
    {
      if(CurrentFilePath.isEmpty()) //注释2
        SaveAsCurrentFile();
      else
        SaveCurrentFile();
    }
    if(buttons == QMessageBox::No)  //注释3
       MainEditWindow->clear();
  }
  if(CurrentFilePath.isEmpty()) //注释4
    return;
  MainEditWindow->clear();
}
注释1:在新建一个文件之前,需要判断下这个编辑窗体中是否有文本,以及文本有没有改动,如果窗体中存在文本,且文本状态为“已经改动”,则需要提示用户是否需要保存当前编辑的文本
注释2 :如果需要保存,这里还要区分两种情况,即如果正在编辑的文本是第一次打开程序后写入的内容,此时程序中用于保持文件名的变量CurrentFilePath为空,遇到这种情况需要使用“另存为”来保存文本,其他情况则使用“保持”
注释3: 如果用户选择了不需要保存,则直接清空编辑框里的内容
注释4: 这行代码用于应对一种特殊情况,在注释2中,用户选择了保存文件,而且这个文件是第一次打开程序输入的文件(即CurrentFilePath为空),然后转至"另存为"中,但在“另存为”函数中,用户没有保存,而是直接关闭了文件保存对话框,遇到这种情况,就必须避免用户在“另存为”功能中选择不保存而把编辑的内容清空掉

               最后把这些槽函数和对应的“文件”菜单中的动作连接起来,在ConnectSignalAndSlot()函数中添加
  connect(Open_Action,SIGNAL(triggered()),this,SLOT(OpenExistFile()));
  connect(New_Action,SIGNAL(triggered()),this,SLOT(CreateNewFile()));
  connect(Save_Action,SIGNAL(triggered()),this,SLOT(SaveCurrentFile()));
  connect(SaveAs_Action,SIGNAL(triggered()),this,SLOT(SaveAsCurrentFile()));


               就ReadMe程序,要完善他的功能,还需要重新实现它的关闭事件函数,在关闭事件之前,首先要用isModified()判断当前编辑文本是否已经被保存,如果没有保存,则需要询问下是否需要被保存,这里和“新建”功能有些类似
               
void ReadMe::closeEvent(QCloseEvent* event)
{
  if(MainEditWindow->document()->isModified() and !(MainEditWindow->toPlainText().isEmpty()))
  {
    int buttons = QMessageBox::warning(this,tr("文件未保存"),tr("原文件尚未保存,是否需要保存?"),QMessageBox::Yes|QMessageBox::No);
    if(buttons == QMessageBox::Yes)
    {
      if(CurrentFilePath.isEmpty())
        SaveAsCurrentFile();
      else
        SaveCurrentFile();
      event->ignore();    //注释1
    }
    if(buttons == QMessageBox::No)
    {
      event->accept();    
      return;
    }
  }
}
注释1: 对于一个为编辑的文本,程序在关闭前给出了一个提示,是否需要保存,如果选择了保存,则进入保存程序,但无论用户是否保存,这次关闭事件都将不执行,这里用事件的成员函数igrone()来屏蔽掉此次关闭事件
    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值