तार और ArrayBuffers के बीच परिवर्तित


264

क्या ArrayBuffers और इसके विपरीत में जावास्क्रिप्ट स्ट्रिंग्स को कुशलता से परिवर्तित करने के लिए आमतौर पर स्वीकृत तकनीक है ? विशेष रूप से, मैं एक ArrayBuffer की सामग्री को लिखने localStorageऔर इसे पढ़ने के लिए सक्षम होना चाहूंगा ।


1
मुझे इसमें कोई अनुभव नहीं है, लेकिन एपीआई प्रलेखन ( khronos.org/registry/typedarray/specs/latest ) से देखते हुए अगर आप इसका निर्माण करते हैं, Int8Array ArrayBufferViewतो हो सकता है कि चार्टstring[i] = buffer[i] और इसके विपरीत कॉपी करने के लिए केवल ब्रैकेट नोटेशन का उपयोग करें ।
FK82

2
@ FK82, जो एक उचित दृष्टिकोण ( Uint16Arrayजेएस के 16-बिट पात्रों के लिए s का उपयोग करके) की तरह दिखता है , लेकिन जावास्क्रिप्ट स्ट्रिंग्स अपरिवर्तनीय हैं, इसलिए आप किसी चरित्र की स्थिति में सीधे असाइन नहीं कर सकते। मुझे अभी भी एक सामान्य String.fromCharCode(x)में प्रत्येक मान की प्रतिलिपि बनाने और फिर कॉल करने की आवश्यकता होगी । Uint16ArrayArray.join()Array
kpozin

@kpozin: सच है, वास्तव में उस के माध्यम से नहीं सोचा था।
FK82

5
@kpozin यह पता चलता है कि अधिकांश आधुनिक जेएस इंजनों ने स्ट्रिंग कॉन्टेनेशन को उस बिंदु पर अनुकूलित किया है जहां यह सिर्फ उपयोग करने के लिए सस्ता है string += String.fromCharCode(buffer[i]);। यह अजीब लगता है कि तार और टाइप किए गए सरणियों के बीच परिवर्तित करने के लिए अंतर्निहित तरीके नहीं होंगे। उन्हें कुछ इस तरह जानना था जैसे यह सामने आएगा।
डाउनलोड करें

arrayBuffer.toString () मेरे लिए अच्छा काम कर रहा है।
नागर कन्वीनर

जवाबों:


129

अद्यतन २०१६ - पाँच साल अब ऐनक में नए तरीके हैं (नीचे समर्थन देखें) स्ट्रिंग और टाइप किए गए सरणियों के बीच कनवर्ट करने के लिए उचित एन्कोडिंग के साथ।

TextEncoder

TextEncoderका प्रतिनिधित्व करता है :

TextEncoderइंटरफ़ेस एक विशिष्ट विधि के लिए एक एनकोडर, एक विशिष्ट वर्ण एन्कोडिंग है कि, की तरह का प्रतिनिधित्व करता है utf-8,iso-8859-2, koi8, cp1261, gbk, ... एक एनकोडर इनपुट के रूप में कोड बिंदुओं की एक धारा लेता है और बाइट्स की एक धारा का उत्सर्जन करता है।

ऊपर लिखे जाने के बाद से नोट बदलें: (ibid।)

नोट: फ़ायरफ़ॉक्स, क्रोम और ओपेरा में utf-8 (जैसे utf-16, iso-8859-2, koi8, cp1261, और gbk) के अलावा अन्य एन्कोडिंग प्रकारों के लिए समर्थन होता था। फ़ायरफ़ॉक्स 48 [...], क्रोम 54 [...] और ओपेरा 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लिए जिसके परिणामस्वरूप पैरामीटर का उपयोग करते हैं ।Uint8ArrayArrayBuffer

बस यह सुनिश्चित करें कि स्ट्रिंग में वर्ण एन्कोडिंग स्कीमा का पालन करते हैं, उदाहरण के लिए, यदि आप उदाहरण के लिए UTF-8 श्रेणी के बाहर के वर्णों का उपयोग करते हैं तो वे एक के बजाय दो बाइट्स के लिए एन्कोड हो जाएंगे।

