Qt 5 C++ – File và thư mục

5/5 - (2 votes)

Trong phần này chúng ta sẽ học về cách làm việc với file và thư mục.

QFile, QDir, và QFileInfo là các lớp chuyên làm việc với file trong Qt5. QFile cho phép bạn đọc và ghi file, QDir cho phép bạn truy cập và xem nội dung thư mục. QFileInfo cung cấp các thông tin về file độc lập với hệ thống, bao gồm tên file, đường dẫn thư mục, thời gian chỉnh sửa lần cuối, quyền đọc ghi hay quyền sở hữu.

Kích thước file

Ví dụ dưới đây lấy kích thước của một file

#include <QTextStream>
#include <QFileInfo>

int main(int argc, char *argv[]) {
    
  QTextStream out(stdout);

  if (argc != 2) {
      
    qWarning("Usage: file_size file");
    return 1;
  }
  
  QString filename = argv[1];
  
  if (!QFile(filename).exists()) {
  
    qWarning("The file does not exist");
    return 1;
  }  
  
  QFileInfo fileinfo(filename);
  
  qint64 size = fileinfo.size();
  
  QString str = "The size is: %1 bytes";
  
  out << str.arg(size) << endl;
}

Kích thước file có thể truy xuất thông qua phương thức size().

QString filename = argv[1];

Chúng ta lấy tên file qua tham số truyền vào chương trình.

if (!QFile(filename).exists()) {
  qWarning("The file does not exist");
  return 1;
}

Kiểm tra xem file đã tồn tại hay chưa bằng phương thức exists(), nếu chưa tồn tại thì cho in thông báo và thoát chương trình.

QString str = "The size is: %1 bytes";

out << str.arg(size) << endl;

In kết quả ra màn hình.

The size is: 28029 bytes

Đọc file trong Qt

Muốn đọc file thì trước tiên phải mở file đó ra đã. Các phương thức mở file sẽ trả về một stream để đọc file, nếu đã từng lập trình với Java hay C# thì có lẽ bạn đã từng được học về khái niệm stream, còn nếu chưa thì bạn cứ hiểu stream giống như std::cin hay std::cout trong thư viện <iostream> của C++ ấy, chỉ khác là trong C++ thường thì cincout mặc định làm việc với màn hình và bàn phím, còn trong Qt thì làm việc với file.

#include <QTextStream>
#include <QFile>

int main(void) {
    
  QTextStream out(stdout);

  QFile file("colours");
  
  if (!file.open(QIODevice::ReadOnly)) {
      
    qWarning("Cannot open file for reading");
    return 1;
  }

  QTextStream in(&file);

  while (!in.atEnd()) {
      
    QString line = in.readLine();    
    out << line << endl;
  }

  file.close();
}

Ví dụ trên đọc dữ liệu từ file colours. Trong đó chứa tám string.

if (!file.open(QIODevice::ReadOnly)) {
  qWarning("Cannot open file for reading");
  return 1;
}

Phương thức open() sẽ mở file ở chế độc read-only. Nếu giá trị trả về false thì ta báo không mở file được và cho thoát chương trình.

QTextStream in(&file);

Tạo một đối tượng thuộc lớp QTextStream nhận vào đối tượng QFile. Đây chính là một stream và ta sẽ dùng nó để đọc file.

while (!in.atEnd()) {
    
  QString line = in.readLine();    
  out << line << endl;
}

Trong vòng lặp while, chúng ta sẽ đọc file theo từng dòng cho đến hết file bằng phương thức readLine(), mỗi dòng trong file sẽ được lưu vô một biến QString rồi in ra màn hình. Phương thức atEnd() kiểm tra xem đã hết file hay chưa.

file.close();

Khi đã đọc file xong ta dùng phương thức close() để đóng file.

red
green
blue
yellow
brown
white
black
violet

Ghi file trong Qt

Để ghi file thì cũng tương tự như đọc file, phải mở được file đó trước. Và cũng như đọc file, chúng ta sẽ có một stream riêng để ghi file.

#include <QTextStream>
#include <QFile>

int main(void) {
    
  QTextStream out(stdout);
    
  QString filename = "distros";
  QFile file(filename);
  
  if (file.open(QIODevice::WriteOnly)) {
      
    QTextStream out(&file);
    out << "Xubuntu" << endl;
    out << "Arch" << endl;
    out << "Debian" << endl;
    out << "Redhat" << endl;
    out << "Slackware" << endl;
    
  } else {
      
    qWarning("Could not open file");
  }  
  
  file.close(); 
}

