You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript如何读取数据文件中Byte、Short、String类型数据

JavaScript 复刻旧二进制文件读写方案

等价写入实现

JavaScript 完全可以等价复刻旧代码的写入逻辑,核心依赖ArrayBuffer和DataView实现按指定字节长度、字节序写入不同类型数据,不需要额外第三方库。
在写代码前先确认旧代码三个写入函数的底层规则,避免解析出错:

  • WriteByte:基本无歧义,固定写入1字节无符号整数,取值范围0-255
  • WriteShort:固定写入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 17:54:30