सामान्य उपयोग के लिए आप 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 के रूप में लाइसेंस प्राप्त) जो लक्ष्य है:

  • स्ट्रिंग्स के लिए सी-लाइक इंटरफेस बनाने के लिए (जैसे, कैरेक्टर कोड की एक सरणी - जावास्क्रिप्ट में एक ऐरेबफ़र व्यू) जावास्क्रिप्ट ऐरेबफ़र इंटरफ़ेस पर आधारित
  • अत्यधिक एक्सटेंसिबल लाइब्रेरी बनाने के लिए जिसे कोई भी ऑब्जेक्ट StringView.prototype के तरीकों को जोड़कर बढ़ा सकता है
  • ऐसी स्ट्रिंग जैसी वस्तुओं के लिए तरीकों का एक संग्रह बनाने के लिए (अब से: stringViews) जो नए अपरिवर्तनीय जावास्क्रिप्ट स्ट्रिंग्स बनाने के बजाय संख्याओं के सरणियों पर सख्ती से काम करते हैं
  • जावास्क्रिप्ट डिफ़ॉल्ट UTF-16 DOMStrings के अलावा यूनिकोड एनकोडिंग के साथ काम करने के लिए

और अधिक लचीलापन दे। हालांकि, यह हमारे लिए लिंक करने के लिए आवश्यकता होती है या इस लाइब्रेरी जबकि एम्बेड हैं TextEncoder/ TextDecoderआधुनिक ब्राउज़रों में निर्मित की जा रही है।

सहयोग

जुलाई / 2018 तक:

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


1
MS के अनुसार यह विकास में है: developer.microsoft.com/en-us/microsoft-edge/platform/status/…
मौरिस मुलर

2018-04-18 में सफारी मोबाइल (आईओएस) के लिए कोई समर्थन नहीं: developer.mozilla.org/en-US/docs/Web/API/TextDecoder
कांस्य पुरुष

एक-लाइनर: var encoder = 'TextEncoder' in window ? new TextEncoder() : {encode: function(str){return Uint8Array.from(str, function(c){return c.codePointAt(0);});}};तो आप बसvar array = encoder.encode('hello');
यति

1
इसके साथ बात यह TextEncoderहै कि यदि आपके पास एक स्ट्रिंग (जैसे, छवि) में द्विआधारी डेटा है, तो आप उपयोग नहीं करना चाहते हैं TextEncoder(जाहिरा तौर पर)। 127 से अधिक कोड बिंदुओं वाले वर्ण दो बाइट का उत्पादन करते हैं। मेरे पास एक स्ट्रिंग में बाइनरी डेटा क्यों है? cy.fixture(NAME, 'binary')( cypress) एक स्ट्रिंग पैदा करता है।
x- यूरी

175

हालांकि डेनिस और gengkev ब्लॉब / FileReader काम का उपयोग करने के समाधान, मैं उस दृष्टिकोण लेने का सुझाव नहीं होगा। यह एक साधारण समस्या के लिए एक async दृष्टिकोण है, और यह प्रत्यक्ष समाधान की तुलना में बहुत धीमा है। मैंने 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;
}

संपादित करें:

एन्कोडिंग एपीआई स्ट्रिंग रूपांतरण को सुलझाने में मदद करता है समस्या। उपरोक्त मूल लेख Html5Rocks.com पर जेफ पोसनिक की प्रतिक्रिया देखें।

अंश:

एन्कोडिंग एपीआई कच्चे बाइट्स और देशी जावास्क्रिप्ट स्ट्रिंग्स के बीच अनुवाद करना आसान बनाता है, चाहे आपको कितने मानक एन्कोडिंग की आवश्यकता हो।

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

16
दुर्भाग्य से html5rocks पर मेरी टिप्पणी अभी तक अनुमोदित नहीं है। इसलिए यहाँ एक छोटा जवाब। मुझे अभी भी लगता है, यह सही तरीका नहीं है, क्योंकि आप बहुत सारे पात्रों को याद करते हैं, खासकर क्योंकि अधिकांश पृष्ठ आज यूटीएफ -8 एन्कोडिंग में हैं। एक तरफ, अधिक विशेष पात्रों के लिए (चलो एशियाई कहते हैं), charCodeAt फ़ंक्शन 4-बाइट मान लौटाता है, इसलिए उन्हें काट दिया जाएगा। दूसरी तरफ, सरल अंग्रेजी वर्ण ArrayBuffer को दो बार बढ़ाएंगे (आप प्रत्येक 1-बाइट चरित्र के लिए 2 बाइट का उपयोग कर रहे हैं)। WebSocket पर एक अंग्रेजी पाठ भेजने की कल्पना करें, इसे दो बार (वास्तविक समय वातावरण में अच्छा नहीं) की आवश्यकता होगी।
डेनिस

