[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>


답변

당신은 사용할 수 있습니다 TextEncoderTextDecoder로부터 인코딩 표준 에 의해 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));

그것 때문에

  1. 큰 버퍼에서 충돌 (누군가 246300의 “마법”크기에 대해 썼지 만 Maximum call stack size exceeded120000 바이트 버퍼에서 오류 가 발생했습니다 (Chrome 29))
  2. 그것은이 정말 성능 저하 (아래 참조)

동기식 솔루션이 정확히 필요한 경우 다음과 같은 것을 사용하십시오.

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🙂

도움이 되었기를 바랍니다,