Ví dụ trên ghi tên 5 phiên bản của hệ điều hành họ Linux vào một file tên là distros.

QString filename = "distros";
QFile file(filename);

Tạo một đối tượng QFile với tên file.

if (file.open(QIODevice::WriteOnly))

Mở file ở chế độ WriteOnly với phương thức open().

QTextStream out(&file);

Dòng code trên tạo ra một đối tượng QTextStream để ghi file.

out << "Xubuntu" << endl;
out << "Arch" << endl;
out << "Debian" << endl;
out << "Redhat" << endl;
out << "Slackware" << endl;

Dùng toán tử << để ghi dữ liệu.

file.close(); 

Cuối cùng đóng file lại.

Xubuntu
Arch
Debian
Redhat
Slackware

Copy một file trong Qt

Xem ví dụ để biết cách copy file.

#include <QTextStream>
#include <QFile>

int main(int argc, char *argv[]) {
    
  QTextStream out(stdout);
  
  if (argc != 3) {
      
      qWarning("Usage: copyfile source destination");
      return 1;
  }
  
  QString source = argv[1];
  
  if (!QFile(source).exists()) {
      qWarning("The source file does not exist");
      return 1;
  }
  
  QString destin(argv[2]);

  QFile::copy(source, destin);
}

Qt cung cấp phương thức QFile::copy() để copy một file, tham số gồm file gốc và file đích sẽ được tạo ra.

if (argc != 3) {
    
    qWarning("Usage: copyfile source destination");
    return 1;
}

Ở đây mình truyền vào chương trình 2 tham số là tên file gốc và tên file đích. Nếu không đủ cả 2 thì ta báo lỗi và thoát chương trình.

QString source = argv[1];

Lấy tên file gốc từ tham số đầu tiên.

if (!QFile(source).exists()) {
    qWarning("The source file does not exist");
    return 1;
}

Kiểm tra xem file gốc có tồn tại hay không, nếu không thì in ra thông báo lỗi và thoát chương trình.

QString destin(argv[2]);

Lấy file đích.

QFile::copy(source, destin);

Dùng phương thức QFile::copy() để copy file gốc sang file đích.

Làm việc với thư mục trong Qt

Lớp QDir được sinh ra để làm việc này. Xem ví dụ.

#include <QTextStream>
#include <QDir>

int main(void) {
    
  QTextStream out(stdout);
  QDir dir;
  
  if (dir.mkdir("mydir")) {
    out << "mydir successfully created" << endl;
  }
  
  dir.mkdir("mydir2");
  
  if (dir.exists("mydir2")) {
    dir.rename("mydir2", "newdir");    
  }
  
  dir.mkpath("temp/newdir");
}

Ở trên mình trình bày bốn phương thức thường dùng của lớp này.

if (dir.mkdir("mydir")) {
  out << "mydir successfully created" << endl;
}

Phương thức mkdir() tạo ra một thư mục. Trả về true nếu tạo thành công.

if (dir.exists("mydir2")) {
  dir.rename("mydir2", "newdir");    
}

Phương thức exists() kiểm tra xem một thư mục đã tồn tại hay chưa. Phương thức rename() đổi tên một thư mục.

dir.mkpath("temp/newdir");

Phương thức mkpath() tạo ra một thư mục và tạo cả các thư mục cha của nó nếu chưa có.

Một số hằng số về các thư mục đặc biệt

Trong hệ thống có một số thư mục đặc biệt, chẳng hạn như thư mục home hay thư mục gốc. Xem ví dụ.

#include <QTextStream>
#include <QDir>

int main(void) {
    
  QTextStream out(stdout);
   
  out << "Current path:" << QDir::currentPath() << endl;
  out << "Home path:" << QDir::homePath() << endl;
  out << "Temporary path:" << QDir::tempPath() << endl;
  out << "Rooth path:" << QDir::rootPath() << endl;
}

Ví dụ trên in ra bốn thư mục đặc biệt

out << "Current path:" << QDir::currentPath() << endl;

Phương thức QDir::currentPath() trả về thư mục mà mình đang làm việc chẳng hạn ở trên mình đang làm việc với file special_paths.cpp thì phương thức này sẽ in ra đường dẫn đến file này.

out << "Home path:" << QDir::homePath() << endl;