9
तीन उदाहरण: (1) This is a cool text!यूटीएफ 8 में 20 बाइट - यूनिकोड में 40 बाइट। (2) ÄÖÜUTF8 में 6 बाइट्स - यूनिकोड में 6 बाइट्स। (3) ☐☑☒यूटीएफ 8 में 9 बाइट्स - यूनिकोड में 6 बाइट्स। यदि आप स्ट्रिंग को UTF8- फाइल (ब्लॉब और फाइल राइटर एपीआई के माध्यम से) के रूप में संग्रहीत करना चाहते हैं, तो आप इस 2 विधियों का उपयोग नहीं कर सकते, क्योंकि ArrayBuffer यूनिकोड में होगा और UTF8 में नहीं।
डेनिस

3
मुझे एक त्रुटि मिलती है: अनट्रेडेड रेंजआयर: अधिकतम कॉल स्टैक आकार को पार कर गया। क्या समस्या हो सकती है?
याकूब

6
@Dennis - जे एस तार UCS2, नहीं UTF8 (या यहां तक UTF16) का उपयोग - जिसका अर्थ है charCodeAt () हमेशा मूल्यों रिटर्न 0 -> 65535 किसी भी UTF8 कोड मुद्दा यह है कि 4 बाइट समाप्त होता है सरोगेट जोड़े के साथ प्रतिनिधित्व किया जाएगा की आवश्यकता है (देखें en.wikipedia .org / wiki /… ) - दो अलग-अलग 16-बिट UCS2 मान।
ब्रूफो

6
@jacob - मेरा मानना ​​है कि त्रुटि है क्योंकि सरणी की लंबाई पर एक सीमा है जिसे लागू करने के लिए पारित किया जा सकता है () विधि। जैसे String.fromCharCode.apply(null, new Uint16Array(new ArrayBuffer(246300))).lengthक्रोम में मेरे लिए काम करता है, लेकिन अगर आप इसके बजाय 246301 का उपयोग करते हैं, तो मुझे आपका रेंजवेयर अपवाद मिल जाता है
ब्रूफो

71

आप ArrayBuffers से स्ट्रिंग को परिवर्तित करने के लिए, स्ट्रिंगकोडिंग लाइब्रेरी द्वारा पॉलीफ़िल्ड , जो एन्कोडिंग मानक से उपयोग कर सकते हैं TextEncoderऔर कर सकते हैं :TextDecoder

var uint8array = new TextEncoder().encode(string);
var string = new TextDecoder(encoding).decode(uint8array);

2
वैसे, यह फ़ायरफ़ॉक्स में डिफ़ॉल्ट रूप से उपलब्ध है: developer.mozilla.org/en-US/docs/Web/API/TextDecoder.decode
जोएल रिचर्ड

2
नए APIs के लिए अंगूठे जो कि अजीब वर्कअराउंड से बहुत बेहतर हैं!
टॉम ज़ातो -

1
यह सभी प्रकार के वर्णों के साथ काम नहीं करेगा।
डेविड

5
npm install text-encoding, var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;। जी नहीं, धन्यवाद।
इवान हू

बड़बड़ा ... अगर मेरे पास एक मौजूदा सरणी है तो मैं एक स्ट्रिंग लिखना चाहता हूं मुझे लगता है कि मुझे uint8array लेना है और इसे 2 बार कॉपी करना है ??
शौंक

40

बूँद की तुलना में बहुत धीमी है 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 से अधिक के बड़े तार के लिए भी काम करता है।


3
हमें इस समाधान के साथ जाना चाहिए। जैसा कि यह स्वीकार किया गया एक से अधिक उपयोग के मामले को सुलझाता है
sam

24

