网页端JS工具:如何按特定二进制值拆分含二进制内容的字符串?
按特定二进制值拆分本地二进制文件内容(MIDI Syssex场景)
嘿,刚好做过类似的MIDI Syssex处理工具,给你梳理下正确的解决思路——千万别用字符串来处理二进制!这是踩过坑的教训😂。因为JS字符串是UTF-16编码的,直接处理二进制数据会出现编码丢失或乱码,必须用ArrayBuffer + Uint8Array来操作原始字节。
核心步骤说明
你的需求是拆分以0xF0作为首尾标识的二进制记录,每条记录内部数据都是0x00-0x7F,具体实现分为这几步:
1. 读取本地文件为原始二进制数据
用FileReader的readAsArrayBuffer方法读取文件,这样拿到的是未经编码转换的原始二进制缓冲区,完全保留每个字节的真实值。
2. 转换为Uint8Array方便字节级操作
把ArrayBuffer转成Uint8Array,就能直接遍历每个字节的数值(0-255),轻松定位0xF0分隔符。
3. 遍历拆分出每条Syssex记录
遍历Uint8Array,记录每个0xF0的位置:第一次遇到0xF0标记为记录起始,第二次遇到0xF0就截取从起始到当前位置的片段作为一条完整记录,然后重置起始标记继续处理下一条。
完整代码示例
<input type="file" id="syssexFileInput" accept=".syx,.bin"> <script> document.getElementById('syssexFileInput').addEventListener('change', handleSyssexFileSelect); function handleSyssexFileSelect(e) { const selectedFile = e.target.files[0]; if (!selectedFile) return; const fileReader = new FileReader(); fileReader.onload = function(loadEvent) { // 拿到原始二进制缓冲区 const rawBuffer = loadEvent.target.result; // 转换为Uint8Array const byteArray = new Uint8Array(rawBuffer); // 拆分Syssex消息 const syssexMessages = splitSyssexRecords(byteArray); // 处理每条拆分后的消息 syssexMessages.forEach((msg, index) => { console.log(`第 ${index+1} 条Syssex消息(长度:${msg.length}字节)`, msg); // 可以在这里进一步解析消息内容:位图、数值、ASCII等 parseSyssexContent(msg); }); }; // 以ArrayBuffer格式读取文件 fileReader.readAsArrayBuffer(selectedFile); } function splitSyssexRecords(byteArray) { const records = []; let startPos = -1; for (let i = 0; i < byteArray.length; i++) { const currentByte = byteArray[i]; if (currentByte === 0xF0) { if (startPos === -1) { // 标记一条记录的起始位置 startPos = i; } else { // 标记记录结束,截取从start到当前位置(包含首尾两个0xF0) const currentRecord = byteArray.slice(startPos, i + 1); // 可以在这里加个校验:确保记录长度至少为2(首尾两个F0) if (currentRecord.length >= 2) { records.push(currentRecord); } // 重置起始位置,准备下一条记录 startPos = -1; } } } // 处理文件末尾未结束的不完整记录 if (startPos !== -1) { console.warn('检测到不完整的Syssex记录,已自动忽略'); } return records; } function parseSyssexContent(syssexMsg) { // 跳过首尾的0xF0,获取核心内容 const content = syssexMsg.slice(1, syssexMsg.length - 1); if (content.length === 0) return; // 解析位图(假设第一个字节是位图) const bitmap = content[0]; console.log('位图(二进制):', bitmap.toString(2).padStart(8, '0')); // 解析1字节数值 const singleByteValue = content[1]; console.log('1字节数值:', singleByteValue); // 解析低字节在前的2字节数值 if (content.length >=4) { const twoByteValue = content[2] + (content[3] << 8); console.log('2字节数值(低字节在前):', twoByteValue); } // 解析后续ASCII字符 const asciiStr = Array.from(content.slice(4)).map(byte => String.fromCharCode(byte)).join(''); console.log('ASCII内容:', asciiStr); } </script>
关键注意事项
- 绝对不要用字符串处理二进制:比如
readAsText会把二进制转成字符串,0xF0这类非UTF-8兼容字节会被替换成�,直接丢失数据。 - 校验记录有效性:拆分后可以检查每条记录的首尾是否都是
0xF0,过滤掉无效或不完整的记录。 - 大文件兼容性:
readAsArrayBuffer是异步操作,不会阻塞主线程,处理几百MB的文件也没问题。
内容的提问来源于stack exchange,提问作者Chris Beall
相关产品推荐
相关产品推荐