Phương thức QDir::homePath() trả về đường dẫn đến thư mục user của bạn.

out << "Temporary path:" << QDir::tempPath() << endl;

Phương thức QDir::tempPath() trả về đường dẫn đến thư mục temp của user của bạn.

out << "Rooth path:" << QDir::rootPath() << endl;

Phương thức QDir::rootPath() trả về đường dẫn đến thư mục gốc của bạn, nếu bạn dùng Windows thì thường nó là thư mục cài win, nếu dùng linux thì là dấu /.

Đường dẫn file

Xem ví dụ.

#include <QTextStream>
#include <QFileInfo>

int main(int argc, char *argv[]) {
    
  QTextStream out(stdout);

  if (argc != 2) {
      
      out << "Usage: file_times file" << endl;
      return 1;
  }
  
  QString filename = argv[1];
  
  QFileInfo fileinfo(filename);
  
  QString absPath = fileinfo.absoluteFilePath();
  QString baseName = fileinfo.baseName();
  QString compBaseName = fileinfo.completeBaseName();
  QString fileName = fileinfo.fileName();
  QString suffix = fileinfo.suffix();
  QString compSuffix = fileinfo.completeSuffix();
  
  out << "Absolute file path: " << absPath << endl;
  out << "Base name: " << baseName << endl;
  out << "Complete base name: " << compBaseName << endl;
  out << "File name: " << fileName << endl;
  out << "Suffix: " << suffix << endl;
  out << "Whole suffix: " << compSuffix << endl;
}

Trong ví dụ trên ta in ra đường dẫn của một file và trính xuất một vài thành phần trong đường dẫn đó.

QFileInfo fileinfo(filename);

Dùng lớp QFileInfo để làm việc này.

QString absPath = fileinfo.absoluteFilePath();

Phương thức absoluteFilePath() trả về đường dẫn đầy đủ của file.

QString baseName = fileinfo.baseName();

Phương thức baseName() trả về tên file, không lấy phần mở rộng của file, không lấy đường dẫn thư mục.

QString compBaseName = fileinfo.completeBaseName();

Phương thức completeBaseName() cũng giống với phương thức baseName().

QString fileName = fileinfo.fileName();

Phương thức fileName() trả về tên file và cả phần mở rộng của file.

QString suffix = fileinfo.suffix();

Phương thức suffix() trả về phần mở rộng của file.

QString compSuffix = fileinfo.completeSuffix();

Phương thức completeSuffix() cũng giống với phương thức suffix().

Liệt kê nội dung thư mục trong Qt

Ví dụ dưới đây liệt kê nội dung của một thư mục.

#include <QTextStream>
#include <QFileInfo>
#include <QDir>

int main(int argc, char *argv[]) {
    
  QTextStream out(stdout);

  if (argc != 2) {
      
      qWarning("Usage: list_dir directory");
      return 1;
  }
  
  QString directory = argv[1];
  
  QDir dir(directory);
  
  if (!dir.exists()) {
      qWarning("The directory does not exist");
      return 1;
  }  
  
  dir.setFilter(QDir::Files | QDir::AllDirs);
  dir.setSorting(QDir::Size | QDir::Reversed);

  QFileInfoList list = dir.entryInfoList();
  
  int max_size = 0;
  
  foreach (QFileInfo finfo, list) {
      
      QString name = finfo.fileName();
      int size = name.size();
      
      if (size > max_size) {
          
          max_size = size;
      }
  }
  
  int len = max_size + 2;
  
  out << QString("Filename").leftJustified(len).append("Bytes") << endl;
  
  for (int i = 0; i < list.size(); ++i) {
      
    QFileInfo fileInfo = list.at(i);
    QString str = fileInfo.fileName().leftJustified(len);
    str.append(QString("%1").arg(fileInfo.size()));
    out << str << endl;
  }
    
  return 0;
}

Dùng phương thức entryInfoList() trong lớp QDir để làm việc này. Trong code ví dụ trên chúng ta in ra danh sách đã được sắp xếp theo kích thước của file/thư mục. Chúng ta in ra hai cột, cột đầu tiên là tên file/thư mục, cột thứ hai là kích thước file/thư mục.

QDir dir(directory);

Tạo ra một đối tượng QDir.

dir.setFilter(QDir::Files | QDir::AllDirs);

Phương thức setFilter() có tác dụng lọc các thành phần mà mình muốn liệt kê, trong ví dụ mình lọc ra cả file và thư mục.

