[javascript] 문자열과 ArrayBuffer 간 변환
JavaScript 문자열을 효율적으로 ArrayBuffer 로 변환 하거나 그 반대로 변환하는 데 일반적으로 사용되는 기술이 있습니까? 특히, ArrayBuffer의 내용을 쓰고 localStorage
다시 읽을 수 있기를 바랍니다.
답변
2016 년 업데이트 -5 년이 지난 지금 사양에 새로운 인코딩 방법 (아래 지원 참조)이있어 적절한 인코딩을 사용하여 문자열과 유형 배열 사이를 변환합니다.
TextEncoder
는 다음을 TextEncoder
나타냅니다 .
TextEncoder
인터페이스 같은 특정 문자 인코딩하는 구체적인 방법에 대한 인코더를 나타내고utf-8
,인코더는 코드 포인트 스트림을 입력으로 사용하여 바이트 스트림을 생성합니다.iso-8859-2
,koi8
,
cp1261
,gbk
, …
위의 내용이 기록 된 이후 변경 사항 : (ibid.)
참고 : Firefox, Chrome 및 Opera는 utf-8 이외의 인코딩 유형 (예 : utf-16, iso-8859-2, koi8, cp1261 및 gbk)을 지원했습니다. Firefox 48 […], Chrome 54 […] 및 Opera 41에서 사양과 일치시키기 위해 utf-8 이외의 다른 인코딩 유형은 사용할 수 없습니다. *
*) 업데이트 된 사양 (W3) 및 여기 (whatwg)
인스턴스를 만든 후에 TextEncoder
는 문자열을 가져와 주어진 인코딩 매개 변수를 사용하여 인코딩합니다.
if (!("TextEncoder" in window))
alert("Sorry, this browser does not support TextEncoder...");
var enc = new TextEncoder(); // always utf-8
console.log(enc.encode("This is a string converted to a Uint8Array"));
그런 다음 .buffer
결과 Uint8Array
에 매개 변수를 사용하여 ArrayBuffer
필요한 경우 언더 레이 를 다른보기 로 변환하십시오 .
문자열의 문자가 인코딩 스키마를 준수하는지 확인하십시오. 예를 들어, 예제에서 UTF-8 범위를 벗어난 문자를 사용하는 경우 1 바이트가 아닌 2 바이트로 인코딩됩니다.
일반적으로 UTF-16 인코딩을 다음과 같은 용도로 사용합니다 localStorage
.
TextDecoder
마찬가지로 반대 프로세스 는 다음을 사용합니다TextDecoder
.
TextDecoder
인터페이스는 특정 문자 인코딩, 추천되는 특정 방법하는 디코더 나타내고utf-8
,iso-8859-2
,koi8
,
cp1261
,gbk
, … 디코더 입력으로 바이트 스트림을 취하여 코드 포인트 스트림을 방출한다.
사용 가능한 모든 디코딩 유형은 여기 에서 찾을 수 있습니다 .
if (!("TextDecoder" in window))
alert("Sorry, this browser does not support TextDecoder...");
var enc = new TextDecoder("utf-8");
var arr = new Uint8Array([84,104,105,115,32,105,115,32,97,32,85,105,110,116,
56,65,114,114,97,121,32,99,111,110,118,101,114,116,
101,100,32,116,111,32,97,32,115,116,114,105,110,103]);
console.log(enc.decode(arr));
MDN StringView 라이브러리
이에 대한 대안은 StringView
라이브러리 (lgpl-3.0으로 라이센스)를 사용하는 것입니다. 목표는 다음과 같습니다.
- JavaScript ArrayBuffer 인터페이스를 기반으로 문자열에 대한 C와 같은 인터페이스 (즉, 문자 코드 배열-JavaScript의 ArrayBufferView)를 작성
- StringView.prototype 객체에 메소드를 추가하여 누구나 확장 할 수있는 확장 성이 뛰어난 라이브러리를 만드는 방법
- 불변의 새로운 JavaScript 캐릭터 라인을 생성하는 것이 아니라, 숫자의 배열로 엄격하게 동작하는 캐릭터 라인과 같은 객체에 대한 메소드의 콜렉션을 작성하는 (지금부터는 stringViews)
- JavaScript의 기본 UTF-16 DOMString 이외의 유니 코드 인코딩 작업
훨씬 더 많은 유연성을 제공합니다. 그러나 최신 브라우저에 TextEncoder
/ TextDecoder
가 내장되어있는 동안이 라이브러리에 링크하거나 내장해야 합니다.
지원하다
2018 년 7 월 기준 :
TextEncoder
(실험, 표준 트랙)
Chrome | Edge | Firefox | IE | Opera | Safari
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 19° | - | 25 | -
Chrome/A | Edge/mob | Firefox/A | Opera/A |Safari/iOS | Webview/A
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 19° | ? | - | 38
°) 18: Firefox 18 implemented an earlier and slightly different version
of the specification.
WEB WORKER SUPPORT:
Experimental, On Standard Track
Chrome | Edge | Firefox | IE | Opera | Safari
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 20 | - | 25 | -
Chrome/A | Edge/mob | Firefox/A | Opera/A |Safari/iOS | Webview/A
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 20 | ? | - | 38
Data from MDN - `npm i -g mdncomp` by epistemex
답변
Blob / FileReader를 사용하는 Dennis 및 gengkev 솔루션이 작동하지만 해당 접근법을 사용하는 것은 권장하지 않습니다. 간단한 문제에 대한 비동기 접근 방식이며 직접 솔루션보다 훨씬 느립니다. 더 간단하고 훨씬 빠른 솔루션으로 html5rocks에 게시물을 작성했습니다 .http :
//updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
해결책은 다음과 같습니다.
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i<strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
편집하다:
인코딩 API는 문자열 변환 해결하는 데 도움이 문제를. Html5Rocks.com의 Jeff Posnik 이 위의 원본 기사에 대한 답변을 확인 하십시오.
발췌 :
인코딩 API를 사용하면 작업해야하는 많은 표준 인코딩에 관계없이 원시 바이트와 기본 JavaScript 문자열간에 간단하게 변환 할 수 있습니다.
<pre id="results"></pre>
<script>
if ('TextDecoder' in window) {
// The local files to be fetched, mapped to the encoding that they're using.
var filesToEncoding = {
'utf8.bin': 'utf-8',
'utf16le.bin': 'utf-16le',
'macintosh.bin': 'macintosh'
};
Object.keys(filesToEncoding).forEach(function(file) {
fetchAndDecode(file, filesToEncoding[file]);
});
} else {
document.querySelector('#results').textContent = 'Your browser does not support the Encoding API.'
}
// Use XHR to fetch `file` and interpret its contents as being encoded with `encoding`.
function fetchAndDecode(file, encoding) {
var xhr = new XMLHttpRequest();
xhr.open('GET', file);
// Using 'arraybuffer' as the responseType ensures that the raw data is returned,
// rather than letting XMLHttpRequest decode the data first.
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
if (this.status == 200) {
// The decode() method takes a DataView as a parameter, which is a wrapper on top of the ArrayBuffer.
var dataView = new DataView(this.response);
// The TextDecoder interface is documented at http://encoding.spec.whatwg.org/#interface-textdecoder
var decoder = new TextDecoder(encoding);
var decodedString = decoder.decode(dataView);
// Add the decoded file's text to the <pre> element on the page.
document.querySelector('#results').textContent += decodedString + '\n';
} else {
console.error('Error while requesting', file, this);
}
};
xhr.send();
}
</script>
답변
당신은 사용할 수 있습니다 TextEncoder
및 TextDecoder
로부터 인코딩 표준 에 의해 polyfilled되고, stringencoding 라이브러리 변환 문자열, 및 ArrayBuffers에서 :
var uint8array = new TextEncoder().encode(string);
var string = new TextDecoder(encoding).decode(uint8array);
답변
얼룩이보다 느리다 String.fromCharCode(null,array);
그러나 배열 버퍼가 너무 커지면 실패합니다. 내가 찾은 최선의 해결책 String.fromCharCode(null,array);
은 스택을 날려 버리지 않지만 한 번에 하나의 문자보다 빠른 작업 으로 사용 하고 분할하는 것입니다.
대형 배열 버퍼에 가장 적합한 솔루션은 다음과 같습니다.
function arrayBufferToString(buffer){
var bufView = new Uint16Array(buffer);
var length = bufView.length;
var result = '';
var addition = Math.pow(2,16)-1;
for(var i = 0;i<length;i+=addition){
if(i + addition > length){
addition = length - i;
}
result += String.fromCharCode.apply(null, bufView.subarray(i,i+addition));
}
return result;
}
나는 이것을 블롭을 사용하는 것보다 약 20 배 빠릅니다. 또한 100MB 이상의 큰 문자열에도 작동합니다.
답변
gengkev의 답변을 바탕으로 BlobBuilder 가 String 및 ArrayBuffer를 처리 할 수 있으므로 두 가지 방법으로 함수를 만들었습니다 .
function string2ArrayBuffer(string, callback) {
var bb = new BlobBuilder();
bb.append(string);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
}
f.readAsArrayBuffer(bb.getBlob());
}
과
function arrayBuffer2String(buf, callback) {
var bb = new BlobBuilder();
bb.append(buf);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result)
}
f.readAsText(bb.getBlob());
}
간단한 테스트 :
string2ArrayBuffer("abc",
function (buf) {
var uInt8 = new Uint8Array(buf);
console.log(uInt8); // Returns `Uint8Array { 0=97, 1=98, 2=99}`
arrayBuffer2String(buf,
function (string) {
console.log(string); // returns "abc"
}
)
}
)
답변
다음은 배열 버퍼에서 이진 문자열을 가져 오는 것에 관한 것입니다.
사용하지 않는 것이 좋습니다
var binaryString = String.fromCharCode.apply(null, new Uint8Array(arrayBuffer));
그것 때문에
- 큰 버퍼에서 충돌 (누군가 246300의 “마법”크기에 대해 썼지 만
Maximum call stack size exceeded
120000 바이트 버퍼에서 오류 가 발생했습니다 (Chrome 29)) - 그것은이 정말 성능 저하 (아래 참조)
동기식 솔루션이 정확히 필요한 경우 다음과 같은 것을 사용하십시오.
var
binaryString = '',
bytes = new Uint8Array(arrayBuffer),
length = bytes.length;
for (var i = 0; i < length; i++) {
binaryString += String.fromCharCode(bytes[i]);
}
이전보다 느리지 만 올바르게 작동합니다. 이것을 작성하는 순간 그 문제에 대한 매우 빠른 동기 솔루션이없는 것 같습니다 (이 주제에서 언급 된 모든 라이브러리는 동기 기능에 대해 동일한 접근 방식을 사용합니다).
하지만 내가 정말로 권장하는 것은 Blob
+ FileReader
접근법을 사용 하는 것입니다.
function readBinaryStringFromArrayBuffer (arrayBuffer, onSuccess, onFail) {
var reader = new FileReader();
reader.onload = function (event) {
onSuccess(event.target.result);
};
reader.onerror = function (event) {
onFail(event.target.error);
};
reader.readAsBinaryString(new Blob([ arrayBuffer ],
{ type: 'application/octet-stream' }));
}
유일한 단점은 (전부가 아님) 그것이 비동기 적이라는 것입니다 . 그리고 이전 솔루션 보다 약 8-10 배 빠릅니다 ! (일부 세부 정보 : 내 환경의 동기 솔루션은 2.4Mb 버퍼의 경우 950-1050ms가 걸렸지 만 FileReader 솔루션은 동일한 양의 데이터에 대해 약 100-120ms의 시간이 걸렸습니다 .100Kb 버퍼에서 동기 솔루션을 모두 테스트 했으며 거의 동시에, 따라서 ‘적용’을 사용하면 루프가 훨씬 느리지 않습니다.)
BTW here : ArrayBuffer와 String author 를 변환하는 방법은 나와 같은 두 가지 접근법을 비교하고 완전히 반대의 결과를 얻습니다 ( 그의 테스트 코드는 여기에 있습니다 ) 왜 그렇게 다른 결과가 있습니까? 아마도 1Kb 길이의 테스트 문자열 ( “veryLongStr”이라고 함) 때문일 수 있습니다. 버퍼는 2.4Mb 크기의 JPEG 이미지였습니다.
답변
( 업데이트 더 나은 솔루션을 제공 한이 답변의 후반부를 참조하십시오.)
또한이 문제에 부딪 쳤습니다 .FF 6 (한 방향)에서 다음이 작동합니다.
var buf = new ArrayBuffer( 10 );
var view = new Uint8Array( buf );
view[ 3 ] = 4;
alert(Array.prototype.slice.call(view).join(""));
불행히도 물론 문자가 아닌 배열 값의 ASCII 텍스트 표현으로 끝납니다. 그래도 루프보다 훨씬 더 효율적이어야합니다. 예. 위의 예에서 결과는 0004000000
여러 개의 null 문자 및 chr (4)가 아니라입니다.
편집하다:
에보고 한 후 MDC 여기 , 당신은을 만들 수 있습니다 ArrayBuffer
에서 Array
다음과 같이 :
var arr = new Array(23);
// New Uint8Array() converts the Array elements
// to Uint8s & creates a new ArrayBuffer
// to store them in & a corresponding view.
// To get at the generated ArrayBuffer,
// you can then access it as below, with the .buffer property
var buf = new Uint8Array( arr ).buffer;
원래 질문에 대답하기 위해 ArrayBuffer
<-> String
를 다음과 같이 변환 할 수 있습니다 .
var buf, view, str;
buf = new ArrayBuffer( 256 );
view = new Uint8Array( buf );
view[ 0 ] = 7; // Some dummy values
view[ 2 ] = 4;
// ...
// 1. Buffer -> String (as byte array "list")
str = bufferToString(buf);
alert(str); // Alerts "7,0,4,..."
// 1. String (as byte array) -> Buffer
buf = stringToBuffer(str);
alert(new Uint8Array( buf )[ 2 ]); // Alerts "4"
// Converts any ArrayBuffer to a string
// (a comma-separated list of ASCII ordinals,
// NOT a string of characters from the ordinals
// in the buffer elements)
function bufferToString( buf ) {
var view = new Uint8Array( buf );
return Array.prototype.join.call(view, ",");
}
// Converts a comma-separated ASCII ordinal string list
// back to an ArrayBuffer (see note for bufferToString())
function stringToBuffer( str ) {
var arr = str.split(",")
, view = new Uint8Array( arr );
return view.buffer;
}
편의를 위해 다음은 function
원시 유니 코드 String
를 로 변환 하기위한 ArrayBuffer
것입니다 (ASCII / 1 바이트 문자로만 작동 함)
function rawStringToBuffer( str ) {
var idx, len = str.length, arr = new Array( len );
for ( idx = 0 ; idx < len ; ++idx ) {
arr[ idx ] = str.charCodeAt(idx) & 0xFF;
}
// You may create an ArrayBuffer from a standard array (of values) as follows:
return new Uint8Array( arr ).buffer;
}
// Alerts "97"
alert(new Uint8Array( rawStringToBuffer("abc") )[ 0 ]);
위의 예에서는 문자열을 예를 들어 저장할 수있는 ArrayBuffer
-> String
및 다시 다시 갈 ArrayBuffer
수 있습니다. .localStorage
🙂
도움이 되었기를 바랍니다,
단