This chapter reveals how to structure I/O so the code remains unified for files, memory, and even processes. You’ll discover a non-obvious approach: working not with “files,” but with a device abstraction — thereby accelerating development, improving reliability, and simplifying testing. Professional developers use this style because it provides a clear “before/after” contrast: fewer conventions, more code reuse.
Inside — 3 modes of thinking for QIODevice (read/write/positioning), practical techniques with QDir + filters, and safer deletion via QFile::moveToTrash() in Qt6. Plus — ready-made architecture for monitoring changes via QFileSystemWatcher, so the interface updates asynchronously and without blocking.
If these techniques aren’t applied now — the next “trivial” file operation will eat up another evening.
This chapter includes ready-to-use code examples.
Chapter Self-Check
Why is the QIODevice class abstract, and which methods must be implemented when creating a custom I/O device?Answer
Correct answer: QIODevice is abstract to provide a unified interface for all I/O devices. When inheriting, you must implement readData() and writeData(), and typically also open(), close(), and atEnd().
Why is it necessary to explicitly call the setVersion() method in the QDataStream class before writing data?Answer
Correct answer: The QDataStream format has changed between Qt versions. Explicitly specifying the version ensures that data written in one Qt version can be correctly read in another version.
What’s the fundamental difference between QTextStream and QDataStream, and when should each be used?Answer
Correct answer: QTextStream is designed for Unicode text data and converts numbers to text; QDataStream is for binary data in a platform-independent format. Use QTextStream for text files, QDataStream for object serialization and network exchange.
Why should the readAll() method be used with caution, and how can memory consumption be reduced when using it?Answer
Correct answer: readAll() loads the entire file into memory, which can cause problems with large files. Memory consumption can be reduced by applying qCompress() and qUncompress() functions to the QByteArray data if the file contains redundant information.
Why is QApplication::processEvents() called inside the start() method in the recursive file search example?Answer
Correct answer: File searching can be lengthy and executes in the main thread, causing GUI “freezing.” processEvents() allows processing accumulated events and maintaining interface responsiveness.
What’s the advantage of using QFile::moveToTrash() in Qt6 instead of remove()?Answer
Correct answer: moveToTrash() moves the file to the OS trash bin, providing the possibility of recovery, while remove() deletes the file permanently.
What’s the practical benefit of the QBuffer class, and in which scenarios should it be applied?Answer
Correct answer: QBuffer emulates a file in RAM, which is significantly faster than disk operations. Useful for caching bitmap images, temporary data storage, and testing I/O code without creating real files.
Why should QStandardPaths be used to obtain paths to user directories instead of hardcoded strings?Answer
Correct answer: Paths to standard directories differ between platforms and can change. QStandardPaths provides platform-independent and correct access to documents, settings, cache, and other directories.
What will happen if you open QFile in QIODevice::Truncate mode?Answer
Correct answer: All existing file data will be deleted upon opening, the file will become empty and ready for writing new data from the beginning.
How does the QFileSystemWatcher class notify about changes, and why doesn’t this block the main thread?Answer
Correct answer: QFileSystemWatcher uses fileChanged() and directoryChanged() signals for asynchronous notifications. Monitoring occurs in the background without blocking the main application thread.
What will happen when attempting to use seek() and pos() methods for a network connection (QAbstractSocket)?Answer
Correct answer: These methods lose their meaning for sequential access (like network connections), as they’re only applicable for random access — QFile, QBuffer, and QTemporaryFile.
Why use the QDir::NoDotAndDotDot flag instead of explicit checking for “.” and “..”?Answer
Correct answer: The flag makes code cleaner, clearer, and less error-prone, automatically excluding special directories from the list when calling entryList().
What’s the advantage of using QTemporaryFile over manually creating a temporary file?Answer
Correct answer: QTemporaryFile automatically generates a unique name (avoiding conflicts), places the file in the correct temporary directory, and automatically deletes the file when the object is destroyed.
What will happen if you write data with QDataStream using Qt_6_9 version and read it without specifying a version?Answer
Correct answer: Data may be read incorrectly or not at all, since QDataStream will use the default format, which may differ from the writing format.
Practice Assignments
Easy Level
File Information Viewer
Create a Qt application with a text field for entering a file path and a “Show Information” button. When the button is pressed, the application should display: file name, extension, size in bytes (and in human-readable format), creation date, last modification date, and check whether the file is executable, hidden, readable, and writable.
Hints: Use QFileInfo to get all file information. The exists() method helps check file existence. For size conversion, use a function from the chapter example or implement your own. The created(), lastModified() methods return QDateTime, which can be converted to a string via toString().
Medium Level
Text Editor with Auto-Save
Develop a simple text editor with QTextEdit that automatically saves content to a file every 30 seconds (use QTimer). Implement a menu with “Open”, “Save As”, “Export to Uppercase” items. When opening a file, use QTextStream for reading. Add a status bar displaying the time of last save and current document size.
Hints: QTimer with a 30000 ms interval for auto-save. Use QTextStream for reading/writing text files. QFileDialog::getOpenFileName() and getSaveFileName() for dialogs. The toPlainText() method of QTextEdit gives all text. QString::toUpper() for conversion. QStatusBar for displaying status.
Hard Level
Backup Manager with Monitoring
Create an application for backing up a selected directory. Use QFileSystemWatcher to track changes in the source directory. When changes are detected, automatically copy changed files to the backup directory. Implement file filtering by mask (e.g., only *.cpp and *.h). Add the ability to compress backups via qCompress() and save to QDataStream. Display an operation log in QTextEdit with timestamps. Provide settings: selecting source and target directories, enabling/disabling automatic copying, file mask.
Hints: Use QFileSystemWatcher::addPath() for monitoring. The fileChanged() signal for tracking changes. QDir::entryList() with filters for selecting files by mask. QFile::copy() for copying or a read()/write() combination for compressed copies. QDataStream with qCompress()/qUncompress() for compression. QDateTime::currentDateTime().toString() for timestamps. QSettings for saving settings between runs.
💬 Join the Discussion!
Got a handle on the QIODevice hierarchy and differences between streams? Have questions about when to use QTextStream versus QDataStream?
Share your experience working with files and directories in Qt, tell us about unconventional solutions, or ask questions about the chapter material. Perhaps you have interesting use cases for QFileSystemWatcher or QBuffer?