JavascriptのUint8Array要素の範囲にいくつかのUTF-8エンコードデータが存在します。これらを通常のJavaScript文字列にデコードする効率的な方法はありますか(Javascriptは16ビットUnicodeを使用していると思います)?文字列の連結がCPUを集中的に使用するようになるので、一度に1文字は追加したくありません。
toStringをUint8Array返します"91,50,48,49,57,45"
JavascriptのUint8Array要素の範囲にいくつかのUTF-8エンコードデータが存在します。これらを通常のJavaScript文字列にデコードする効率的な方法はありますか(Javascriptは16ビットUnicodeを使用していると思います)?文字列の連結がCPUを集中的に使用するようになるので、一度に1文字は追加したくありません。
toStringをUint8Array返します"91,50,48,49,57,45"
回答:
TextEncoderまたTextDecoder、stringencoding ライブラリによってポリフィルされているエンコーディング標準から、文字列とArrayBufferの間で変換されます。
var uint8array = new TextEncoder("utf-8").encode("¢");
var string = new TextDecoder("utf-8").decode(uint8array);
npm install text-encoding、var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;。結構です。
utf-8。だから、TextEncoder議論は不要です!
TextEncoder/ TextDecoderAPIを追加したため、現在のノードバージョンのみを対象とする場合、追加のパッケージをインストールする必要はありません。
これはうまくいくはずです:
// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
/* utf.js - UTF-8 <=> UTF-16 convertion
*
* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
* Version: 1.0
* LastModified: Dec 25 1999
* This library is free. You can redistribute it and/or modify it.
*/
function Utf8ArrayToStr(array) {
var out, i, len, c;
var char2, char3;
out = "";
len = array.length;
i = 0;
while(i < len) {
c = array[i++];
switch(c >> 4)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12: case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
}
ハックを使用せず、ブラウザのJS機能に依存しないため、他のソリューションと同様にクリーンです。たとえば、他のJS環境でも機能します。
JSFiddleデモを確認してください。
fromUTF8Array([240,159,154,133])空になる(while fromUTF8Array([226,152,131])→"☃")
これが私が使うものです:
var str = String.fromCharCode.apply(null, uint8Arr);
RangeErrorにより大きなテキストがスローされます。「最大コールスタックサイズを超えました」
SCRIPT28: Out of stack space、300 + k文字、またはRangeErrorChrome 39 をフィードするとスローします。Firefox33は問題ありません。100 + kは3つすべてで大丈夫です。
これは、Chromeのサンプルアプリケーションの1つにありますが、非同期変換で問題のない大きなデータブロックを対象としています。
/**
* Converts an array buffer to a string
*
* @private
* @param {ArrayBuffer} buf The buffer to convert
* @param {Function} callback The function to call when conversion is complete
*/
function _arrayBufferToString(buf, callback) {
var bb = new Blob([new Uint8Array(buf)]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsText(bb);
}
ノードでは、「BufferインスタンスもあるUint8Arrayインスタンスので、」buf.toString()このケースで動作します。
BufferUint8Arrayも認識されていませんでした。ありがとう!
Buffer.from(uint8array).toString('utf-8')
Albertが提供する解決策は、提供される関数が頻繁に呼び出されず、適度なサイズの配列にのみ使用される限り、うまく機能します。それ以外の場合は、非常に非効率的です。以下は、Nodeとブラウザの両方で機能し、次の利点がある拡張されたバニラJavaScriptソリューションです。
•すべてのオクテット配列サイズで効率的に機能します
•中間の使い捨て文字列を生成しません
•最新のJSエンジンで4バイト文字をサポートします(それ以外の場合は「?」に置き換えられます)
var utf8ArrayToStr = (function () {
var charCache = new Array(128); // Preallocate the cache for the common single byte chars
var charFromCodePt = String.fromCodePoint || String.fromCharCode;
var result = [];
return function (array) {
var codePt, byte1;
var buffLen = array.length;
result.length = 0;
for (var i = 0; i < buffLen;) {
byte1 = array[i++];
if (byte1 <= 0x7F) {
codePt = byte1;
} else if (byte1 <= 0xDF) {
codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F);
} else if (byte1 <= 0xEF) {
codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
} else if (String.fromCodePoint) {
codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
} else {
codePt = 63; // Cannot convert four byte code points, so use "?" instead
i += 3;
}
result.push(charCache[codePt] || (charCache[codePt] = charFromCodePt(codePt)));
}
return result.join('');
};
})();
@Sudhirが言ったことを行ってから、カンマで区切られた数値のリストから文字列を取得します。
for (var i=0; i<unitArr.byteLength; i++) {
myString += String.fromCharCode(unitArr[i])
}
これにより、関連性がある場合は、必要な文字列が得られます。
String.fromCharCode.apply(null, unitArr);。前述のように、UTF8エンコーディングは処理しませんが、ASCIIのサポートのみが必要で、TextEncoder / TextDecoderにアクセスできない場合は、これで十分な場合もあります。
IEでサポートされていないためにTextDecoder APIを使用できない場合:
function utf8ArrayToString(aBytes) {
var sView = "";
for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) {
nPart = aBytes[nIdx];
sView += String.fromCharCode(
nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
/* (nPart - 252 << 30) may be not so safe in ECMAScript! So...: */
(nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
(nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
(nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
(nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
(nPart - 192 << 6) + aBytes[++nIdx] - 128
: /* nPart < 127 ? */ /* one byte */
nPart
);
}
return sView;
}
let str = utf8ArrayToString([50,72,226,130,130,32,43,32,79,226,130,130,32,226,135,140,32,50,72,226,130,130,79]);
// Must show 2H₂ + O₂ ⇌ 2H₂O
console.log(str);
これらの機能を試してください、
var JsonToArray = function(json)
{
var str = JSON.stringify(json, null, 0);
var ret = new Uint8Array(str.length);
for (var i = 0; i < str.length; i++) {
ret[i] = str.charCodeAt(i);
}
return ret
};
var binArrayToJson = function(binArray)
{
var str = "";
for (var i = 0; i < binArray.length; i++) {
str += String.fromCharCode(parseInt(binArray[i]));
}
return JSON.parse(str)
}
ソース:https : //gist.github.com/tomfa/706d10fed78c497731ac、Tomfaへの称賛
私は、人々が両方の方法を示す方法を示していないこと、または些細なUTF8文字列では機能しないことを示していないことに不満を感じていました。うまくいくコードがいくつかあるcodereview.stackexchange.comの投稿を見つけました。私はそれを使用して、古代のルーン文字をバイトに変換し、バイトでいくつかのcrypoをテストしてから、文字列に変換し直しました。作業コードはgithubにあります。わかりやすくするために、メソッドの名前を変更しました。
// https://codereview.stackexchange.com/a/3589/75693
function bytesToSring(bytes) {
var chars = [];
for(var i = 0, n = bytes.length; i < n;) {
chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff));
}
return String.fromCharCode.apply(null, chars);
}
// https://codereview.stackexchange.com/a/3589/75693
function stringToBytes(str) {
var bytes = [];
for(var i = 0, n = str.length; i < n; i++) {
var char = str.charCodeAt(i);
bytes.push(char >>> 8, char & 0xFF);
}
return bytes;
}
単体テストでは、次のUTF-8文字列を使用します。
// http://kermitproject.org/utf8.html
// From the Anglo-Saxon Rune Poem (Rune version)
const secretUtf8 = `ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ
ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ
ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬`;
文字列の長さは117文字のみですが、エンコード時のバイト長は234です。
console.log行のコメントを外すと、デコードされた文字列は、エンコードされた文字列と同じであることがわかります(バイトはShamirの秘密共有アルゴリズムを介して渡されます!):
String.fromCharCode.apply(null, chars)charsが大きすぎるとエラーになります。
NodeJSでは、利用可能なバッファがあり、それらを使用した文字列変換は非常に簡単です。Uint8Arrayをバッファに変換するのは簡単です。このコードを試してください、それは私にとってNodeで基本的にUint8Arraysを含むすべての変換のために機能します:
let str = Buffer.from(uint8arr.buffer).toString();
Uint8ArrayからArrayBufferを抽出し、それを適切なNodeJSバッファーに変換しています。次に、Bufferを文字列に変換します(必要に応じて、16進数またはbase64エンコードでスローできます)。
文字列からUint8Arrayに戻す場合は、次のようにします。
let uint8arr = new Uint8Array(Buffer.from(str));
文字列に変換するときにbase64のようなエンコーディングを宣言した場合、base64を使用したBuffer.from(str, "base64")場合、または使用した他のエンコーディングを使用する必要があることに注意してください。
これはモジュールなしではブラウザで動作しません!NodeJSバッファーはブラウザーに存在しないため、ブラウザーにバッファー機能を追加しない限り、このメソッドは機能しません。これは実際にはとても簡単ですが、このように小さくて高速なモジュールを使用するだけです!
class UTF8{
static encode(str:string){return new UTF8().encode(str)}
static decode(data:Uint8Array){return new UTF8().decode(data)}
private EOF_byte:number = -1;
private EOF_code_point:number = -1;
private encoderError(code_point) {
console.error("UTF8 encoderError",code_point)
}
private decoderError(fatal, opt_code_point?):number {
if (fatal) console.error("UTF8 decoderError",opt_code_point)
return opt_code_point || 0xFFFD;
}
private inRange(a:number, min:number, max:number) {
return min <= a && a <= max;
}
private div(n:number, d:number) {
return Math.floor(n / d);
}
private stringToCodePoints(string:string) {
/** @type {Array.<number>} */
let cps = [];
// Based on http://www.w3.org/TR/WebIDL/#idl-DOMString
let i = 0, n = string.length;
while (i < string.length) {
let c = string.charCodeAt(i);
if (!this.inRange(c, 0xD800, 0xDFFF)) {
cps.push(c);
} else if (this.inRange(c, 0xDC00, 0xDFFF)) {
cps.push(0xFFFD);
} else { // (inRange(c, 0xD800, 0xDBFF))
if (i == n - 1) {
cps.push(0xFFFD);
} else {
let d = string.charCodeAt(i + 1);
if (this.inRange(d, 0xDC00, 0xDFFF)) {
let a = c & 0x3FF;
let b = d & 0x3FF;
i += 1;
cps.push(0x10000 + (a << 10) + b);
} else {
cps.push(0xFFFD);
}
}
}
i += 1;
}
return cps;
}
private encode(str:string):Uint8Array {
let pos:number = 0;
let codePoints = this.stringToCodePoints(str);
let outputBytes = [];
while (codePoints.length > pos) {
let code_point:number = codePoints[pos++];
if (this.inRange(code_point, 0xD800, 0xDFFF)) {
this.encoderError(code_point);
}
else if (this.inRange(code_point, 0x0000, 0x007f)) {
outputBytes.push(code_point);
} else {
let count = 0, offset = 0;
if (this.inRange(code_point, 0x0080, 0x07FF)) {
count = 1;
offset = 0xC0;
} else if (this.inRange(code_point, 0x0800, 0xFFFF)) {
count = 2;
offset = 0xE0;
} else if (this.inRange(code_point, 0x10000, 0x10FFFF)) {
count = 3;
offset = 0xF0;
}
outputBytes.push(this.div(code_point, Math.pow(64, count)) + offset);
while (count > 0) {
let temp = this.div(code_point, Math.pow(64, count - 1));
outputBytes.push(0x80 + (temp % 64));
count -= 1;
}
}
}
return new Uint8Array(outputBytes);
}
private decode(data:Uint8Array):string {
let fatal:boolean = false;
let pos:number = 0;
let result:string = "";
let code_point:number;
let utf8_code_point = 0;
let utf8_bytes_needed = 0;
let utf8_bytes_seen = 0;
let utf8_lower_boundary = 0;
while (data.length > pos) {
let _byte = data[pos++];
if (_byte == this.EOF_byte) {
if (utf8_bytes_needed != 0) {
code_point = this.decoderError(fatal);
} else {
code_point = this.EOF_code_point;
}
} else {
if (utf8_bytes_needed == 0) {
if (this.inRange(_byte, 0x00, 0x7F)) {
code_point = _byte;
} else {
if (this.inRange(_byte, 0xC2, 0xDF)) {
utf8_bytes_needed = 1;
utf8_lower_boundary = 0x80;
utf8_code_point = _byte - 0xC0;
} else if (this.inRange(_byte, 0xE0, 0xEF)) {
utf8_bytes_needed = 2;
utf8_lower_boundary = 0x800;
utf8_code_point = _byte - 0xE0;
} else if (this.inRange(_byte, 0xF0, 0xF4)) {
utf8_bytes_needed = 3;
utf8_lower_boundary = 0x10000;
utf8_code_point = _byte - 0xF0;
} else {
this.decoderError(fatal);
}
utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed);
code_point = null;
}
} else if (!this.inRange(_byte, 0x80, 0xBF)) {
utf8_code_point = 0;
utf8_bytes_needed = 0;
utf8_bytes_seen = 0;
utf8_lower_boundary = 0;
pos--;
code_point = this.decoderError(fatal, _byte);
} else {
utf8_bytes_seen += 1;
utf8_code_point = utf8_code_point + (_byte - 0x80) * Math.pow(64, utf8_bytes_needed - utf8_bytes_seen);
if (utf8_bytes_seen !== utf8_bytes_needed) {
code_point = null;
} else {
let cp = utf8_code_point;
let lower_boundary = utf8_lower_boundary;
utf8_code_point = 0;
utf8_bytes_needed = 0;
utf8_bytes_seen = 0;
utf8_lower_boundary = 0;
if (this.inRange(cp, lower_boundary, 0x10FFFF) && !this.inRange(cp, 0xD800, 0xDFFF)) {
code_point = cp;
} else {
code_point = this.decoderError(fatal, _byte);
}
}
}
}
//Decode string
if (code_point !== null && code_point !== this.EOF_code_point) {
if (code_point <= 0xFFFF) {
if (code_point > 0)result += String.fromCharCode(code_point);
} else {
code_point -= 0x10000;
result += String.fromCharCode(0xD800 + ((code_point >> 10) & 0x3ff));
result += String.fromCharCode(0xDC00 + (code_point & 0x3ff));
}
}
}
return result;
}
`
私はこのTypescriptスニペットを使用しています:
function UInt8ArrayToString(uInt8Array: Uint8Array): string
{
var s: string = "[";
for(var i: number = 0; i < uInt8Array.byteLength; i++)
{
if( i > 0 )
s += ", ";
s += uInt8Array[i];
}
s += "]";
return s;
}
JavaScriptバージョンが必要な場合は、型注釈を削除してください。お役に立てれば!
u8array.toString()わかりませんが、を呼び出すときにUint8Arrayオブジェクトを公開するBrowserFSからファイルを読み取るときに使用しますfs.readFile。