/*******************************************************************************************/
一、QT文件操作
文件操作是应用程序必不可少的 部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。
Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。
qt中的文件(IO)层次:
QIODevice是基类,下面派生出了各个子类,其中:
QBuffer 类似与数组,操作内存缓冲区
QProcess 类似system函数,开启一个其他进程
QFileDevice 普通文件,类似文件描述符
QAbsrtacSocket 网络套接字
具体见图1:
具体说明:
QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;
QFileDevice:Qt5新增加的类,提供了有关文件操作的通用实现。
QFlie:访问本地文件或者嵌入资源;
QTemporaryFile:创建和访问本地文件系统的临时文件;
QBuffer:读写QbyteArray, 内存文件;
QProcess:运行外部程序,处理进程间通讯;
QAbstractSocket:所有套接字类的父类;
QTcpSocket:TCP协议网络数据传输;
QUdpSocket:传输 UDP 报文;
QSslSocket:使用 SSL/TLS 传输数据;
文件操作分类:
顺序访问设备:
是指它们的数据只能访问一遍:从头走到尾,从第一个字节开始访问,直到最后一个字节,中途不能返回去读取上一个字节,这其中,QProcess、QTcpSocket、QUdpSoctet和QSslSocket是顺序访问设备。
随机访问设备:
可以访问任意位置任意次数,还可以使用QIODevice::seek()函数来重新定位文件访问位置指针,QFile、QTemporaryFile和QBuffer是随机访问设备,
/*******************************************************************************************/
二、QFile文件操作(基本文件操作)
文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。在所有的 I/O 设备中,
文件 I/O 是最重要的部分之一。因为我们大多数的程序依旧需要首先访问本地文件(当然,在云计算大行其道的将来,这一观点可能改变)。
QFile提供了从文件中读取和写入数据的能力。
我们通常会将文件路径作为参数传给QFile的构造函数。不过也可以在创建好对象最后,使用setFileName()来修改。QFile需要使用 / 作为文件分隔符,
不过,它会自动将其转换成操作系统所需要的形式。例如 C:/windows 这样的路径在 Windows 平台下同样是可以的。
QFile主要提供了有关文件的各种操作,比如打开文件、关闭文件、刷新文件等。我们可以使用QDataStream或QTextStream类来读写文件,
也可以使用QIODevice类提供的read()、readLine()、readAll()以及write()这样的函数。
值得注意的是,有关文件本身的信息,比如文件名、文件所在目录的名字等,则是通过QFileInfo获取,而不是自己分析文件路径字符串。
1.QFile读文件:
void Widget::on_buttonRead_clicked()
{
QString path = QFileDialog::getOpenFileName(this,
"open", "../", "TXT(*.txt)");//获取文件路径
if(path.isEmpty() == false)
{
//创建文件对象
QFile file(path);
//打开文件,只读方式
bool isOk = file.open(QIODevice::ReadOnly);
if(isOk == true)
{
#if 0
//读文件
QByteArray array = file.readAll();//一次性读完,这个读的接口默认都的文本是只识别utf8编码
//,要读其他编码的要借助于后面讲的文本流
//显示到编辑区
//ui->textEdit->setText(QString(array));
ui->textEdit->setText(array);//内部自动转换了类型
#endif
QByteArray array;
while( file.atEnd() == false)
{
//读一行
array += file.readLine();//默认读完一行,换行符也读进去了
}
ui->textEdit->setText(array);
}
//关闭文件
file.close();
}
}
2.QFile写文件:
void Widget::on_buttonWrite_clicked()
{
//获取保存文件的路径
QString path = QFileDialog::getSaveFileName(this, "save", "../", "TXT(*.txt)");
if(path.isEmpty() == false)
{
QFile file; //创建文件对象
//关联文件名字
file.setFileName(path);
//打开文件(没有则创建),只写方式
bool isOk = file.open(QIODevice::WriteOnly);
if(isOk == true)
{
//获取编辑区内容
QString str = ui->textEdit->toPlainText();//qt里文本用的是utf8
//写文件
// QString -> QByteArray
//file.write(str.toUtf8());//保存的结果是utf8的文本,可以正常打开并显示,无乱码
//QString -> c++ string -> char *
//file.write(str.toStdString().data());//保存的结果是utf8的文本,可以正常打开并显示,无乱码
//转换为本地平台编码(win默认编码,一般是ansi),可以正常打开并显示,无乱码
file.write(str.toLocal8Bit());
//QString -> QByteArray 转换方法:
QString buf = "123";
QByteArray a = buf.toUtf8(); //中文//QByteArray是utf8格式的文本
a = buf.toLocal8Bit(); //本地编码(win默认编码)//QByteArray是本地编码的文本
//QByteArray -> char *
char *b = a.data();
//char * -> QString
char *p = "abc";
QString c = QString(p);
}
file.close();
}
}
3.QfileInfo获取文件信息
//获取文件信息
QFileInfo info(path);
qDebug() << "文件名字:" << info.fileName().toUtf8().data();//转换为utf8才能打印出来
qDebug() << "文件后缀:" << info.suffix();
qDebug() << "文件大小:" << info.size();
qDebug() << "文件创建时间:" <<
info.created().toString("yyyy-MM-dd hh:mm:ss"); //2016-01-04 15:13:00
具体见《QFile》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 6 namespace Ui { 7 class Widget; 8 } 9 10 class Widget : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit Widget(QWidget *parent = 0); 16 ~Widget(); 17 18 private slots: 19 void on_buttonRead_clicked(); 20 21 void on_buttonWrite_clicked(); 22 23 private: 24 Ui::Widget *ui; 25 }; 26 27 #endif // WIDGET_H

