文字列をBytearrayに変換する方法


90

JavaScriptを使用してbytearrayの文字列を変換するにはどうすればよいですか。出力は、以下のC#コードと同等である必要があります。

UnicodeEncoding encoding = new UnicodeEncoding();
byte[] bytes = encoding.GetBytes(AnyString);

UnicodeEncodingは、デフォルトでリトルエンディアンのUTF-16であるため。

編集:上記のC#コードを使用して、bytearrayで生成されたクライアント側をサーバー側で生成されたものと一致させる必要があります。


3
javascriptは、BLOBで簡単に使用できることでよく知られているわけではありません。文字列をJSONで送信してみませんか?
MarcGravell

たぶん、あなたは見ることができ、ここで...
V4Vendetta

2
Javascript文字列はUTF-16です、またはこれをすでに知っていましたか?
ケビン

2
まず第一に、なぜこれをjavascriptに変換する必要があるのですか?
BreakHead 2011年

17
文字列はエンコードされません。はい、内部的にはバイトとして表され、エンコーディングがありますが、スクリプトレベルでは本質的に無意味です。文字列は、文字の論理的なコレクションです。文字をエンコードするには、エンコードスキームを明示的に選択する必要があります。これを使用して、各文字コードを1バイト以上のシーケンスに変換できます。以下のこの質問に対する答えは、charCodeAtを呼び出し、その値を「bytes」と呼ばれる配列に貼り付けるため、ごみです。こんにちは!charCodeAtは255より大きい値を返す可能性があるため、バイトではありません。
Triynko 2013

回答:


21

これを実行しているC#では

UnicodeEncoding encoding = new UnicodeEncoding();
byte[] bytes = encoding.GetBytes("Hello");

で配列を作成します

72,0,101,0,108,0,108,0,111,0

バイト配列

コードが255より大きい文字の場合、次のようになります。

バイト配列

JavaScriptで非常によく似た動作が必要な場合は、これを行うことができます(v2はもう少し堅牢なソリューションですが、元のバージョンは0x00〜0xffでのみ機能します)

var str = "Hello竜";
var bytes = []; // char codes
var bytesv2 = []; // char codes

for (var i = 0; i < str.length; ++i) {
  var code = str.charCodeAt(i);
  
  bytes = bytes.concat([code]);
  
  bytesv2 = bytesv2.concat([code & 0xff, code / 256 >>> 0]);
}

// 72, 101, 108, 108, 111, 31452
console.log('bytes', bytes.join(', '));

// 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 220, 122
console.log('bytesv2', bytesv2.join(', '));


1
私はすでにこれを試しましたが、これは上記のC#コードとは異なる結果をもたらします。この場合のように、C#コード出力バイト配列は= 72,0,101,0,108,0,108,0,111,0であり、機能しないように両方を一致させる必要があります。
shas 2011年

2
@shas以前はFirefox4でのみテストしました。更新されたバージョンはFirefox4、Chrome 13、IE9でテストしました。
BrunoLM 2011年

40
文字列にUnicode文字が含まれている場合、charCodeAt(i)は> 255になることに注意してください。これは、おそらく必要なものではありません。
broofa 2012

23
ええ、これは正しくありません。charCodeAtはバイトを返しません。255より大きい値を「バイト」と呼ばれる配列にプッシュすることは意味がありません。非常に誤解を招く。この関数はエンコードをまったく実行せず、文字コードを配列に貼り付けるだけです。
Triynko 2013

1
何もエンコードされていないため、この回答が正しいとマークされている理由がわかりません。
AB

32

node.jsで機能するソリューションを探している場合は、次を使用できます。

var myBuffer = [];
var str = 'Stack Overflow';
var buffer = new Buffer(str, 'utf16le');
for (var i = 0; i < buffer.length; i++) {
    myBuffer.push(buffer[i]);
}

console.log(myBuffer);

3
これはnode.js用ですが、質問はブラウザーで機能するソリューションを探していると思います。それにもかかわらず、この質問に対する他のほとんどの回答とは異なり、正しく機能するため、+ 1します。
ダニエルキャシディ

