【问题标题】:Reading a Stored Byte array of binary file as comma separated values in text file将二进制文件的存储字节数组读取为文本文件中的逗号分隔值
【发布时间】:2020-10-10 20:35:58
【问题描述】:

问题陈述:

  • 我得到一个文本文件,其中二进制文件的字节数组以逗号分隔的值存储在一行中。
  • 例如:82,19,176,106,0,0,0,4,247,35,186,20,87,143,18,120,44,76,100
  • 字符串很长,所有内容都在一行中,我无法控制,因为它取决于二进制文件的大小。
  • 我必须读取这个字节数组并将其转换回原始二进制文件。

实现的逻辑:
使用 Node.js 和 FS

var instream = fs.createReadStream('stream1.txt',{ highWaterMark: 1 * 1024  , encoding: 'utf8' });
instream.on("data", function(line) {  
  
  lineCount++;
  var splitArray = line.split(',');  
  var uintArray = new Uint8Array(splitArray); 
  chunks.push(uintArray);
  console.log(lineCount);
});

instream.on("end", function() {  
  var fullUint8Array = concatenate(chunks);
  fs.writeFile("abc.prt", Buffer.from(fullUint8Array), function (err) {
      if (err) {
        console.log(err);
      } else {
        console.log("Done");
      }
  }); 
});
  • 我无法获取原始二进制文件。它总是被损坏。
  • 如果我正在读取单个块中的文件并尝试上述解决方案,它将起作用。但总是无法做到这一点,因为如果尝试将一个非常大的字符串数组转换为 uint8Array 则会出现内存错误。

但是当我分块读取字符串时,我无法获取二进制文件。

我无法理解我做错了什么。使用的技术Node.JS、javascript。

用示例更新了问题

  • 这是一个示例流。 (stream1.txt)
  • 这是读取stream1.txt后需要输出的原始二进制文件。
  • Link to the files
  • 连接代码

//For joining uInt8Arrays
function concatenate(arrays) {
  let totalLength = 0;
  for (const arr of arrays) {
    totalLength += arr.length;
  }
  const result = new Uint8Array(totalLength);
  let offset = 0;
  for (const arr of arrays) {
    result.set(arr, offset);
    offset += arr.length;
  }
  return result;
}

【问题讨论】:

  • concatenate 是如何实现的?
  • @JózefPodlecki 我将在原始问题中更新的连接代码

标签: javascript node.js arrays


【解决方案1】:

如果您正在分块读取文件,则需要调整拆分逻辑以应对这种情况。您的代码可能确实会产生损坏的结果,因为像82,19,176,106,0,0 这样的输入字符串可能会被读取为82,19,17+6,106,0,082,19+,176,106,+0,0

相反,您需要确保始终读取整个字节值。如果后面没有逗号或 eof,则您还不能处理它。我建议使用Transform stream 来执行此操作(另请参阅this article 关于该技术):

import { createReadStream, createWriteStream } from 'fs';
import { pipeline, Transform } from 'stream';

const parseCommaSeparatedBytes = new Transform({
  transform(chunk, encoding, callback) {
    const prefix = this.leftover || '';
    const string = prefix + chunk.toString();
    // TODO: validate inputs to be numeric and in the byte range
    const splitArray = string.split(',');
    if (splitArray.length)
      this.leftover = splitArray.pop();
    this.push(new Uint8Array(splitArray));
    callback();
  },
  flush(callback) {
    const last = this.leftover || '';
    if (last.length)
      this.push(new Uint8Array([last]));
    callback();
  },
});

const instream = createReadStream('stream1.txt', {
  highWaterMark: 1024,
  encoding: 'utf8'
});
const outstream = createWriteStream('abc.prt');

pipeline(instream, parseCommaSeparatedBytes, outstream, function (err) {
  if (err) {
    console.error(err);
  } else {
    console.log("Done");
  }
});

【讨论】:

  • 我同意你的回答。所以我的问题仍然存在。我可以实施什么解决方案来处理这个问题?
【解决方案2】:

我无法获取原始二进制文件。它总是越来越 损坏。

不,它没有损坏。该字符串由逗号分隔,其未编码值被放入Uint8Array,随后文件与该数据一起保存。

这或多或少是正在发生的事情

let line = "82,19,176,106,0,0,0,4,247,35,186,20,87,143,18,120,44,76,100";
let result = line.split(',').map(pr => String.fromCharCode(Number(pr))).join('');
console.log(result);

// Solution 1
let encoded = line.split('').map(npr => npr.charCodeAt(0));
result = encoded.map(pr => String.fromCharCode(pr)).join('');
console.log(result);

// Solution 2
const encoder = new TextEncoder();
const decoder = new TextDecoder();
encoded = encoder.encode(line);
result = decoder.decode(encoded);
console.log(result);

如果你应用上面的代码,它可能看起来像这样:

const fs = require('fs');
let lineCount = 0;
let chunks = [];

const encoder = new TextEncoder();

function concatenate(chunks) {
  return chunks.reduce((acc, chunk) => {
    return new Uint8Array([...acc, ...chunk]);
  }, new Uint8Array([]));
}

var instream = fs.createReadStream('stream1.txt',{ highWaterMark: 1 * 1024  , encoding: 'utf8' });
instream.on("data", function(line) {  
  
  lineCount++;
  var splitArray = line.split(',');
  var uintArray = encoder.encode(line);
  chunks.push(uintArray);
});

instream.on("end", function() {  
  var fullUint8Array = concatenate(chunks);
  fs.writeFile("abc.prt", Buffer.from(fullUint8Array, 'utf-8'), function (err) {
      if (err) {
        console.log(err);
      } else {
        console.log("Done");
      }
  }); 
});

如果我正在读取单个块中的文件并尝试上述解决方案 将工作。但总是无法做到这一点,因为如果尝试转换 uint8Array 的字符串数组非常大,它会产生内存错误。

您可以通过创建写入流并将数据立即放在那里来减少内存占用。

示例

const fs = require('fs');
let lineCount = 0;
let chunks = [];

const encoder = new TextEncoder();
var outputStream = fs.createWriteStream("abc.prt");
var inputStream = fs.createReadStream('stream1.txt',{ highWaterMark: 1 * 1024  , encoding: 'utf8' });

outputStream.on("open", function() {
  inputStream.on("data", function(line) {  
  
    lineCount++;
    var splitArray = line.split(',');
    var uintArray = encoder.encode(line);
    outputStream.write(uintArray);
  });
  
  inputStream.on("end", function() { 
    outputStream.close();
  })
})

【讨论】:

  • 我尝试了您的解决方案,但仍然无法正常工作。我可以与原始二进制文件一起共享流文件。我将附加到原始问题。
  • 哪个部分不起作用,您选择了哪种解决方案?
  • 未编码的值”是什么意思? OP 正在处理原始字节,没有编码。 split(',') 生成一个整数字符串数组,Uint8Array 构造函数将其解析为字节值。之后,它们只是被写入输出文件。
  • 之后,它们只是被写入输出文件。 然后他用他最喜欢的文本查看器打开文件并看到乱码,因为程序将这些字节值解码为文字Unicode 值。你真的看不出区别吗?
  • @JózefPodlecki No. OP 说它是一个二进制文件(并使用一些 .prt 文件扩展名作为输出,显然是一些 CAD 数据)。您为什么希望他们在文本查看器中打开它?另外,如果字节乱码,你会将它们解码成什么?
猜你喜欢
  • 1970-01-01
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多