1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QFile> 4 #include <QFileDialog> 5 #include <QFileInfo> 6 #include <QDebug> 7 #include <QDateTime> 8 #include <QDataStream> 9 10 Widget::Widget(QWidget *parent) : 11 QWidget(parent), 12 ui(new Ui::Widget) 13 { 14 ui->setupUi(this); 15 } 16 17 Widget::~Widget() 18 { 19 delete ui; 20 } 21 22 void Widget::on_buttonRead_clicked() 23 { 24 QString path = QFileDialog::getOpenFileName(this, 25 "open", "../", "TXT(*.txt)"); 26 if(path.isEmpty() == false) 27 { 28 //文件对象 29 QFile file(path); 30 31 //打开文件,只读方式 32 bool isOk = file.open(QIODevice::ReadOnly); 33 if(isOk == true) 34 { 35 #if 0 36 //读文件,默认只识别utf8编码 37 QByteArray array = file.readAll(); 38 //显示到编辑区 39 //ui->textEdit->setText(QString(array)); 40 ui->textEdit->setText(array); 41 #endif 42 43 QByteArray array; 44 while( file.atEnd() == false) 45 { 46 //读一行 47 array += file.readLine(); 48 } 49 ui->textEdit->setText(array); 50 51 } 52 53 //关闭文件 54 file.close(); 55 56 //获取文件信息 57 QFileInfo info(path); 58 qDebug() << "文件名字:" << info.fileName().toUtf8().data(); 59 qDebug() << "文件后缀:" << info.suffix(); 60 qDebug() << "文件大小:" << info.size(); 61 qDebug() << "文件创建时间:" << 62 info.created().toString("yyyy-MM-dd hh:mm:ss"); //2016-01-04 15:13:00 63 64 } 65 66 67 } 68 69 void Widget::on_buttonWrite_clicked() 70 { 71 QString path = QFileDialog::getSaveFileName(this, "save", "../", "TXT(*.txt)"); 72 if(path.isEmpty() == false) 73 { 74 QFile file; //创建文件对象 75 //关联文件名字 76 file.setFileName(path); 77 78 //打开文件,只写方式 79 bool isOk = file.open(QIODevice::WriteOnly); 80 if(isOk == true) 81 { 82 //获取编辑区内容 83 QString str = ui->textEdit->toPlainText(); 84 //写文件 85 // QString -> QByteArray 86 //file.write(str.toUtf8()); 87 88 //QString -> c++ string -> char * 89 //file.write(str.toStdString().data()); 90 91 //转换为本地平台编码 92 file.write(str.toLocal8Bit()); 93 94 95 //QString -> QByteArray 96 QString buf = "123"; 97 QByteArray a = buf.toUtf8(); //中文 98 a = buf.toLocal8Bit(); //本地编码 99 100 //QByteArray -> char * 101 char *b = a.data(); 102 103 //char * -> QString 104 char *p = "abc"; 105 QString c = QString(p); 106 107 108 } 109 110 file.close(); 111 112 } 113 114 115 116 117 }
/*******************************************************************************************/
三、QDataStream读写文件(二进制读写文件)
以二进制的方法操作文件,就要借助QDataStream
由于是操作文件,所以还得用到QFile
void Widget::writeData()
{
//创建文件对象
QFile file("../test.txt");
//打开文件, 只写方式打开
bool isOk = file.open(QIODevice::WriteOnly);
if(true == isOk)
{
//创建数据流,和file文件关联
//往数据流中写数据,相当于往文件里写数据
QDataStream stream(&file);
stream << QString("主要看气质") << 250;//结果文件里都是不可见的二进制数据
file.close();
}
}
#define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]"
void Widget::readData()
{
//创建文件对象
QFile file("../test.txt");
//打开文件, 只读方式打开
bool isOk = file.open(QIODevice::ReadOnly);
if(true == isOk)
{
//创建数据流,和file文件关联
//往数据流中读数据,相当于往文件里读数据
QDataStream stream(&file);
//读的时候,按写的顺序取数据
QString str;//先取出str,因为这个是之前先输入的
int a;
stream >> str >> a;
//qDebug() << str.toUtf8().data() << a;
cout << str.toUtf8().data() << a;
file.close();
}
}
具体见《QDataStream》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 6 namespace Ui { 7 class Widget; 8 } 9 10 class Widget : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit Widget(QWidget *parent = 0); 16 ~Widget(); 17 18 void writeData(); 19 void readData(); 20 21 private: 22 Ui::Widget *ui; 23 }; 24 25 #endif // WIDGET_H