गेंगकेव के उत्तर के आधार पर, मैंने दोनों तरीकों के लिए फ़ंक्शंस बनाए, क्योंकि ब्लबबर्स्टिंग स्ट्रिंग और एरेबेलर को संभाल सकता है:

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"
            }
        )
    }
)

ArrayBuffer2String () में, क्या आपको कंसोल.लॉग () के बजाय कॉलबैक (...) कॉल करना है? अन्यथा कॉलबैक तर्क अप्रयुक्त हो जाता है।
दान फिलिमोर

यह जाने के लिए मार्ग की तरह दिखता है - धन्यवाद गेनेव और डेनिस। यह मूर्खतापूर्ण लगता है कि इसे पूरा करने के लिए कोई समकालिक तरीका नहीं है, लेकिन आप क्या कर सकते हैं ...
kpozin

जावास्क्रिप्ट सिंगल थ्रेडेड है। इसलिए FileReader दो कारणों से अतुल्यकालिक है: (1) यह एक (विशाल) फ़ाइल लोड करते समय अन्य जावास्क्रिप्ट के निष्पादन को अवरुद्ध नहीं करेगा (एक अधिक जटिल अनुप्रयोग की कल्पना करें) और (2) यह UI / ब्राउज़र (सामान्य समस्या) को ब्लॉक नहीं करेगा। लंबे समय तक जेएस कोड निष्पादित करने के साथ)। बहुत सारे एपीआई अतुल्यकालिक हैं। XMLHttpRequest 2 में भी सिंक्रोनस को हटा दिया जाता है।
डेनिस

मैं वास्तव में उम्मीद कर रहा था कि यह मेरे लिए काम करेगा, लेकिन स्ट्रिंग से ArrayBuffer में रूपांतरण मज़बूती से काम नहीं कर रहा है। मैं 256 मानों के साथ एक ArrayBuffer बना रहा हूं, और इसे एक स्ट्रिंग में लंबाई 256 के साथ बदल सकता हूं। लेकिन तब अगर मैं उस ArrayBuffer को वापस बदलने की कोशिश करूं - मेरे शुरुआती ArrayBuffer की सामग्री के आधार पर - मुझे 376 तत्व मिल रहे हैं। यदि आप मेरी समस्या को पुन: उत्पन्न करने का प्रयास करना चाहते हैं, तो मैं अपने ArrayBuffer को Uint8Array में 16x16 ग्रिड के रूप में मान a[y * w + x] = (x + y) / 2 * 16; रहा हूं getBlob("x"), जैसा कि मैंने कोशिश की है , जैसे कि मैंने कई अलग-अलग mimetypes के साथ गणना की है - कोई भाग्य नहीं।
मैट क्रूशांक

18
BlobBuilder नए ब्राउज़रों में पदावनत है। बदलें new BlobBuilder(); bb.append(buf);करने के लिए new Blob([buf]), एक UintArray के माध्यम से दूसरे समारोह में ArrayBuffer डाली new UintArray(buf)(या जो भी अंतर्निहित डेटा प्रकार के लिए उपयुक्त है), और फिर से छुटकारा पाने के getBlob()कॉल। अंत में, सफाई के लिए, बी.बी. का नाम बदलकर ब्लो करने के लिए क्योंकि यह अब ब्लूबेरी नहीं है।
जुग

18

निम्नलिखित सभी सरणी बफ़र्स से बाइनरी स्ट्रिंग्स प्राप्त करने के बारे में है

मैं उपयोग न करने की सलाह दूंगा

var binaryString = String.fromCharCode.apply(null, new Uint8Array(arrayBuffer));

