<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Data转CSV转换器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Arial, sans-serif;
        }
        body {
            background-color: #f0f2f5;
            padding: 2rem;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            background: white;
            padding: 2rem;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        h1 {
            text-align: center;
            color: #1a73e8;
            margin-bottom: 1.5rem;
            font-weight: 600;
        }
        .form-group {
            margin-bottom: 1.2rem;
        }
        label {
            display: block;
            margin-bottom: 0.5rem;
            color: #5f6368;
            font-weight: 500;
        }
        .input-group {
            display: flex;
            gap: 0.5rem;
            align-items: center;
        }
        input[type="text"] {
            flex: 1;
            padding: 0.75rem;
            border: 1px solid #dadce0;
            border-radius: 4px;
            font-size: 1rem;
            transition: border 0.2s;
        }
        input[type="text"]:focus {
            outline: none;
            border-color: #1a73e8;
        }
        button {
            padding: 0.75rem 1.25rem;
            border: none;
            border-radius: 4px;
            background: #1a73e8;
            color: white;
            font-size: 1rem;
            cursor: pointer;
            transition: background 0.2s;
        }
        button:hover {
            background: #1557b0;
        }
        button#convertBtn {
            background: #34a853;
            width: 100%;
            padding: 0.9rem;
            font-size: 1.05rem;
            margin-top: 0.5rem;
        }
        button#convertBtn:hover {
            background: #2d8643;
        }
        .info-section {
            margin: 1.5rem 0;
            padding: 1rem;
            background: #f8f9fa;
            border-radius: 6px;
            border-left: 3px solid #1a73e8;
        }
        .info-title {
            font-weight: 600;
            color: #202124;
            margin-bottom: 0.8rem;
            font-size: 1.05rem;
        }
        .info-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 0.8rem;
        }
        .info-item {
            display: flex;
            justify-content: space-between;
            padding: 0.4rem 0;
        }
        .info-key {
            color: #5f6368;
        }
        .info-value {
            color: #202124;
            font-weight: 500;
        }
        .status {
            margin-top: 1rem;
            text-align: center;
            padding: 0.75rem;
            border-radius: 4px;
            background: #f1f3f4;
            color: #5f6368;
        }
        .error {
            background: #fce8e6;
            color: #d93025;
        }
        .success {
            background: #e6f4ea;
            color: #137333;
        }
        select {
            width: 100%;
            padding: 0.75rem;
            border: 1px solid #dadce0;
            border-radius: 4px;
            font-size: 1rem;
            background-color: white;
            transition: border 0.2s;
        }
        select:focus {
            outline: none;
            border-color: #1a73e8;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Data转CSV转换器</h1>
        <!-- 编码选择 -->
        <div class="form-group">
            <label for="encodingSelect">选择编码格式</label>
            <select id="encodingSelect">
                <option value="gb2312">GB2312</option>
                <option value="utf-8">UTF-8</option>
                <option value="gbk">GBK</option>
                <option value="big5">Big5</option>
            </select>
        </div>
        <!-- 输入文件选择 -->
        <div class="form-group">
            <label for="inputFilePath">输入文件(*.data)</label>
            <div class="input-group">
                <input type="text" id="inputFilePath" placeholder="未选择文件" readonly>
                <button id="browseBtn">浏览</button>
                <input type="file" id="fileInput" accept=".data" style="display: none;">
            </div>
        </div>
        <!-- 输出文件路径 -->
        <div class="form-group">
            <label for="outputFilePath">输出文件(*.csv)</label>
            <input type="text" id="outputFilePath" placeholder="自动生成输出路径">
        </div>
        <!-- 文件信息显示区域 -->
        <div class="info-section">
            <div class="info-title">文件结构信息</div>
            <div class="info-grid">
                <div class="info-item">
                    <span class="info-key">每条记录最大字节数:</span>
                    <span class="info-value" id="recordLength">--</span>
                </div>
                <div class="info-item">
                    <span class="info-key">字段数量:</span>
                    <span class="info-value" id="fieldCount">--</span>
                </div>
                <div class="info-item">
                    <span class="info-key">最大行数:</span>
                    <span class="info-value" id="maxRows">--</span>
                </div>
                <div class="info-item">
                    <span class="info-key">实际行数:</span>
                    <span class="info-value" id="actualRows">--</span>
                </div>
            </div>
        </div>
        <!-- 转换按钮和状态 -->
        <button id="convertBtn">开始转换</button>
        <div class="status" id="status">就绪</div>
    </div>
    <script>
        // 全局变量存储文件数据和解析信息
        let fileData = null;
        let fileInfo = {
            recordLength: 0,
            fieldCount: 0,
            maxRows: 0,
            actualRows: 0
        };
        // DOM元素
        const browseBtn = document.getElementById('browseBtn');
        const fileInput = document.getElementById('fileInput');
        const inputFilePath = document.getElementById('inputFilePath');
        const outputFilePath = document.getElementById('outputFilePath');
        const encodingSelect = document.getElementById('encodingSelect');
        const convertBtn = document.getElementById('convertBtn');
        const statusEl = document.getElementById('status');
        // 信息显示元素
        const recordLengthEl = document.getElementById('recordLength');
        const fieldCountEl = document.getElementById('fieldCount');
        const maxRowsEl = document.getElementById('maxRows');
        const actualRowsEl = document.getElementById('actualRows');
        // 浏览文件按钮点击事件
        browseBtn.addEventListener('click', () => {
            fileInput.click();
        });
        // 文件选择事件
        fileInput.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (!file) return;
            // 显示输入文件路径
            inputFilePath.value = file.name;
            // 自动生成输出文件路径
            const outputName = file.name.replace(/\.data$/, '.csv');
            outputFilePath.value = outputName;
            try {
                // 读取文件数据
                const reader = new FileReader();
                reader.onload = (event) => {
                    fileData = new Uint8Array(event.target.result);
                    // 解析文件信息
                    parseFileInfo();
                    statusEl.textContent = "文件加载成功";
                    statusEl.className = "status success";
                };
                reader.onerror = () => {
                    throw new Error("文件读取失败");
                };
                reader.readAsArrayBuffer(file);
            } catch (error) {
                statusEl.textContent = `错误:${error.message}`;
                statusEl.className = "status error";
                resetFileInfo();
            }
        });
        // 解析文件信息(从指定地址读取数据)
        function parseFileInfo() {
            try {
                // 每条记录最大字节数:0x020c位置,4字节(小端)
                fileInfo.recordLength = readUint32LE(0x020c);
                // 字段数量:0x0214位置,4字节(小端)
                fileInfo.fieldCount = readUint32LE(0x0214);
                // 最大行数:0x0218位置,4字节(小端)
                fileInfo.maxRows = readUint32LE(0x0218);
                // 实际行数:0x0208位置,4字节(小端)
                fileInfo.actualRows = readUint32LE(0x0208);
                // 更新界面显示
                recordLengthEl.textContent = fileInfo.recordLength;
                fieldCountEl.textContent = fileInfo.fieldCount;
                maxRowsEl.textContent = fileInfo.maxRows;
                actualRowsEl.textContent = fileInfo.actualRows;
            } catch (error) {
                statusEl.textContent = `解析错误:${error.message}`;
                statusEl.className = "status error";
                resetFileInfo();
            }
        }
        // 从指定偏移量读取4字节小端无符号整数
        function readUint32LE(offset) {
            if (!fileData || offset + 4 > fileData.length) {
                throw new Error("文件数据不足或格式错误");
            }
            return (
                fileData[offset] +
                (fileData[offset + 1] << 8) +
                (fileData[offset + 2] << 16) +
                (fileData[offset + 3] << 24)
            );
        }
        // 重置文件信息显示
        function resetFileInfo() {
            fileInfo = {
                recordLength: 0,
                fieldCount: 0,
                maxRows: 0,
                actualRows: 0
            };
            recordLengthEl.textContent = "--";
            fieldCountEl.textContent = "--";
            maxRowsEl.textContent = "--";
            actualRowsEl.textContent = "--";
            fileData = null;
        }
        // 转换按钮点击事件
        convertBtn.addEventListener('click', async () => {
            if (!fileData) {
                statusEl.textContent = "错误:请先选择有效的.data文件";
                statusEl.className = "status error";
                return;
            }
            if (!outputFilePath.value) {
                statusEl.textContent = "错误:请指定输出文件路径";
                statusEl.className = "status error";
                return;
            }
            try {
                statusEl.textContent = "正在转换...";
                statusEl.className = "status";
                convertBtn.disabled = true;
                // 执行转换
                await convertDataToCsv();
                statusEl.textContent = "转换成功!文件已下载";
                statusEl.className = "status success";
            } catch (error) {
                statusEl.textContent = `转换错误:${error.message}`;
                statusEl.className = "status error";
            } finally {
                convertBtn.disabled = false;
            }
        });
        // 核心转换逻辑
        async function convertDataToCsv() {
            const { recordLength, fieldCount, actualRows } = fileInfo;
            const encoding = encodingSelect.value;
            const firstRecordOffset = 0x0400;
            const fieldLength = recordLength / fieldCount;
            if (!Number.isInteger(fieldLength)) {
                throw new Error("记录长度不能被字段数量整除,文件结构异常");
            }
            // 存储CSV内容
            const csvLines = [];
            // 遍历所有实际记录
            for (let row = 0; row < actualRows; row++) {
                const rowData = [];
                // 计算当前记录的起始偏移量
                const recordOffset = firstRecordOffset + row * recordLength;
                // 遍历每个字段
                for (let field = 0; field < fieldCount; field++) {
                    const fieldOffset = recordOffset + field * fieldLength;
                    // 读取字段数据(到\0结束或字段长度结束)
                    let fieldBytes = [];
                    for (let i = 0; i < fieldLength; i++) {
                        const byte = fileData[fieldOffset + i];
                        if (byte === 0x00) break; // 遇到字符串结束符'\0'停止
                        fieldBytes.push(byte);
                    }
                    // 解码字段(处理不同编码)
                    let fieldStr = await decodeBytes(fieldBytes, encoding);
                    // 替换^为英文逗号
                    fieldStr = fieldStr.replace(/\^/g, ',');
                    // 去除前后空格
                    fieldStr = fieldStr.trim();
                    rowData.push(fieldStr);
                }
                // 添加当前行到CSV(不使用双引号包裹)
                csvLines.push(rowData.join(','));
            }
            // 生成CSV字符串
            const csvContent = csvLines.join('\n');
            // 下载文件
            downloadFile(csvContent, outputFilePath.value, 'text/csv');
        }
        // 解码字节数组(支持多种编码)
        async function decodeBytes(bytes, encoding) {
            // 处理编码名称映射(TextDecoder使用的名称可能不同)
            const encodingMap = {
                'gb2312': 'gbk',  // 浏览器通常用gbk兼容gb2312
                'utf-8': 'utf-8',
                'gbk': 'gbk',
                'big5': 'big5'
            };
            const decoder = new TextDecoder(encodingMap[encoding] || encoding, { fatal: false });
            return decoder.decode(new Uint8Array(bytes));
        }
        // 下载文件
        function downloadFile(content, fileName, mimeType) {
            const blob = new Blob([content], { type: mimeType });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = fileName;
            document.body.appendChild(a);
            a.click();
            setTimeout(() => {
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            }, 100);
        }
    </script>
</body>
</html>