これは機能しますが、はるかに単純なコードはfunction convertString(myString){var myBuffer = new Buffer(myString、 'utf16le');です。console.log(myBuffer); myBufferを返します。}
フィリップルトビッツ

16

C#とJavaは等しいバイト配列を生成すると思います。非ASCII文字がある場合は、0を追加するだけでは不十分です。私の例にはいくつかの特殊文字が含まれています。

var str = "Hell ö € Ω 𝄞";
var bytes = [];
var charCode;

for (var i = 0; i < str.length; ++i)
{
    charCode = str.charCodeAt(i);
    bytes.push((charCode & 0xFF00) >> 8);
    bytes.push(charCode & 0xFF);
}

alert(bytes.join(' '));
// 0 72 0 101 0 108 0 108 0 32 0 246 0 32 32 172 0 32 3 169 0 32 216 52 221 30

C#がBOM(Byte Order Marks)を配置するかどうかはわかりませんが、UTF-16を使用する場合、JavaString.getBytesは次のバイトを追加します:254255。

String s = "Hell ö € Ω ";
// now add a character outside the BMP (Basic Multilingual Plane)
// we take the violin-symbol (U+1D11E) MUSICAL SYMBOL G CLEF
s += new String(Character.toChars(0x1D11E));
// surrogate codepoints are: d834, dd1e, so one could also write "\ud834\udd1e"

byte[] bytes = s.getBytes("UTF-16");
for (byte aByte : bytes) {
    System.out.print((0xFF & aByte) + " ");
}
// 254 255 0 72 0 101 0 108 0 108 0 32 0 246 0 32 32 172 0 32 3 169 0 32 216 52 221 30

編集:

特殊文字(U + 1D11E)MUSICAL SYMBOL G CLEF(BPMの外部、UTF-16で2バイトだけでなく4バイトも使用)を追加しました。

現在のJavaScriptバージョンは内部で「UCS-2」を使用しているため、この記号は2つの通常の文字のスペースを取ります。

よくわかりませんが、使用する場合 charCodeAtわかりすると、UTF-16でも使用されているサロゲートコードポイントが正確に取得されるため、BPM以外の文字は正しく処理されます。

この問題は絶対に重要です。使用するJavaScriptのバージョンとエンジンによって異なる場合があります。したがって、信頼性の高いソリューションが必要な場合は、以下を参照してください。


1
まだ完全な答えではありません。UTF16は、16ビットチャンクを使用して文字を表す可変長エンコーディングです。1文字は、文字コード値の大きさに応じて、2バイトまたは4バイトとしてエンコードされます。この関数は最大2バイトを書き込むため、すべてのUnicode文字コードポイントを処理できるわけではなく、UTF16エンコーディングの完全な実装ではありません。
Triynko 2013

私の編集とテストの後の@Triynko、あなたはまだこれが完全な答えではないと思いますか?はいの場合、答えはありますか?
hgoebl 2013年

