Read the last N lines of a file without loading the entire thing.
| 69 | |
| 70 | // Read the last N lines of a file without loading the entire thing. |
| 71 | static QStringList tailFile(const QString& path, int maxLines = 500) |
| 72 | { |
| 73 | QFile f(path); |
| 74 | if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) |
| 75 | return {}; |
| 76 | |
| 77 | // For small files, just read everything |
| 78 | if (f.size() < 64 * 1024) { |
| 79 | QStringList lines; |
| 80 | while (!f.atEnd()) { |
| 81 | QString line = QString::fromUtf8(f.readLine()).trimmed(); |
| 82 | if (!line.isEmpty()) |
| 83 | lines.append(line); |
| 84 | } |
| 85 | if (lines.size() > maxLines) |
| 86 | lines = lines.mid(lines.size() - maxLines); |
| 87 | return lines; |
| 88 | } |
| 89 | |
| 90 | // For large files, seek backwards to find enough newlines |
| 91 | constexpr qint64 CHUNK = 8192; |
| 92 | qint64 pos = f.size(); |
| 93 | QByteArray tail; |
| 94 | int nlCount = 0; |
| 95 | while (pos > 0 && nlCount <= maxLines) { |
| 96 | qint64 readSize = qMin(CHUNK, pos); |
| 97 | pos -= readSize; |
| 98 | f.seek(pos); |
| 99 | QByteArray chunk = f.read(readSize); |
| 100 | tail.prepend(chunk); |
| 101 | nlCount += chunk.count('\n'); |
| 102 | } |
| 103 | QStringList all = QString::fromUtf8(tail).split('\n', Qt::SkipEmptyParts); |
| 104 | for (auto& s : all) s = s.trimmed(); |
| 105 | all.removeAll(QString()); |
| 106 | if (all.size() > maxLines) |
| 107 | all = all.mid(all.size() - maxLines); |
| 108 | return all; |
| 109 | } |
| 110 | |
| 111 | // ── SpotTableModel ────────────────────────────────────────────────────────── |
| 112 |