1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QDataStream> 4 #include <QTextStream> 5 #include <QFile> 6 #include <QDebug> 7 #define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]" 8 9 Widget::Widget(QWidget *parent) : 10 QWidget(parent), 11 ui(new Ui::Widget) 12 { 13 ui->setupUi(this); 14 15 writeData(); 16 readData(); 17 } 18 19 Widget::~Widget() 20 { 21 delete ui; 22 } 23 24 void Widget::writeData() 25 { 26 //创建文件对象 27 QFile file("../test.txt"); 28 29 //打开文件, 只写方式打开 30 bool isOk = file.open(QIODevice::WriteOnly); 31 if(true == isOk) 32 { 33 //创建数据流,和file文件关联 34 //往数据流中写数据,相当于往文件里写数据 35 QDataStream stream(&file); 36 37 stream << QString("主要看气质") << 250; 38 39 file.close(); 40 41 } 42 43 } 44 45 void Widget::readData() 46 { 47 //创建文件对象 48 QFile file("../test.txt"); 49 50 //打开文件, 只读方式打开 51 bool isOk = file.open(QIODevice::ReadOnly); 52 if(true == isOk) 53 { 54 //创建数据流,和file文件关联 55 //往数据流中读数据,相当于往文件里读数据 56 QDataStream stream(&file); 57 //读的时候,按写的顺序取数据 58 QString str; 59 int a; 60 stream >> str >> a; 61 //qDebug() << str.toUtf8().data() << a; 62 cout << str.toUtf8().data() << a; 63 64 file.close(); 65 66 } 67 }
/*******************************************************************************************/
四、QTextStream操作文件(文本的方式操作文件)
上一节我们介绍了有关二进制文件的读写。二进制文件比较小巧,却不是人可读的格式。而文本文件是一种人可读的文件。
为了操作这种文件,我们需要使用QTextStream类。QTextStream和QDataStream的使用类似,只不过它是操作纯文本文件的。
QTextStream会自动将 Unicode 编码同操作系统的编码进行转换,这一操作对开发人员是透明的。它也会将换行符进行转换,
同样不需要自己处理。QTextStream使用 16 位的QChar作为基础的数据存储单位,同样,它也支持 C++ 标准类型,如 int 等。
实际上,这是将这种标准类型与字符串进行了相互转换。
通过文本的方式操作文件,这样的好处是读写的时候可以指定编码
QTextStream只能通过文本的方式操作文件
void Widget::writeData()
{
QFile file;
file.setFileName("../demo.txt");
bool isOk = file.open(QIODevice::WriteOnly);
if(true == isOk)
{
QTextStream stream(&file);
//指定编码
stream.setCodec("UTF-8");
//默认以系统平台的编码打印
stream << QString("主要看气质") << 250;//格式化输出为可见字符,可以显示,类似格式化打印
file.close();
}
}
void Widget::readData()
{
QFile file;
file.setFileName("../demo.txt");
bool isOk = file.open(QIODevice::ReadOnly);
if(true == isOk)
{
QTextStream stream(&file);
//指定编码,前面指定以什么编码读,最好后面也指定以什么编码读
stream.setCodec("UTF-8");
QString str;//
int a;
stream >> str >> a;//这种方式读取,由于文件中写入的都是文本字符串,所以第一次就
//把内容全部读取给str了,a根本就获取不到内容。所以不能使用这种方式,一般使用函数
//的方式,见下面的函数
cout << str.toUtf8().data() << a;//显示的时候还是需要转换为utf8
file.close();
}
}
void Widget::on_pushButton_clicked()
{
QString path = QFileDialog::getOpenFileName(this,
"open", "../" );
if(false == path.isEmpty())
{
QFile file;
file.setFileName(path);
bool isOk = file.open(QIODevice::ReadOnly);
if(true == isOk)
{
QTextStream stream(&file);
//指定编码
stream.setCodec("UTF-8");
QString str = stream.readAll();//使用函数读取自己想要的内容
ui->textEdit->setText(str);
file.close();
}
}
}
具体见《QTextStream》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 6 namespace Ui { 7 class Widget; 8 } 9 10 class Widget : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit Widget(QWidget *parent = 0); 16 ~Widget(); 17 18 void writeData(); 19 void readData(); 20 21 private slots: 22 void on_pushButton_clicked(); 23 24 private: 25 Ui::Widget *ui; 26 }; 27 28 #endif // WIDGET_H