क्योंकि यह

  1. बड़े बफ़र्स पर क्रैश (किसी ने "जादू" के बारे में लिखा 246300 का आकार, लेकिन मुझे Maximum call stack size exceeded120000 बाइट्स बफर पर त्रुटि मिली (क्रोम 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-1050 एमएस लिया, लेकिन FileReader के साथ समाधान में डेटा की समान मात्रा के लिए लगभग 100-120 एमएस था। और मैंने 100Kb बफर पर दोनों तुल्यकालिक समाधानों का परीक्षण किया है और उन्हें लिया है। लगभग एक ही समय, इसलिए लूप 'लागू' का उपयोग करके बहुत धीमा नहीं है।)

BTW यहाँ: कैसे और स्ट्रिंग लेखक से ArrayBuffer कन्वर्ट करने के लिए मेरे जैसे दो दृष्टिकोणों की तुलना करें और पूरी तरह से विपरीत परिणाम प्राप्त करें ( उसका परीक्षण कोड यहाँ है ) क्यों इतने अलग परिणाम? संभवतः उनके टेस्ट स्ट्रिंग की वजह से यह 1Kb लंबा है (उन्होंने इसे "veryLongStr" कहा)। मेरा बफ़र आकार 2.4Mb की एक बहुत बड़ी JPEG छवि थी।


13

( अपडेट कृपया इस उत्तर का दूसरा भाग देखें, जहां मेरे पास (उम्मीद है) अधिक संपूर्ण समाधान उपलब्ध है।)

मैं भी इस मुद्दे पर भागा, एफएफ 6 में मेरे लिए निम्नलिखित कार्य (एक दिशा के लिए):

var buf = new ArrayBuffer( 10 );
var view = new Uint8Array( buf );
view[ 3 ] = 4;
alert(Array.prototype.slice.call(view).join(""));

दुर्भाग्य से, निश्चित रूप से, आप वर्णों के बजाय सरणी में मूल्यों के ASCII पाठ अभ्यावेदन के साथ समाप्त होते हैं। यह अभी भी (होना चाहिए) लूप की तुलना में बहुत अधिक कुशल है, हालांकि। जैसे। ऊपर के उदाहरण के लिए, परिणाम 0004000000कई अशक्त वर्णों और chr (4) के बजाय है।

संपादित करें:

यहां एमडीसी को देखने के बाद , आप निम्नानुसार से एक बना सकते हैं :ArrayBufferArray

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(केवल एएससीआईआई / एक-बाइट पात्रों के साथ काम करेगा)

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:)

उम्मीद है की यह मदद करेगा,

सज्जन


1
मुझे नहीं लगता कि यह एक कुशल विधि है (समय या स्थान के संदर्भ में), और यह द्विआधारी डेटा को संग्रहीत करने के लिए एक बहुत ही असामान्य तरीका है।
kpozin

@kpozin: जहां तक ​​मुझे पता है, लोकलस्टोरीज में बाइनरी डेटा स्टोर करने का कोई और तरीका नहीं है
डैन फिलिमोर

1
Base64 एन्कोडिंग का उपयोग करने के बारे में क्या?
निक सोटरोस

13

यहाँ समाधानों के विपरीत, मुझे UTF-8 डेटा से / को परिवर्तित करने की आवश्यकता है। इस प्रयोजन के लिए, मैंने (अन) एस्केप / (एन) डिकोड्यूरिकम्पोनेंट ट्रिक का उपयोग करके निम्नलिखित दो कार्यों को कोडित किया। वे मेमोरी के बहुत बेकार हैं, एन्कोडेड utf8-string की 9 गुना लंबाई आवंटित करते हैं, हालांकि उन्हें जीसी द्वारा पुनर्प्राप्त किया जाना चाहिए। बस उन्हें 100mb पाठ के लिए उपयोग न करें।

function utf8AbFromStr(str) {
    var strUtf8 = unescape(encodeURIComponent(str));
    var ab = new Uint8Array(strUtf8.length);
    for (var i = 0; i < strUtf8.length; i++) {
        ab[i] = strUtf8.charCodeAt(i);
    }
    return ab;
}

function strFromUtf8Ab(ab) {
    return decodeURIComponent(escape(String.fromCharCode.apply(null, ab)));
}

जाँच रहा है कि यह काम करता है:

strFromUtf8Ab(utf8AbFromStr('latinкирилицаαβγδεζηあいうえお'))
-> "latinкирилицаαβγδεζηあいうえお"

8

यदि आपके पास एक स्ट्रिंग में बाइनरी डेटा है ( nodejs+ readFile(..., 'binary'), या cypress+ cy.fixture(..., 'binary'), आदि से प्राप्त ), तो आप उपयोग नहीं कर सकते TextEncoder। यह केवल समर्थन करता है utf8। मूल्यों के साथ बाइट्स >= 128प्रत्येक 2 बाइट्स में बदल जाते हैं।

