File indexing completed on 2024-04-21 14:44:00
0001 /* 0002 SPDX-FileCopyrightText: 2007 James B. Bowlin <bowlin@mindspring.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include <QFile> 0010 #include <QObject> 0011 #include <QTextStream> 0012 0013 class QString; 0014 0015 /** 0016 * @class KSFileReader 0017 * I totally rewrote this because the earlier scheme of reading all the lines of 0018 * a file into a buffer before processing is actually extremely inefficient 0019 * because it makes it impossible to interleave file reading and data processing 0020 * which all modern computers can do. It also force large data files to be 0021 * split up into many smaller files which made dealing with the data much more 0022 * difficult and made the code to read in the data needlessly complicated. A 0023 * simple subclassing of QTextStream fixed all of the above problems (IMO). 0024 * 0025 * I had added periodic progress reports based on line number to several parts 0026 * of the code that read in large files. So I combined that duplicated code 0027 * into one place which was naturally here. The progress code causes almost 0028 * nothing extra to take place when reading a file that does not use it. The 0029 * only thing extra is incrementing the line number. For files that you want to 0030 * emit periodic progress reports, you call setProgress() once setting up the 0031 * message to display and the intervals the message will be emitted. Then 0032 * inside the loop you call showProgress(). This is an extra call in the read 0033 * loop but it just does an integer compare and almost always returns. We could 0034 * inline it to reduce the call overhead but I don't think it makes a bit of 0035 * difference considering all of the processing that takes place when we read in 0036 * a line from a data file. 0037 * 0038 * NOTE: We no longer close the file like the previous version did. I've 0039 * changed the code where this was assumed. 0040 * 0041 * There are two ways to use this class. One is pass in a QFile& in the 0042 * constructor which is included only for backward compatibility. The preferred 0043 * way is to just instantiate KSFileReader with no parameters and then use the 0044 * open( QString fname ) method to let this class handle the file opening which 0045 * helps take unneeded complexity out of the calling classes. I didn't make a 0046 * constructor with the filename in it because we need to be able to inform the 0047 * caller of an error opening the file, hence the bool open(filename) method. 0048 * 0049 * 0050 * -- James B. Bowlin 0051 */ 0052 0053 class KSFileReader : public QObject, public QTextStream 0054 { 0055 Q_OBJECT 0056 0057 public: 0058 /** 0059 * @short this is the preferred constructor. You can then use 0060 * the open() method to let this class open the file for you. 0061 */ 0062 explicit KSFileReader(qint64 maxLen = 1024); 0063 0064 /** 0065 * Constructor 0066 * @param file is a previously opened (for reading) file. 0067 * @param maxLen sets the maximum line length before wrapping. Setting 0068 * this parameter should help efficiency. The little max-length.pl 0069 * script will tell you the maximum line length of files. 0070 */ 0071 explicit KSFileReader(QFile &file, qint64 maxLen = 1024); 0072 0073 /** 0074 * @short opens the file fname from the QStandardPaths::AppLocalDataLocation directory and uses that 0075 * file for the QTextStream. 0076 * 0077 * @param fname the name of the file to open 0078 * @return returns true on success. Prints an error message and returns 0079 * false on failure. 0080 */ 0081 bool open(const QString &fname); 0082 0083 /** 0084 * @short opens the file with full path fname and uses that 0085 * file for the QTextStream. open() locates QStandardPaths::AppLocalDataLocation behind the scenes, 0086 * so passing fname such that 0087 * QString fname = KSPaths::locate(QStandardPaths::AppLocalDataLocation, "file_name" ); 0088 * is equivalent 0089 * 0090 * @param fname full path to directory + name of the file to open 0091 * @return returns true on success. Prints an error message and returns 0092 * false on failure. 0093 */ 0094 bool openFullPath(const QString &fname); 0095 0096 /** 0097 * @return true if we are not yet at the end of the file. 0098 * (I added this to be compatible with existing code.) 0099 */ 0100 bool hasMoreLines() const { return !QTextStream::atEnd(); } 0101 0102 /** 0103 * @short increments the line number and returns the next line from the file as a QString. 0104 */ 0105 inline QString readLine() 0106 { 0107 m_curLine++; 0108 return QTextStream::readLine(m_maxLen); 0109 } 0110 0111 /** @short returns the current line number */ 0112 int lineNumber() const { return m_curLine; } 0113 0114 /** 0115 * @short Prepares this instance to emit progress reports on how much 0116 * of the file has been read (in percent). 0117 * @param label the label 0118 * @param lastLine the number of lines to be read 0119 * @param numUpdates the number of progress reports to send 0120 */ 0121 void setProgress(QString label, unsigned int lastLine, unsigned int numUpdates = 10); 0122 0123 /** 0124 * @short emits progress reports when required and updates bookkeeping 0125 * for when to send the next report. This might seem slow at first 0126 * glance but almost all the time we are just doing an integer compare 0127 * and returning. If you are worried about speed we can inline it. 0128 * It could also safely be included in the readLine() method since 0129 * m_targetLine is set to MAXUINT in the constructor. 0130 */ 0131 void showProgress(); 0132 0133 signals: 0134 void progressText(const QString &message); 0135 0136 private: 0137 QFile m_file; 0138 qint64 m_maxLen; 0139 unsigned int m_curLine { 0 }; 0140 0141 unsigned int m_totalLines { 0 }; 0142 unsigned int m_targetLine { UINT_MAX }; 0143 unsigned int m_targetIncrement { 0 }; 0144 QString m_label; 0145 };