1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QTextStream> 4 #include <QFile> 5 #include <QDebug> 6 #include <QFileDialog> 7 #include <QBuffer> 8 #define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]" 9 10 Widget::Widget(QWidget *parent) : 11 QWidget(parent), 12 ui(new Ui::Widget) 13 { 14 ui->setupUi(this); 15 writeData(); 16 17 readData(); 18 } 19 20 Widget::~Widget() 21 { 22 delete ui; 23 } 24 25 void Widget::writeData() 26 { 27 QFile file; 28 file.setFileName("../demo.txt"); 29 30 bool isOk = file.open(QIODevice::WriteOnly); 31 if(true == isOk) 32 { 33 QTextStream stream(&file); 34 //指定编码 35 stream.setCodec("UTF-8"); 36 37 stream << QString("主要看气质") << 250; 38 39 file.close(); 40 } 41 } 42 43 void Widget::readData() 44 { 45 QFile file; 46 file.setFileName("../demo.txt"); 47 48 bool isOk = file.open(QIODevice::ReadOnly); 49 if(true == isOk) 50 { 51 QTextStream stream(&file); 52 //指定编码 53 stream.setCodec("UTF-8"); 54 QString str; 55 int a; 56 stream >> str >> a; 57 58 cout << str.toUtf8().data() << a; 59 60 file.close(); 61 62 } 63 } 64 65 void Widget::on_pushButton_clicked() 66 { 67 QString path = QFileDialog::getOpenFileName(this, 68 "open", "../" ); 69 if(false == path.isEmpty()) 70 { 71 QFile file; 72 file.setFileName(path); 73 74 bool isOk = file.open(QIODevice::ReadOnly); 75 if(true == isOk) 76 { 77 QTextStream stream(&file); 78 //指定编码 79 stream.setCodec("UTF-8"); 80 81 QString str = stream.readAll(); 82 ui->textEdit->setText(str); 83 84 file.close(); 85 } 86 87 } 88 89 90 }
/*******************************************************************************************/
四、QBuffer
QBuffer也叫内存文件,放在内存里面的
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QByteArray array;
QBuffer memFile(&array); //创建内存文件,指定了缓冲区&array,后续读写操作的都是这个&array
memFile.open(QIODevice::WriteOnly);
memFile.write("11111111111111111");//实际上是写到缓冲区里面
memFile.write("22222222222222222222");//注意两个之间没有换行符
memFile.close();
qDebug() << memFile.buffer();//取出数据
qDebug() << "array" << array;//前后打印结果是一样的
//内存文件配合数据流的使用
QBuffer memFile1;
memFile1.open(QIODevice::WriteOnly);
QDataStream stream(&memFile1);//内存文件在数据流中的使用
stream << QString("测试") << 250;
memFile1.close();
qDebug() << memFile1.buffer();//输出显示的是一堆二进制数据
memFile1.open(QIODevice::ReadOnly);
QDataStream in;
in.setDevice(&memFile1);
QString str;
int a;
in >> str >> a;
memFile1.close();
qDebug() << str.toUtf8().data() << a;
}
具体见《QBuffer》

