JavaScript如何读取数据文件中Byte、Short、String类型数据
JavaScript 复刻旧二进制文件读写方案
等价写入实现
JavaScript 完全可以等价复刻旧代码的写入逻辑,核心依赖ArrayBuffer和DataView实现按指定字节长度、字节序写入不同类型数据,不需要额外第三方库。
在写代码前先确认旧代码三个写入函数的底层规则,避免解析出错:
WriteByte:基本无歧义,固定写入1字节无符号整数,取值范围0-255WriteShort:固定写入2字节整数,需要确认是大端序/小端序、有符号/无符号,多数老式桌面工具默认用小端序无符号整数WriteString:不同老语言实现差异较大,需要确认是「1字节长度前缀+单字节编码内容」、固定长度字符串,还是0x00作为终止符的字符串,以及字符串的编码格式(ASCII/GBK等)
以下为匹配多数老式实现(WriteString为1字节长度前缀+单字节字符、WriteShort为小端无符号)的写入示例:
// 按旧代码顺序写入关卡数据 function buildLevBuffer(lData) { // 预计算缓冲区总长度 let strTotalLen = 0; const strFields = [lData.designer, lData.creation, lData.lastSave, lData.name]; strFields.forEach(s => strTotalLen += 1 + s.length); const byteTotalLen = 3; // levprotect、levdim两个维度值共3个单字节字段 const mapTotalLen = lData.dim[0] * lData.dim[1] * 2; // 每个地图块占2字节 const buffer = new ArrayBuffer(strTotalLen + byteTotalLen + mapTotalLen); const view = new DataView(buffer); let offset = 0; // 复刻WriteString const writeString = (str) => { view.setUint8(offset, str.length & 0xFF); offset++; for (let i = 0; i < str.length; i++) { view.setUint8(offset, str.charCodeAt(i) & 0xFF); offset++; } }; // 复刻WriteByte const writeByte = (val) => { view.setUint8(offset, val & 0xFF); offset++; }; // 复刻WriteShort 小端无符号,大端序把最后一个参数改为false const writeShort = (val) => { view.setUint16(offset, val & 0xFFFF, true); offset += 2; }; // 严格按照旧代码顺序写入 writeString(lData.designer); writeString(lData.creation); writeString(lData.lastSave); writeByte(lData.protect); writeString(lData.name); writeByte(lData.dim[0]); writeByte(lData.dim[1]); for (let y = 0; y < lData.dim[1]; y++) { for (let x = 0; x < lData.dim[0]; x++) { writeShort(lData.map[x][y]); } } return buffer; } // 输出文件:Node环境直接用fs写入Buffer,浏览器环境包成Blob触发下载 // Node示例:fs.writeFileSync('./test.lev', Buffer.from(buildLevBuffer(levData))) // 浏览器示例:new Blob([buildLevBuffer(levData)], {type: 'application/octet-stream'})
服务器文件读取能力
按该结构存储的文件放在服务器上时,JavaScript完全可以正常读取解析:
- 浏览器端:通过
fetch请求文件地址,拿到响应后调用arrayBuffer()方法获取二进制缓冲区,用DataView按照写入时的顺序、字节序逐段解析字段即可,注意需要服务器配置对应资源的CORS响应头,避免跨域拦截。 - Node端:既可以直接用
fs模块读取服务器本地的文件,也可以通过fetch/内置http模块请求远程文件地址,解析逻辑和浏览器端完全一致。
对应上述写入规则的解析示例:
// 浏览器端读取解析示例 async function loadLevFile(fileUrl) { const resp = await fetch(fileUrl); const buffer = await resp.arrayBuffer(); const view = new DataView(buffer); let offset = 0; const readString = () => { const len = view.getUint8(offset); offset++; let str = ''; for (let i = 0; i < len; i++) { str += String.fromCharCode(view.getUint8(offset)); offset++; } return str; }; const readByte = () => { const val = view.getUint8(offset); offset++; return val; }; const readShort = () => { const val = view.getUint16(offset, true); offset += 2; return val; }; const levData = {}; levData.designer = readString(); levData.creation = readString(); levData.lastSave = readString(); levData.protect = readByte(); levData.name = readString(); levData.dim = [readByte(), readByte()]; levData.map = Array(levData.dim[0]).fill(0).map(() => Array(levData.dim[1]).fill(0)); for (let y = 0; y < levData.dim[1]; y++) { for (let x = 0; x < levData.dim[0]; x++) { levData.map[x][y] = readShort(); } } return levData; }
注意:如果字符串包含非单字节编码的字符(比如中文),需要确认旧代码使用的编码格式,上述示例的单字节charCode读写方式仅适用于ASCII、Latin1、GBK这类单/双字节编码的单字节字符读取,多字节编码需要配合TextDecoder做转码。字节序必须和旧代码保持一致,否则数值类字段会解析错误。
内容的提问来源于stack exchange,提问作者DREAM68
相关产品推荐
相关产品推荐