2
@Triynkoあなたは半分正しいですが、実際にはこの答えは正しく機能します。JavaScript文字列は、実際にはUnicodeコードポイントのシーケンスではなく、UTF-16コードユニットのシーケンスです。名前にもかかわらず、charCodeAt0〜65535の範囲のUTF-16コードユニットを返します。2バイトの範囲外の文字は、UTF-16と同様に、サロゲートペアとして表されます。(ちなみに、これはJavaやC#を含む他のいくつかの言語の文字列にも当てはまります。)
Daniel Cassidy

ちなみに、(charCode & 0xFF00) >> 8冗長なので、シフトする前にマスクする必要はありません。
パトリック・ロバーツ

15

2018年の最も簡単な方法はTextEncoderですが、返される要素はバイト配列ではなく、Uint8Arrayです。(そして、すべてのブラウザがそれをサポートしているわけではありません)

let utf8Encode = new TextEncoder();
utf8Encode.encode("eee")
> Uint8Array [ 101, 101, 101 ]

これは独特です。utf8Decodeとutf8Encodeが機能するため、異なる変数名を使用するとは思いません。
ユニヘドロン

TextDecoderを使用して次をデコードできますnew TextDecoder().decode(new TextEncoder().encode(str)) == str
フォン

TextEncodercaniuse
Fons

11

UTF-16バイト配列

JavaScriptは、C#と同様に文字列をUTF-16としてエンコードするUnicodeEncodingため、バイト配列はcharCodeAt()、を使用して正確に一致し、返された各バイトペアを次のように2つの別々のバイトに分割する必要があります。

function strToUtf16Bytes(str) {
  const bytes = [];
  for (ii = 0; ii < str.length; ii++) {
    const code = str.charCodeAt(ii); // x00-xFFFF
    bytes.push(code & 255, code >> 8); // low, high
  }
  return bytes;
}

例えば:

strToUtf16Bytes('🌵'); 
// [ 60, 216, 53, 223 ]

ただし、UTF-8バイト配列を取得する場合は、バイトをトランスコードする必要があります。

UTF-8バイト配列

解決策はやや自明ではないように感じますが、トラフィックの多い本番環境で以下のコードを使用して大成功を収めました(元のソース)。

また、興味のある読者のために、PHPなどの他の言語で報告された文字列の長さを処理するのに役立つUnicodeヘルパーを公​​開しました。

/**
 * Convert a string to a unicode byte array
 * @param {string} str
 * @return {Array} of bytes
 */
export function strToUtf8Bytes(str) {
  const utf8 = [];
  for (let ii = 0; ii < str.length; ii++) {
    let charCode = str.charCodeAt(ii);
    if (charCode < 0x80) utf8.push(charCode);
    else if (charCode < 0x800) {
      utf8.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
    } else if (charCode < 0xd800 || charCode >= 0xe000) {
      utf8.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
    } else {
      ii++;
      // Surrogate pair:
      // UTF-16 encodes 0x10000-0x10FFFF by subtracting 0x10000 and
      // splitting the 20 bits of 0x0-0xFFFFF into two halves
      charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(ii) & 0x3ff));
      utf8.push(
        0xf0 | (charCode >> 18),
        0x80 | ((charCode >> 12) & 0x3f),
        0x80 | ((charCode >> 6) & 0x3f),
        0x80 | (charCode & 0x3f),
      );
    }
  }
  return utf8;
}

そしてこれの逆は何ですか?
simbo19 0519年

逆関数は、「UTF-8バイト配列をネイティブUTF-16文字列に変換する」と説明します。私は逆を生成したことはありません。myc envでは、API出力をバイト範囲ではなく文字範囲に変更してこのコードを削除してから、ルーン文字を使用して範囲を解析しました。
jchook

私はこれがこの質問に対する受け入れられた答えであるべきだと提案します。
LeaveTheCapital

10

@hgoeblの答えに触発されました。彼のコードはUTF-16用で、US-ASCII用のものが必要でした。したがって、US-ASCII、UTF-16、およびUTF-32をカバーするより完全な回答があります。

/**@returns {Array} bytes of US-ASCII*/
function stringToAsciiByteArray(str)
{
    var bytes = [];
   for (var i = 0; i < str.length; ++i)
   {
       var charCode = str.charCodeAt(i);
      if (charCode > 0xFF)  // char > 1 byte since charCodeAt returns the UTF-16 value
      {
          throw new Error('Character ' + String.fromCharCode(charCode) + ' can\'t be represented by a US-ASCII byte.');
      }
       bytes.push(charCode);
   }
    return bytes;
}
/**@returns {Array} bytes of UTF-16 Big Endian without BOM*/
function stringToUtf16ByteArray(str)
{
    var bytes = [];
    //currently the function returns without BOM. Uncomment the next line to change that.
    //bytes.push(254, 255);  //Big Endian Byte Order Marks
   for (var i = 0; i < str.length; ++i)
   {
       var charCode = str.charCodeAt(i);
       //char > 2 bytes is impossible since charCodeAt can only return 2 bytes
       bytes.push((charCode & 0xFF00) >>> 8);  //high byte (might be 0)
       bytes.push(charCode & 0xFF);  //low byte
   }
    return bytes;
}
/**@returns {Array} bytes of UTF-32 Big Endian without BOM*/
function stringToUtf32ByteArray(str)
{
    var bytes = [];
    //currently the function returns without BOM. Uncomment the next line to change that.
    //bytes.push(0, 0, 254, 255);  //Big Endian Byte Order Marks
   for (var i = 0; i < str.length; i+=2)
   {
       var charPoint = str.codePointAt(i);
       //char > 4 bytes is impossible since codePointAt can only return 4 bytes
       bytes.push((charPoint & 0xFF000000) >>> 24);
       bytes.push((charPoint & 0xFF0000) >>> 16);
       bytes.push((charPoint & 0xFF00) >>> 8);
       bytes.push(charPoint & 0xFF);
   }
    return bytes;
}