1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 6 namespace Ui { 7 class Widget; 8 } 9 10 class Widget : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit Widget(QWidget *parent = 0); 16 ~Widget(); 17 18 19 private: 20 Ui::Widget *ui; 21 }; 22 23 #endif // WIDGET_H

1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QBuffer>//内存文件 4 #include <QDebug> 5 #include <QDataStream> 6 7 Widget::Widget(QWidget *parent) : 8 QWidget(parent), 9 ui(new Ui::Widget) 10 { 11 ui->setupUi(this); 12 13 QByteArray array; 14 QBuffer memFile(&array); //创建内存文件 15 memFile.open(QIODevice::WriteOnly); 16 17 memFile.write("11111111111111111"); 18 memFile.write("22222222222222222222"); 19 20 memFile.close(); 21 22 qDebug() << memFile.buffer(); 23 qDebug() << "array" << array; 24 25 26 QBuffer memFile1; 27 memFile1.open(QIODevice::WriteOnly); 28 QDataStream stream(&memFile1); 29 stream << QString("测试") << 250; 30 memFile1.close(); 31 32 qDebug() << memFile1.buffer(); 33 34 memFile1.open(QIODevice::ReadOnly); 35 QDataStream in; 36 in.setDevice(&memFile1); 37 QString str; 38 int a; 39 in >> str >> a; 40 memFile1.close(); 41 42 qDebug() << str.toUtf8().data() << a; 43 44 } 45 46 Widget::~Widget() 47 { 48 delete ui; 49 }
/*******************************************************************************************/
五、实例___棋盘的制作
使用绘图实现
核心算法:
1.等分窗口的宽和高,形成一个个相同的格子,同时记录格子的宽和高方便绘图
gridW = width()/10; //窗口宽度分10份
gridH = height()/10; //窗口高度分10份
//棋盘起点坐标
startX = gridW;
startY = gridH;
2.计算鼠标落在哪个格子上,使用除法,鼠标的坐标除以格子的宽和高取整就知道落在哪个格子上
知道落在哪个格子上,再乘以对应的宽和高就知道绘图的坐标
{
// 棋盘的位置转换转换为坐标下标值
// 类似于a[i][j]的i和j
chessX = (x - startX)/gridW;
chessY = (y - startY)/gridH;
qDebug() << chessX << chessY;
//更新窗口,间接调用paintEvent()
update();
}
//画棋子
if(chessX != -1 && chessY != -1)
{
p.drawPixmap(startX+chessX*gridW, startY+chessY*gridH,
gridW, gridH,
QPixmap(":/new/prefix1/Image/face.png")
);
}
具体见《ChessDemo》