ES2015:

a = Uint8Array.from(s, x => x.charCodeAt(0))

Uint8Array (33) [2, 134, 140, 186, 82, 70, 108, 182, 233, 40, 143, 247, 29, 76, 245, 206, 29, 87, 48, 48, 160, 78, 225, 242 , 56, 236, 201, 80, 80, 152, 118, 92, 144, 48

s = String.fromCharCode.apply(null, a)

"ÕÎRFl¶é (º L0W0 NáìÉ8PPPv \ 0"


7

मैंने पाया कि मुझे इस दृष्टिकोण के साथ समस्याएं थीं, मूल रूप से क्योंकि मैं एक फ़ाइल में आउटपुट लिखने की कोशिश कर रहा था और यह गैर एनकोडेड था। चूंकि JS UCS-2 एन्कोडिंग ( स्रोत , स्रोत ) का उपयोग करने के लिए लगता है , हमें इस समाधान को एक कदम आगे बढ़ाने की आवश्यकता है, यहां मेरा बढ़ाया हुआ समाधान है जो मेरे लिए काम करता है।

मुझे जेनेरिक पाठ से कोई कठिनाई नहीं थी, लेकिन जब यह अरब या कोरियाई के लिए नीचे था, तो आउटपुट फ़ाइल में सभी वर्ण नहीं थे, बल्कि त्रुटि वर्ण दिखा रहे थे।

फ़ाइल आउटपुट: ","10k unit":"",Follow:"Õ©íüY‹","Follow %{screen_name}":"%{screen_name}U“’Õ©íü",Tweet:"ĤüÈ","Tweet %{hashtag}":"%{hashtag} ’ĤüÈY‹","Tweet to %{name}":"%{name}U“xĤüÈY‹"},ko:{"%{followers_count} followers":"%{followers_count}…X \Ì","100K+":"100Ì tÁ","10k unit":"Ì è",Follow:"\°","Follow %{screen_name}":"%{screen_name} Ø \°X0",K:"œ",M:"1Ì",Tweet:"¸","Tweet %{hashtag}":"%{hashtag}

मूल: ","10k unit":"万",Follow:"フォローする","Follow %{screen_name}":"%{screen_name}さんをフォロー",Tweet:"ツイート","Tweet %{hashtag}":"%{hashtag} をツイートする","Tweet to %{name}":"%{name}さんへツイートする"},ko:{"%{followers_count} followers":"%{followers_count}명의 팔로워","100K+":"100만 이상","10k unit":"만 단위",Follow:"팔로우","Follow %{screen_name}":"%{screen_name} 님 팔로우하기",K:"천",M:"백만",Tweet:"트윗","Tweet %{hashtag}":"%{hashtag}

मैंने डेनिस के समाधान से जानकारी ली और यह पोस्ट मुझे मिली।

यहाँ मेरा कोड है:

function encode_utf8(s) {
  return unescape(encodeURIComponent(s));
}

function decode_utf8(s) {
  return decodeURIComponent(escape(s));
}

 function ab2str(buf) {
   var s = String.fromCharCode.apply(null, new Uint8Array(buf));
   return decode_utf8(decode_utf8(s))
 }

function str2ab(str) {
   var s = encode_utf8(str)
   var buf = new ArrayBuffer(s.length); 
   var bufView = new Uint8Array(buf);
   for (var i=0, strLen=s.length; i<strLen; i++) {
     bufView[i] = s.charCodeAt(i);
   }
   return bufView;
 }

यह मुझे एन्कोडिंग समस्याओं के बिना फ़ाइल को सामग्री को सहेजने की अनुमति देता है।

यह कैसे काम करता है: यह मूल रूप से एक यूटीएफ -8 चरित्र की रचना करने वाले एकल 8-बाइट का हिस्सा लेता है और उन्हें एकल पात्रों के रूप में बचाता है (इसलिए इस तरह से निर्मित एक यूटीएफ -8 चरित्र, इन वर्णों के 1-4 द्वारा रचा जा सकता है)। UTF-8 एक प्रारूप में वर्णों को कूटबद्ध करता है जो लंबाई में 1 से 4 बाइट्स तक होता है। हम यहां क्या कर रहे हैं एक यूआरआई घटक में स्टिंग एन्कोडिंग है और फिर इस घटक को लें और इसे संबंधित 8 बाइट चरित्र में अनुवाद करें। इस तरह हम UTF8 वर्णों द्वारा दी गई जानकारी को नहीं खोते हैं जो 1 बाइट से अधिक लंबी होती हैं।


6

यदि आपने विशाल सरणी उदाहरण का उपयोग किया है तो arr.length=1000000 स्टैक कॉलबैक समस्याओं से बचने के लिए आप इस कोड का उपयोग कर सकते हैं

function ab2str(buf) {
var bufView = new Uint16Array(buf);
var unis =""
for (var i = 0; i < bufView.length; i++) {
    unis=unis+String.fromCharCode(bufView[i]);
}
return unis
}

ऊपर से उल्टे फंक्शन मैंगिनी का जवाब

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;
}

