31


28

JavaScript文字列からバイトを読み取る

私はJavaScriptでバイナリデータを含む文字列を持っています。 今、私はそれから例えば整数を読みたい。 だから私は最初の4文字を得て、 `charCodeAt`を使い、いくつかのシフトをするなどしています。 整数を取得します。

問題は、JavaScriptの文字列が(ASCIIではなく)UTF-16であり、 `charCodeAt`がしばしば256より高い値を返すことです。

https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/charCodeAt [Mozilla reference]には、「最初の128個のUnicodeコードポイントはASCII文字エンコーディングと直接一致する」と記載されています。 (128より大きいASCII値はどうですか?)

`charCodeAt`の結果をどうやってASCII値に変換することができますか? それとも、4文字の文字列を4バイトの整数に変換するためのより良い方法はありますか?

9 Answer


35


比較的簡単なビット操作でこれを実現できると思います。

function stringToBytes(str){var ch、st、re = []; (var i = 0; i <str。length; i){ch = str.charCodeAt(i); // char st = [];を取得します。 // "stack"を設定するdo {st.push(ch

stringToBytes( "A \ u1242B \ u4123C"); // [65、18、66、66、65、35、67]

バイト配列をあたかもそれがメモリであるかのように読んでそれをより大きな数に加算することによって出力を合計することは簡単な問題であるべきです。

関数getIntAt(arr、offs){return(arr [offs 0] << 24)(arr [offs 1] << 16)(arr [offs 2] << 8)arr [offs 3]; }

function getWordAt(arr、offs){return(arr [offs 0] << 8)arr [offs 1]; }

'\\ u' getWordAt(stringToBytes( "A \ u1242")、1).toString(16); // "1242"


15


Borgarの答えは正しいようです。

一点を明確にしたかっただけです。 Javascriptはビット単位の演算を32ビットの符号付き整数として扱います。最後の(最も左の)ビットは符号ビットです。 つまり

getIntAt([0x7f、0,0,0]、0).toString(16)// "7f000000"

getIntAt([0x80,0,0,0]、0).toString(16)// "-80000000"

ただし、オクテットデータ処理(ネットワークストリームなど)では、通常は 'unsigned int’表現が必要です。 これは、内部的にJavascriptに符号なしとして扱うように指示する '>>> 0'(ゼロフィル右シフト)演算子を追加することによって達成できます。

function getUIntAt(arr、offs){return(arr [offs 0] << 24)(arr [offs 1] << 16)(arr [offs 2] << 8)arr [offs 3] >>> 0; }

getUIntAt([0x80,0,0,0]、0).toString(16)// "80000000"


13


utf-8文字列をバイト配列にエンコードおよびデコードする方法は2つあります。

var utf8 = {}

utf8.toByteArray = function(str){var byteArray = []; (var i = 0; i <str.length; i)である場合(str.charCodeAt(i)<= 0x7F)byteArray.push(str.charCodeAt(i)); else {var h = encodeURIComponent(str.charAt(i))。substr(1).split( '%'); (var j = 0; j <h.length; j)byteArray.push(parseInt(h [j]、16)); byteArrayを返します。 ;

utf8.parse = function(byteArray){var str = ''; (var i = 0; i <byteArray.length; i)の場合str = byteArray [i] <= 0x7F? byteArray [i] === 0x25? "%25"://%String.fromCharCode(byteArray [i]): "%" byteArray [i] .toString(16).toUpperCase(); decodeURIComponent(str)を返します。 ;

// sample
var str = "Да!"; var ba = utf8.toByteArray(str);警戒(ba); // 208、148、208、176、33 alert(ba.length); // 5 alert(utf8.parse(ba)); //Да!


9


https://stackoverflow.com/questions/1240408/reading-bytes-from-a-javascript-string/8639991#answer-1242596[@Borgar]が質問に正しく答えている間、彼の解決策はかなり遅いです。 それを突き止めるには少し時間がかかりました(私はより大きなプロジェクトのどこかで彼の機能を使用しました)ので、私は私の洞察を共有すると思いました。

私は @Kadmのようなものを持っていました。 これは数パーセント速いわけではなく、500倍速いということです(誇張しないでください!)。 私は little benchmarkを書いたので、あなたは自分でそれを見ることができます:)

function stringToBytesFaster(str){var ch、st、re = []、j = 0; (var i = 0; i <str。length; i){ch = str.charCodeAt(i); if(ch <127){re [j] = ch


5


Borgaのソリューションは完璧に機能します。 より具体的な実装が必要な場合は、http://blog.vjeux.com/2010/javascript/javascript-binary-reader.html [BjeRe]のBinaryReaderクラスを参照してください(これは、レコードは、Jonas Raoni Soares Silvaの binary-parserクラスに基づいています。


3


そもそもどうやってバイナリデータを文字列に変換したのですか? バイナリデータを文字列にエンコードする方法は重要な考慮事項であり、先に進む前にその質問に対する回答が必要です。

バイナリデータを文字列に変換するために私が知っている1つの方法は、XHRオブジェクトを使用し、それをUTF-16を想定するように設定することです。

utf-16になると、 `" .... "。charCodeAt(0)`を使用して文字列から16ビットの数値を取得できます

これは0から65535の間の数になります

そして、あなたが好きなら、あなたはこのように0から255の間の2つの数にその数を変換することができます:

var leftByte = mynumber>>>8;
var rightByte = mynumber&255;


3


ボーグソリューション*改善*:

...
do {st.unshift(ch
...


3


1つのすばやく簡単な方法は、encodeURIとunescapeの組み合わせを使用することです。

t = []; (s = unescape(encodeURI( "zażółćgęśląjaźń")))、i = 0; i

それがなぜうまくいくのか、おそらくいくつかの説明が必要です。

encodeURI( "zażółćgęśląjaźń")

戻る

"za%C5%BC%C3%B3%C5%82%C4%87%20g%C4%99%C5%9Bl%C4%85%20ja%C5%BA%C5%84"

これを - よく見ると - > 127の値を持つすべての文字が(おそらく複数の)16進数バイト表現で置き換えられた元の文字列です。 たとえば、文字 "ż"は "%C5%BC"になりました。 実際のところ、encodeURIはスペースなどの通常のASCII文字もエスケープしますが、問題ではありません。 重要なのは、この時点で元の文字列の各バイトが逐語的に( "z"、 "a"、 "g"、または "j"の場合のように)表されるか、パーセントエンコードのバイトシーケンス(オリジナルの2バイトの197と188で、%C5と%BCに変換された "ż"の場合と同様です。

さて、私たちはアンエスケープを適用します。

脱出( "za%C5%BC%C3%B3%C5%82%C4%87%20g%C4%99%C5%9Bl%C4%85%20ja%C5%BA%C5%84")

これは

"zażóÅÄgÄÅlÄjaźÅ"

あなたがネイティブのポーランド語話者でないならば、あなたは気付かないかもしれません、この結果が実際に元の "zażółćgęśląjaźń"とは全く異なるということです。 初心者のために、それは異なった数の文字を持っています:)確かに、あなたは言うことができます、大きな文字Aのこの奇妙なバージョンは標準のASCIIセットに属していません。 実際、この「Å」の値は197です。 (これは正確に16進数のC5です)。

さて、あなたが私のようなものであれば、あなたは自分自身に尋ねるでしょう:ちょっと待って…​これが本当に122、97、197、188という値を持つバイトのシーケンスで、JSが本当にUTFを使っているのならż "文字ではなく、元の"ż "?

実は、このシーケンス122、97、197、188(charCodeAtを適用したときに表示されます)は_bytes_のシーケンスではなく、_codes_のシーケンスであることがわかります。 文字 "Å"のコードは197ですが、実際には2バイト長のシーケンスです(C3 85)。

そのため、unescapeはパーセントエンコードされた文字列で発生する数字をバイト値としてではなくコードとして扱います。つまり、unescapeはマルチバイト文字については何も知らないため、バイトを1つずつデコードして値を処理します。 128よりも小さい値ですが、127以上でマルチバイトである場合はそれほど良くありません。そのような場合、エスケープ解除は単に、要求されたバイト値に等しいコードを持つマルチバイト文字を返します。 この「バグ」は実際には便利な機能です。


2


あなたの目的は文字列から任意のバイトを読み込むことであると私は少しの間仮定するつもりです。 私の最初の提案はあなたの文字列表現をバイナリデータの16進表現にすることでしょう。

16進数から数値への変換を使用して値を読み取ることができます。

var BITS_PER_BYTE = 8;

function readBytes(hexString、numBytes){return Number(parseInt(hexString.substr(0、numBytes *(BITS_PER_BYTE / 4))、16)); }

関数removeBytes(hexString、numBytes){return hexString.substr(numBytes *(BITS_PER_BYTE / BITS_PER_CHAR)); }

それから関数はあなたが欲しいものを何でも読むために使うことができます:

var hex = '4ef2c3382fd';警戒( '私たちは持っていた:' 16進);

var intVal = readBytes(hex、2); alert( '2バイト:' intVal.toString(2));

hex = removeBytes(hex、2);警戒( '今我々は持っている:' 16進);

あなたはそれからあなたが望むバイト列を解釈することができます。

お役に立てれば! 乾杯!