UTF-8は可変長であり、エンコーディングを自分で作成する必要があるため、含まれていません。UTF-8とUTF-16は可変長です。UTF-8、UTF-16、およびUTF-32には、名前が示すように最小ビット数があります。UTF-32文字のコードポイントが65の場合、先行する0が3つあることを意味します。ただし、UTF-16の同じコードには、先頭に0が1つしかありません。一方、US-ASCIIは固定幅の8ビットであるため、バイトに直接変換できます。

String.prototype.charCodeAt最大2バイトを返し、UTF-16と完全に一致します。ただしString.prototype.codePointAt、ECMAScript 6(Harmony)提案の一部であるUTF-32が必要です。charCodeAtはUS-ASCIIが表すことができるよりも多くの可能な文字である2バイトを返すので、関数stringToAsciiByteArrayは文字を半分に分割してどちらかまたは両方のバイトを取る代わりにそのような場合にスローします。

文字エンコードは重要であるため、この答えは重要であることに注意してください。必要なバイト配列の種類は、それらのバイトで表す文字エンコードによって異なります。

javascriptには、UTF-16またはUCS-2のいずれかを内部的に使用するオプションがありますが、UTF-16のように動作するメソッドがあるため、ブラウザーがUCS-2を使用する理由がわかりません。https://mathiasbynens.be/notes/javascript-encodingも参照してください。

はい、質問が4歳であることは知っていますが、自分でこの回答が必要でした。


以下のためのノードのバッファの結果が'02'ある[ 48, 0, 50, 0 ]場所あなたのようにstringToUtf16ByteArray関数が戻ります[ 0, 48, 0, 50 ]。どちらが正しいですか?
pkyeck 2018年

@pkyeck上記のstringToUtf16ByteArray関数は、BOMなしのUTF-16BEを返します。ノードから指定した例は、BOMなしのUTF-16LEです。ビッグエンディアンはリトルエンディアンよりも普通だと思っていましたが、間違っている可能性があります。
skySpiral7 2018年

2

私は答えにコメントすることができないので、私はジン・イズレールの答えに基づいています

var myBuffer = [];
var str = 'Stack Overflow';
var buffer = new Buffer(str, 'utf16le');
for (var i = 0; i < buffer.length; i++) {
    myBuffer.push(buffer[i]);
}

console.log(myBuffer);

ブラウザでNode.jsバッファを使用する場合は、これを使用できると言ってください。

https://github.com/feross/buffer

したがって、トム・スティッケルの異議は有効ではなく、その答えは確かに有効な答えです。


1
String.prototype.encodeHex = function () {
    return this.split('').map(e => e.charCodeAt())
};

String.prototype.decodeHex = function () {    
    return this.map(e => String.fromCharCode(e)).join('')
};

4
他の回答の1つではなく、このアプローチを選択する理由を説明するために、コードと一緒にテキストを提供すると便利です。
nightOwl888 2018

このアプローチは他のアプローチよりも単純ですが、同じことをします。それが私が何も書かなかった理由です。
ファビオマシエル2018

encodeHexバイトではなく、16ビットの数値の配列を返します。
パブロ

0

私がその場で思いついた最善の解決策は(おそらく粗雑ですが)次のようになります。