dir.setSorting(QDir::Size | QDir::Reversed);

Phương thức setSorting() sẽ tự động sắp xếp các thành phần trong thư mục dựa theo tham số truyền vào mà ở đây là sắp xếp đảo ngược theo kích thước.

QFileInfoList list = dir.entryInfoList();

Phương thức entryInfoList() sẽ trả về một đối tượng thuộc lớp QFileInfoList, lớp này là một danh sách các đối tượng QFileInfo đã được sắp xếp. Lớp này tương tự với QList<QFileInfo>.

foreach (QFileInfo finfo, list) {
    
    QString name = finfo.fileName();
    int size = name.size();
    
    if (size > max_size) {
        
        max_size = size;
    }
}

Duyệt qua danh sách và tìm ra file có độ dài lớn nhất. Mình lấy thông tin này ra để lát sau in kết quả ra màn hình thì có thể ngăn cách hai cột cho đẹp.

int len = max_size + 2;

Chúng ta cũng không dùng chính xác độ dài lớn nhất mà cộng thêm 2 vào nữa.

for (int i = 0; i < list.size(); ++i) {
  QFileInfo fileInfo = list.at(i);
  QString str = fileInfo.fileName().leftJustified(len);
  str.append(QString("%1").arg(fileInfo.size()));
  out << str << endl;
}

Cuối cùng ta duyệt qua toàn bộ danh sách và in kết quả ra màn hình.

Filename      Bytes
list_dir.pro  291
list_dir.cpp  1092
..            4096
.             4096
list_dir.o    10440
list_dir      19075
Makefile      28369

Trong bài này, bạn đã được học cách làm việc với file và thư mục trong Qt.

0 0 votes
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

12 Comments
Inline Feedbacks
View all comments
orric
orric
7 năm trước

hi pro. mình có trường hợp này muốn hỏi bạn. mình có 1 thư mục là A và 1 thư mục là B. trong A có nhiều thư mục con là X,Y,Z và trong mỗi thư mục con có các file text.txt(file gi cũng dc). khi mình copy thu mục A vào thư mục B và đồng thời đổi tên thư mục A thành B-A. lúc này mình bị lỗi là : nó tạo ra 1 thư mục mới là B-A và thư mục cũ A vẫn còn . bạn có thể sử lý trường hợp này được không. thank… Đọc thêm »

Vũ Hoàng Văn
Vũ Hoàng Văn
6 năm trước

Cho mình hỏi file và thư mục tương tác với QT thì phải để ở đâu vậy ạ?

Quy
Quy
3 năm trước

Cho mình hỏi với

File text cần đọc mình để ở đường dẫn cùng với project thì đọc được
Nhưng giờ mình muốn đọc file ở ổ D chẳng hạn, thì nó lại không đọc được
VD : QFile inputFile(“D://‪FAT32.txt”);

Chỉ giúp mình cách với !

Thanks !

Quy
Quy
3 năm trước
Reply to  Phở Code

Thanks Phocode !

Mình đã thử đủ mọi cách (//, \, /, \ ) đều không được, có khi nào ở phần cài đặt nó luôn mặc định là đọc file ở đường dẫn ứng dụng không nhỉ
Mình cũng thêm lệnh QDir::setCurrent để set cũng không được

Phocode xem giúp mình với, rất cần

Many thanks !

Quy
Quy
3 năm trước
Reply to  Phở Code

//QFile inputFile(“/FAT32.txt”); // đọc tại đường dẫn cùng ứng dụng (đã test ok)

QFile inputFile("‪D:/FAT32.txt"); // đọc tại đường dẫn bên ngoài (không đọc được)

if (inputFile.open(QIODevice::ReadOnly))
{
ui->textEdit->setText("line1");
QTextStream in(&inputFile);
while (!in.atEnd())
{
QString line = in.readLine();
ui->textEdit->setText(line);
}
inputFile.close();
}

Mình kiểm tra kỹ rồi, file đã đóng hết, đã reset máy tính, nhưng vẫn không đọc được file ở đường dẫn ngoài, hihi, vụ này căng quá

Quy
Quy
3 năm trước
Reply to  Phở Code

Mình thử được rồi, do chương trình không update lệnh và cũng không báo lỗi nên mình không phát hiện ra

Cảm ơn Phocode nhiều nhé !

Quang
Quang
2 năm trước

Hi, nếu muốn read write data vào cùng 1 file thì dùng lệnh thế nào ad