1 #ifndef CHESSWIDGET_H 2 #define CHESSWIDGET_H 3 4 #include <QWidget> 5 6 class ChessWidget : public QWidget 7 { 8 Q_OBJECT 9 10 public: 11 ChessWidget(QWidget *parent = 0); 12 ~ChessWidget(); 13 14 protected: 15 //重写绘图事件 16 void paintEvent(QPaintEvent *); 17 //重写鼠标按下事件 18 void mousePressEvent(QMouseEvent *e); 19 20 private: 21 int gridW; //棋盘水平方向一个格子的宽度 22 int gridH; //棋盘水平方向一个格子的高度 23 int startX; //棋盘起点x坐标 24 int startY; //棋盘起点y坐标 25 26 int chessX, chessY; //棋盘下标 27 28 }; 29 30 #endif // CHESSWIDGET_H

1 #include "chesswidget.h" 2 #include <QPainter> 3 #include <QPen> 4 #include <QMouseEvent> 5 #include <QDebug> 6 7 ChessWidget::ChessWidget(QWidget *parent) 8 : QWidget(parent) 9 { 10 chessX = -1; 11 chessY = -1; 12 } 13 14 ChessWidget::~ChessWidget() 15 { 16 17 } 18 19 void ChessWidget::paintEvent(QPaintEvent *) 20 { 21 gridW = width()/10; //窗口宽度分10份 22 gridH = height()/10; //窗口高度分10份 23 24 //棋盘起点坐标 25 startX = gridW; 26 startY = gridH; 27 28 QPainter p(this); //创建画家,指定窗口为绘图设备 29 30 //背景图 31 p.drawPixmap(rect(), QPixmap(":/new/prefix1/Image/bk.jpg")); 32 33 //设置画笔 34 QPen pen; 35 pen.setWidth(4); //线宽 36 p.setPen(pen); //将画笔交给画家 37 38 //取中间8份画棋盘 39 for(int i = 0; i <= 8; i++) 40 { 41 //横线 42 p.drawLine(startX, startY+i*gridH, startX+8*gridW, startY+i*gridH); 43 44 //竖线 45 p.drawLine(startX+i*gridW, startY, startX+i*gridW, startY+8*gridH); 46 } 47 48 //画棋子 49 if(chessX != -1 && chessY != -1) 50 { 51 p.drawPixmap(startX+chessX*gridW, startY+chessY*gridH, 52 gridW, gridH, 53 QPixmap(":/new/prefix1/Image/face.png") 54 ); 55 } 56 } 57 58 void ChessWidget::mousePressEvent(QMouseEvent *e) 59 { 60 //获取点击的坐标 61 int x = e->x(); 62 int y = e->y(); 63 64 // 要保证点击点在棋盘范围里面 65 if(x >= startX && x <= startX+8*gridW 66 && y >= startY && y <= startX+8*gridH) 67 { 68 // 棋盘的位置转换转换为坐标下标值 69 // 类似于a[i][j]的i和j 70 chessX = (x - startX)/gridW; 71 chessY = (y - startY)/gridH; 72 qDebug() << chessX << chessY; 73 74 //更新窗口,间接调用paintEvent() 75 update(); 76 } 77 }