4

ठीक है, यहाँ एक ही काम करने का कुछ जटिल तरीका है:

var string = "Blah blah blah", output;
var bb = new (window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder)();
bb.append(string);
var f = new FileReader();
f.onload = function(e) {
  // do whatever
  output = e.target.result;
}
f.readAsArrayBuffer(bb.getBlob());

संपादित करें: BlobBuilder लंबे समय से Blob कंस्ट्रक्टर के पक्ष में पदावनत हो गई है, जो तब मौजूद नहीं था जब मैंने पहली बार यह पोस्ट लिखा था। यहाँ एक अद्यतन संस्करण है। (और हाँ, यह रूपांतरण करने के लिए हमेशा एक बहुत ही मूर्खतापूर्ण तरीका रहा है, लेकिन यह सिर्फ मनोरंजन के लिए था!)

var string = "Blah blah blah", output;
var f = new FileReader();
f.onload = function(e) {
  // do whatever
  output = e.target.result;
};
f.readAsArrayBuffer(new Blob([string]));

3

से परिवर्तित करने के लिए Mangini समाधान के साथ खेलने के बाद ArrayBufferकरने के लिए String- ab2str(जो सबसे सुंदर और उपयोगी एक मैं पाया है है - धन्यवाद), मैं कुछ मुद्दों पर जब बड़े सरणियों से निपटने के लिए किया था। अधिक specefivally, कॉलिंग String.fromCharCode.apply(null, new Uint16Array(buf));एक त्रुटि फेंकता है:

arguments array passed to Function.prototype.apply is too large

इसे (बायपास) हल करने के लिए मैंने इनपुट ArrayBufferको चंक्स में संभालने का फैसला किया है। तो संशोधित समाधान है:

function ab2str(buf) {
   var str = "";
   var ab = new Uint16Array(buf);
   var abLen = ab.length;
   var CHUNK_SIZE = Math.pow(2, 16);
   var offset, len, subab;
   for (offset = 0; offset < abLen; offset += CHUNK_SIZE) {
      len = Math.min(CHUNK_SIZE, abLen-offset);
      subab = ab.subarray(offset, offset+len);
      str += String.fromCharCode.apply(null, subab);
   }
   return str;
}

चंक आकार सेट है 2^16क्योंकि यह वह आकार है जिसे मैंने अपने विकास परिदृश्य में काम करने के लिए पाया है। उच्च मान सेट करने पर वही त्रुटि पुनः प्राप्त होती है। CHUNK_SIZEएक अलग मान के लिए चर सेट करके इसे बदला जा सकता है । एक सम संख्या होना जरूरी है।

प्रदर्शन पर ध्यान दें - मैंने इस समाधान के लिए कोई प्रदर्शन परीक्षण नहीं किया। हालांकि, चूंकि यह पिछले समाधान पर आधारित है, और बड़े सरणियों को संभाल सकता है, इसलिए मुझे इसका कोई कारण नहीं दिखता कि इसका उपयोग क्यों न किया जाए।


आप निर्दिष्ट स्थिति और आकार में एक हिस्सा पाने के लिए टाइपरेड्रे.सुबर्रे का उपयोग कर सकते हैं , यह वही है जो मैं द्विआधारी प्रारूपों के हेडर को js में पढ़ने के लिए करता हूं
निकोस एम।

2