String.prototype.getBytes = function() {
    var bytes = [];
    for (var i = 0; i < this.length; i++) {
        var charCode = this.charCodeAt(i);
        var cLen = Math.ceil(Math.log(charCode)/Math.log(256));
        for (var j = 0; j < cLen; j++) {
            bytes.push((charCode << (j*8)) & 0xFF);
        }
    }
    return bytes;
}

私はこの質問が1年以上ここにあることに気づきましたが。


2
これは正しく機能しません。可変長文字ロジックが正しくありません。UTF-16には8ビット文字がありません。名前にもかかわらず、charCodeAt16ビットUTF-16コードユニットを返すため、可変長ロジックは必要ありません。charCodeAtを呼び出し、結果を2つの8ビットバイトに分割し、それらを出力配列に詰め込むことができます(質問ではUTF-16LEが要求されるため、最下位バイトが最初になります)。
ダニエルキャシディ

0

私は質問がほぼ4歳であることを知っています、しかしこれは私とスムーズに働いたものです:

String.prototype.encodeHex = function () {
  var bytes = [];
  for (var i = 0; i < this.length; ++i) {
    bytes.push(this.charCodeAt(i));
  }
  return bytes;
};

Array.prototype.decodeHex = function () {    
  var str = [];
  var hex = this.toString().split(',');
  for (var i = 0; i < hex.length; i++) {
    str.push(String.fromCharCode(hex[i]));
  }
  return str.toString().replace(/,/g, "");
};

var str = "Hello World!";
var bytes = str.encodeHex();

alert('The Hexa Code is: '+bytes+' The original string is:  '+bytes.decodeHex());

または、文字列のみを操作し、配列を操作しない場合は、次を使用できます。

String.prototype.encodeHex = function () {
  var bytes = [];
  for (var i = 0; i < this.length; ++i) {
    bytes.push(this.charCodeAt(i));
  }
  return bytes.toString();
};

String.prototype.decodeHex = function () {    
  var str = [];
  var hex = this.split(',');
  for (var i = 0; i < hex.length; i++) {
    str.push(String.fromCharCode(hex[i]));
  }
  return str.toString().replace(/,/g, "");
};

var str = "Hello World!";
var bytes = str.encodeHex();

alert('The Hexa Code is: '+bytes+' The original string is:  '+bytes.decodeHex());


2
この種の作品ですが、非常に誤解を招く恐れがあります。bytesアレイは「バイト」が含まれていない、それは、UTF-16コード単位の文字列を表す16ビットの番号を含んでいます。これは質問が求めていたものとほぼ同じですが、実際には偶然です。
ダニエルキャシディ

-1

これは、@ BrunoLMが投稿したものと同じ関数をStringプロトタイプ関数に変換したものです。

String.prototype.getBytes = function () {
  var bytes = [];
  for (var i = 0; i < this.length; ++i) {
    bytes.push(this.charCodeAt(i));
  }
  return bytes;
};

関数をそのように定義すると、任意の文字列で.getBytes()メソッドを呼び出すことができます。

var str = "Hello World!";
var bytes = str.getBytes();

31
それが参照する答えと同じように、これはまだ正しくありません。charCodeAtはバイトを返しません。255より大きい値を「バイト」と呼ばれる配列にプッシュすることは意味がありません。非常に誤解を招く。この関数はエンコードをまったく実行せず、文字コードを配列に貼り付けるだけです。UTF16エンコーディングを実行するには、文字コードを調べて、2バイトまたは4バイトのどちらで表す必要があるかを判断し(UTF16は可変長エンコーディングであるため)、各バイトを配列に個別に書き込む必要があります。
Triynko 2013

8
また、ネイティブデータ型のプロトタイプを変更することは悪い習慣です。
Andrew Lundin 2013年

@AndrewLundin、それは興味深いです...誰が言いますか?
Jerther 2015


-3

アンダースコアは必要ありません。組み込みのマップを使用するだけです。

var string = 'Hello World!';

document.write(string.split('').map(function(c) { return c.charCodeAt(); }));


1
これは、文字列をUTF-16コードポイントのシーケンスとして表す16ビットの数値の配列を返します。それはOPが求めたものではありませんが、少なくともそれはあなたを途中まで連れて行きます。
ダニエルキャシディ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.