#include "mainwindow.h"
#include <QFileDialog>
#include <QFile>
#include <QMessageBox>
#include <QHeaderView>
#include <QDebug>
#include <QRegularExpression>
#include <QSettings>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
centralWidget = new QWidget(this);
mainLayout = new QVBoxLayout(centralWidget);
treeWidget = new QTreeWidget(this);
treeWidget->setHeaderLabels(QStringList() << "分类" << "IR_R" << "IR_T");
//treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
treeWidget->setColumnWidth(0,140);
treeWidget->setColumnWidth(1,100);
treeWidget->setColumnWidth(2,100);
treeWidget->setStyleSheet("background-color:white;color:black;font-size:15px;");
browseButton = new QPushButton("选择BIN文件",this);
browseButton->setStyleSheet("background-color:white;color:black;font-size:15px;");
browseButton->setMinimumHeight(40);
mainLayout->addWidget(browseButton);
mainLayout->addWidget(treeWidget);
setCentralWidget(centralWidget);
connect(browseButton,&QPushButton::clicked,this,&MainWindow::onBrowseButtonClicked);
setWindowTitle("BIN文件查看器");
resize(800,600);
}
MainWindow::~MainWindow()
{
}
void MainWindow::onBrowseButtonClicked()
{
QSettings settings("./Setting.ini", QSettings::IniFormat);
QString lastPath = settings.value("LastFilePath").toString(); //获取上次的打开路径
QString filePath = QFileDialog::getOpenFileName(this,"选择SIZE文件",lastPath);
if (!filePath.isEmpty()) {
settings.setValue("lastFilePath", filePath);
}
if(!filePath.isEmpty())
{
//从文件路径提取币种信息
QRegularExpression re("CISW1_(\\w{3})\\.bin$");
QRegularExpressionMatch match = re.match(filePath);
if(match.hasMatch())
{
m_currency = match.captured(1);
}
//获取对应的文件夹路径
QFileInfo fileInfo(filePath);
m_folderPath = fileInfo.absolutePath();
parseBinFile(filePath);
displayData();
}
}
//处理BIN文件
void MainWindow::parseBinFile(const QString &filePath)
{
//清空数据存储
m_data.clear();
m_folderIndex.clear(); //清空索引
buildFolderIndex(); //构建文件夹索引
QFile file(filePath);
if(!file.open(QIODevice::ReadOnly))
{
QMessageBox::warning(this,"错误","无法打开文件");
return;
}
//读取全部数据存入QByteArray对象
QByteArray data = file.readAll();
file.close();
//查找"SIZE"第一次出现的位置(53 49 5A 45)
const char sizeMarker[] = {0x53, 0x49, 0x5A, 0x45};
int startPos = data.indexOf(sizeMarker);
if(startPos == -1)
{
QMessageBox::warning(this,"错误","未找到SIZE标记");
return;
}
//从SIZE标记后开始解析数据
const uchar* fileData = reinterpret_cast<const uchar*>(data.constData() + startPos + 64);
const int fileSize = data.size() - startPos - 64;
const int recordSize = 16;
const int maxRecords = fileSize/recordSize;
//标志是否已经读取到有效数据的末尾
bool endOfVaildData = false;
//记录连续全零记录的数量
int consecutiveZeroRecords = 0;
const int maxConsecutiveZeroRecords = 12; //允许的最大的连续记录为0的行数,一般最多是三组,也就是十二个为0
for(int i = 1;i < maxRecords && !endOfVaildData; ++i)
{
//记录第一次size标记后面的每一行
const uchar* record = fileData + i * recordSize;
//检查在没超过文件大小的情况下是否遇到新的SIZE标记
if(i*recordSize + 4 <= fileSize && memcmp(record,sizeMarker,4) == 0)
{
endOfVaildData = true;
break;
}
//检查记录是否全零
bool isZeroRecord = true;
for(int j = 0;j < recordSize;++j)
{
if(record[j] != 0)
{
isZeroRecord = false;
break;
}
}
if(isZeroRecord)
{
consecutiveZeroRecords++;
//如果连续遇到太多全零记录,就认为是到达有效数据末尾
if(consecutiveZeroRecords >= maxConsecutiveZeroRecords)
{
endOfVaildData = true;
break;
}
//continue;
}
else
{
consecutiveZeroRecords = 0; //重置计数器
}
//解析关键字段
int ir_r = static_cast<int>(record[5]);
int ir_t = static_cast<int>(record[6]);
int irts = record[7];
//使用索引获取文件夹信息
if(m_folderIndex.contains(i))
{
FolderInfo info = m_folderIndex[i];
//存储数据
m_data[info.denomination][info.year][info.version] = {ir_r, ir_t, irts};
}else
{
qDebug() << "警告: 记录" << i << "没有对应的文件夹索引";
}
}
}
void MainWindow::buildFolderIndex()
{
m_folderIndex.clear();
int recordCounter = 1; //从1开始,跳过全局记录
QDir dir(m_folderPath);
QStringList types = {"white", "IR_T", "IR_R"};
foreach (const QString &type, types)
{
QDir typeDir(m_folderPath + "/" + type);
if(!typeDir.exists()) continue;
//获取面额文件夹并排序
QStringList denomDirs = typeDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
std::sort(denomDirs.begin(), denomDirs.end(), [](const QString &a, const QString &b) {
return a.toInt() > b.toInt(); //降序排序
});
foreach (const QString &denom, denomDirs)
{
QDir denomDir(typeDir.path() + "/" + denom);
//获取年份文件夹并排序
QStringList yearDirs = denomDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
std::sort(yearDirs.begin(), yearDirs.end(), [](const QString &a, const QString &b) {
return a.toInt() > b.toInt(); //降序排序
});
foreach(const QString &year, yearDirs)
{
QDir yearDir(denomDir.path() + "/" + year);
//获取版本文件夹
QStringList versionDirs = yearDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach(const QString &version, versionDirs)
{
//为每个版本分配1个记录索引(移除for循环)
if(recordCounter < 1024)
{
//防止溢出
m_folderIndex[recordCounter++] = {
denom, year, version, type
};
}
}
}
}
}
}
QString MainWindow::getFolderPathForRecord(int recordIndex)
{
//计算组索引(每组4个记录对应A/B/C/D版本)
int groupIndex = (recordIndex - 1) / 4;
//遍历文件夹结构查找对应的路径
QDir dir(m_folderPath);
QStringList folders;
//搜索white、IR_T、IR_R子文件夹
QStringList types = {"white", "IR_T", "IR_R"};
foreach(const QString &type, types)
{
QDir typeDir(m_folderPath + "/" + type);
if(typeDir.exists())
{
//搜索面额文件夹
QStringList denomDirs = typeDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach(const QString &denom, denomDirs)
{
QDir denomDir(typeDir.path() + "/" + denom);
//搜索年份文件夹
QStringList yearDirs = denomDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach(const QString &year, yearDirs)
{
QDir yearDir(denomDir.path() + "/" + year);
//搜索版本文件夹(A/B/C/D)
QStringList versionDirs = yearDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach(const QString &version, versionDirs)
{
//检查是否是我们需要的记录
if(--groupIndex < 0)
{
return typeDir.path() + "/" + denom + "/" + year + "/" + version;
}
}
}
}
}
}
return QString();
}
void MainWindow::displayData()
{
treeWidget->clear();
//获取排序后的面额列表(降序)
QStringList denominations = m_data.keys();
std::sort(denominations.begin(), denominations.end(), [](const QString &a, const QString &b) {
return a.toInt() > b.toInt();
});
//按面额分类
foreach(const QString &denomination, denominations)
{
//跳过面额为10000000的记录
if(denomination == "10000000")
{
continue;
}
QTreeWidgetItem *denomItem = new QTreeWidgetItem(treeWidget);
denomItem->setText(0, "面额: " + denomination);
//获取排序后的年份列表(降序)
QStringList years = m_data[denomination].keys();
std::sort(years.begin(), years.end(), [](const QString &a, const QString &b) {
return a.toInt() > b.toInt();
});
//按年份分类
foreach(const QString &year, years)
{
QTreeWidgetItem *yearItem = new QTreeWidgetItem(denomItem);
yearItem->setText(0, "年份: " + year);
//按版本分类
QMap<QString, CurrencyData> versions = m_data[denomination][year];
foreach(const QString &version, versions.keys())
{
CurrencyData data = versions[version];
QTreeWidgetItem *versionItem = new QTreeWidgetItem(yearItem);
versionItem->setText(0, "版本: " + version);
versionItem->setText(1, QString::number(data.ir_r));
versionItem->setText(2, QString::number(data.ir_t));
versionItem->setToolTip(1, QString("IR_R: %1\nIR_T: %2\nIRTS: %3")
.arg(data.ir_r)
.arg(data.ir_t)
.arg(data.irts));
versionItem->setToolTip(2, versionItem->toolTip(1));
}
}
denomItem->setExpanded(true);
}
treeWidget->expandAll();
}
这个代码帮我修改一下逻辑,改为遍历到size以后,继续读取的时候,按照四行是一个面额的某个年份的四个面的值,此时如果遇到面额为10000000的记录就跳过这组,进入下一组,也就是从size后面跳过四行数据后开始读取我需要的数据并且按照面额从大到小、年限从大到小排序,然后在读取到最后一个面额的最后一个年份的这一组的第四行数据以后,就不继续往下读取了,判断是否是最后一个面额的最后一个年份的最后一行数据,可以根据后面还有没有面额来判断,如果超过三组数据也就是十二行数据都是空白的就不继续读取了