यहां देखें: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (जावास्क्रिप्ट ArrayBuffer इंटरफ़ेस पर आधारित स्ट्रिंग्स के लिए एक सी-जैसे इंटरफ़ेस)


2
वह कोड GPLv3 के अंतर्गत है। मुझे लगता है कि यह मोज़िला के लिए बहुत ही अव्यवसायिक है, यहां तक ​​कि उनके मानकों-अनुरूप प्रलेखन के साथ उस कोड को मिश्रण करने के लिए।
user239558

2
  stringToArrayBuffer(byteString) {
    var byteArray = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
      byteArray[i] = byteString.codePointAt(i);
    }
    return byteArray;
  }
  arrayBufferToString(buffer) {
    var byteArray = new Uint8Array(buffer);
    var byteString = '';
    for (var i = 0; i < byteArray.byteLength; i++) {
      byteString += String.fromCodePoint(byteArray[i]);
    }
    return byteString;
  }

यह कोड छोटी गाड़ी है यदि स्ट्रिंग में यूनिकोड वर्ण हैं। उदाहरण:arrayBufferToString(stringToArrayBuffer('🐴'))==='44'
xmcp

2

नोड.जेएस के लिए और https://github.com/feross/buffer का उपयोग करने वाले ब्राउज़रों के लिए भी

function ab2str(buf: Uint8Array) {
  return Buffer.from(buf).toString('base64');
}
function str2ab(str: string) {
  return new Uint8Array(Buffer.from(str, 'base64'))
}

नोट: यहाँ समाधान मेरे लिए काम नहीं किया। मुझे नोड.जेएस और ब्राउज़र का समर्थन करने की आवश्यकता है और बस UInt8Array को एक स्ट्रिंग में क्रमबद्ध करें। मैं इसे एक संख्या [] के रूप में क्रमबद्ध कर सकता था लेकिन यह अनावश्यक स्थान घेरता है। उस समाधान के साथ मुझे एनकोडिंग के बारे में चिंता करने की आवश्यकता नहीं है क्योंकि यह बेस 64 है। बस उसी स्थिति में अन्य लोग उसी समस्या से जूझते हैं ... मेरे दो सेंट


2

मान लें कि आपके पास एक सरणी है बफ़र बाइनरीस्ट्र:

let text = String.fromCharCode.apply(null, new Uint8Array(binaryStr));

और फिर आप राज्य को टेक्स्ट असाइन करते हैं।


1

"देशी" बाइनरी स्ट्रिंग जो एटब () रिटर्न एक 1-बाइट-प्रति-वर्ण ऐरे है।

तो हम एक चरित्र में 2 बाइट स्टोर नहीं करना चाहिए।

var arrayBufferToString = function(buffer) {
  return String.fromCharCode.apply(null, new Uint8Array(buffer));
}

var stringToArrayBuffer = function(str) {
  return (new Uint8Array([].map.call(str,function(x){return x.charCodeAt(0)}))).buffer;
}


0

मैं BlobBuilder की तरह हटाए गए एपीआई का उपयोग नहीं करने की सलाह दूंगा

BlobBuilder को लंबे समय से Blob ऑब्जेक्ट द्वारा हटा दिया गया है। डेनिस के उत्तर में कोड की तुलना करें - जहां BlobBuilder का उपयोग किया जाता है - नीचे दिए गए कोड के साथ:

function arrayBufferGen(str, cb) {

  var b = new Blob([str]);
  var f = new FileReader();

  f.onload = function(e) {
    cb(e.target.result);
  }

  f.readAsArrayBuffer(b);

}

ध्यान दें कि यह कितना साफ और कम फूला हुआ है, यह पदावनत विधि की तुलना में है ... हाँ, यह निश्चित रूप से यहाँ पर विचार करने के लिए कुछ है।


मेरा मतलब है, हाँ, लेकिन यह कि ब्लॉब कंस्ट्रक्टर वास्तव में 2012 में वापस उपयोग करने योग्य नहीं था;)
गेंग्केव


0

मैंने इसका इस्तेमाल किया और मेरे लिए काम करता है।

function arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}



function base64ToArrayBuffer(base64) {
    var binary_string =  window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array( len );
    for (var i = 0; i < len; i++)        {
        bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes.buffer;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.