c# - 手動でエンコーディングを指定せずにC#で文字列の一貫したバイト表現を取得するにはどうすればよいですか?



15 Answers

あなたの文字列( ASCIIUTF-8 、...)のエンコーディングに依存します。

例えば:

byte[] b1 = System.Text.Encoding.UTF8.GetBytes (myString);
byte[] b2 = System.Text.Encoding.ASCII.GetBytes (myString);

エンコードが重要な理由の小さなサンプル:

string pi = "\u03a0";
byte[] ascii = System.Text.Encoding.ASCII.GetBytes (pi);
byte[] utf8 = System.Text.Encoding.UTF8.GetBytes (pi);

Console.WriteLine (ascii.Length); //Will print 1
Console.WriteLine (utf8.Length); //Will print 2
Console.WriteLine (System.Text.Encoding.ASCII.GetString (ascii)); //Will print '?'

ASCIIは特殊文字を扱うためのものではありません。

内部的には、.NET FrameworkはUTF-16を使用して文字列を表します。したがって、.NETが使用する正確なバイト数を取得したい場合は、 System.Text.Encoding.Unicode.GetBytes (...)使用しSystem.Text.Encoding.Unicode.GetBytes (...)

詳細については、.NET Framework (MSDN)の文字エンコーディングを参照してください。

c# .net string character-encoding

特定のエンコーディングを手動で指定せずに.NET(C#)のstringbyte[]に変換するにはどうすればよいですか?

私は文字列を暗号化するつもりです。 私は変換せずにそれを暗号化することができますが、私はまだエンコーディングがここでプレーするようになる理由を知りたいです。

また、なぜエンコーディングを考慮する必要がありますか? 文字列がどのバイトに格納されているかを単に取得することはできませんか? なぜ文字エンコーディングに依存するのですか?




BinaryFormatter bf = new BinaryFormatter();
byte[] bytes;
MemoryStream ms = new MemoryStream();

string orig = "喂 Hello 谢谢 Thank You";
bf.Serialize(ms, orig);
ms.Seek(0, 0);
bytes = ms.ToArray();

MessageBox.Show("Original bytes Length: " + bytes.Length.ToString());

MessageBox.Show("Original string Length: " + orig.Length.ToString());

for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo encrypt
for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo decrypt

BinaryFormatter bfx = new BinaryFormatter();
MemoryStream msx = new MemoryStream();            
msx.Write(bytes, 0, bytes.Length);
msx.Seek(0, 0);
string sx = (string)bfx.Deserialize(msx);

MessageBox.Show("Still intact :" + sx);

MessageBox.Show("Deserialize string Length(still intact): " 
    + sx.Length.ToString());

BinaryFormatter bfy = new BinaryFormatter();
MemoryStream msy = new MemoryStream();
bfy.Serialize(msy, sx);
msy.Seek(0, 0);
byte[] bytesy = msy.ToArray();

MessageBox.Show("Deserialize bytes Length(still intact): " 
   + bytesy.Length.ToString());



これはよくある質問です。 著者が質問していることを理解することは重要であり、最も一般的な必要性とは異なることがあります。 コードが必要ない場所でのコードの誤用を防ぐために、私は後で最初に答えました。

共通のニーズ

すべての文字列には文字セットとエンコーディングがあります。 System.StringオブジェクトをSystem.Byte配列に変換すると、文字セットとエンコーディングはSystem.Byteません。 大部分の用途では、必要な文字セットとエンコーディングを知っていて、.NETでは「変換を伴うコピー」が簡単です。 適切なEncodingクラスを選択するだけです。

// using System.Text;
Encoding.UTF8.GetBytes(".NET String to byte array")

変換では、ターゲットの文字セットまたはエンコーディングがソースにある文字をサポートしないケースを処理する必要があります。 いくつかの選択肢があります:例外、置換、またはスキップ。 デフォルトのポリシーは '?'を代用することです。

// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100")); 
                                                      // -> "You win ?100"

明らかに、コンバージョンは無損失であるとは限りません。

注: System.String 、ソース文字セットはUnicodeです。

唯一わかりにくいのは、.NETがその文字セットの特定のエンコーディングの名前に文字セットの名前を使用することです。 Encoding.UnicodeEncoding.Unicodeと呼びます。

それはほとんどの用途のためです。 それがあなたが必要とするものなら、ここで読むことをやめてください。 あなたがエンコーディングが何であるかを理解していないなら、楽しいjoelonsoftware.com/articles/Unicode.htmlてください。

特定のニーズ

今、質問者は「すべての文字列はバイト配列として格納されていますが、そうではないのですか?

彼は変換を望んでいません。

C#の仕様から

C#での文字と文字列の処理は、Unicodeエンコーディングを使用します。 char型はUTF-16コード単位を表し、string型はUTF-16コード単位のシーケンスを表します。

したがって、ヌル変換(つまり、UTF-16からUTF-16への変換)を求めると、望ましい結果が得られることがわかります。

Encoding.Unicode.GetBytes(".NET String to byte array")

しかし、エンコーディングの言及を避けるために、別の方法で行う必要があります。 中間データ型が受け入れ可能な場合は、概念的なショートカットがあります。

".NET String to byte array".ToCharArray()

Mehrdadの答えはBlockCopyを使ってこのChar配列をByte配列に変換する方法を示しています。 ただし、これは文字列を2回コピーします。 また、エンコード固有のコードSystem.Charも明示的に使用します。

Stringが格納されている実際のバイトに到達する唯一の方法は、ポインタを使用することです。 fixedステートメントでは、値のアドレスを取ることができます。 C#の仕様から:

[For]型stringの式、...イニシャライザは、文字列中の最初の文字のアドレスを計算します。

そのために、コンパイラは、 RuntimeHelpers.OffsetToStringDataを使用して文字列オブジェクトの他の部分にコードスキップを書き込みます。 だから、生のバイトを取得するには、単に文字列へのポインタを作成し、必要なバイト数をコピーしてください。

// using System.Runtime.InteropServices
unsafe byte[] GetRawBytes(String s)
{
    if (s == null) return null;
    var codeunitCount = s.Length;
    /* We know that String is a sequence of UTF-16 codeunits 
       and such codeunits are 2 bytes */
    var byteCount = codeunitCount * 2; 
    var bytes = new byte[byteCount];
    fixed(void* pRaw = s)
    {
        Marshal.Copy((IntPtr)pRaw, bytes, 0, byteCount);
    }
    return bytes;
}

@CodesInChaosが指摘するように、結果はマシンのエンディアンに依存します。 しかし、質問の著者はそれに関係していません。




あなたの質問の最初の部分(バイトを取得する方法)は既に他の人によって答えられましたSystem.Text.Encoding名前空間を調べてください。

私はあなたのフォローアップの質問に対処します:なぜあなたはエンコーディングを選択する必要がありますか? なぜあなたは文字列クラス自体からそれを得ることができないのですか?

答えは2つの部分に分かれています。

まず第一に、文字列クラスによって内部的に使用されるバイトは重要ではありません。あなたが想定している限り、バグを導入する可能性があります。

あなたのプログラムが完全に.Netの世界の中にあるなら、たとえあなたがネットワークを介してデータを送信しているとしても、文字列のバイト配列をまったく得ることを心配する必要はありません。 代わりに.Net Serializationを使用して、データの送信を心配してください。 実際のバイトはそれ以上心配する必要はありません。直列化フォーマッタがそれを行います。

一方、これらのバイトをどこかで送信して、.Netシリアル化ストリームからデータを引き出すことが保証できない場合はどうしたらよいでしょうか? このケースでは、明らかにこの外部システムが気にするので、エンコードについて心配する必要はありません。 したがって、文字列で使用される内部バイトは重要ではありません。エンコードを選択して、受信側でこのエンコードを明示できるようにする必要があります。

この場合、可能であれば文字列変数に格納されている実際のバイトをメモリ内で使用するほうが良いかもしれないと理解しています。 しかし、私はあなたの出力が他端で理解されていることを確認することと、あなたがあなたのエンコーディングを明示しなければならないことを保証することと比べて重要ではないことをあなたに伝えます。 さらに、内部バイトを実際に照合したい場合は、すでにUnicodeエンコーディングを選択して、そのパフォーマンスを節約することができます。

2番目の部分に私をもたらします... Unicodeエンコーディングを選ぶことは.Netに基礎となるバイトを使用するように指示します。 このエンコーディングを選択する必要があります。なぜなら、新しく育ったUnicode-Plusが出てきたときに、.Netランタイムは、あなたのプログラムを壊すことなく、より新しい、より良いエンコーディングモデルを自由に使用する必要があるからです。 しかし、現時点では、Unicodeエンコーディングを選択するだけで、必要なものが得られます。

文字列をワイヤに書き直さなければならないことを理解することも重要です。 一致するエンコーディングを使用する場合でも 、少なくともビットパターンの変換が必要です 。 コンピュータは、Big vs Little Endian、ネットワークバイトオーダー、パケット化、セッション情報などを考慮する必要があります。




まあ、私はすべての答えを読んでいて、エンコーディングを使用していたり​​、対になっていないサロゲートを削除するシリアライズについてのものでした。

例えば、パスワードハッシュなどのバイト配列を格納して作成されたSQL Serverから文字列が来ると、悪いことです。 何かを落とすと、無効なハッシュが格納されます。もしXMLに保存したいのであれば、XMLライターはそれが見つからないサロゲートの例外を削除するので、そのまま残したいと思っています。

だから、私はこのような場合にはバイト配列のBase64エンコーディングを使用しますが、ちょっと、インターネット上でC#でこれに対して唯一の解決策があり、バグがあり、唯一の方法なので、バグを修正して書きました手順。 あなたは、将来のgooglersです:

public static byte[] StringToBytes(string str)
{
    byte[] data = new byte[str.Length * 2];
    for (int i = 0; i < str.Length; ++i)
    {
        char ch = str[i];
        data[i * 2] = (byte)(ch & 0xFF);
        data[i * 2 + 1] = (byte)((ch & 0xFF00) >> 8);
    }

    return data;
}

public static string StringFromBytes(byte[] arr)
{
    char[] ch = new char[arr.Length / 2];
    for (int i = 0; i < ch.Length; ++i)
    {
        ch[i] = (char)((int)arr[i * 2] + (((int)arr[i * 2 + 1]) << 8));
    }
    return new String(ch);
}



stringbyte配列に変換するC#

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}



文字列とバイト配列の間の変換には、次のコードを使用できます。

string s = "Hello World";

// String to Byte[]

byte[] byte1 = System.Text.Encoding.Default.GetBytes(s);

// OR

byte[] byte2 = System.Text.ASCIIEncoding.Default.GetBytes(s);

// Byte[] to string

string str = System.Text.Encoding.UTF8.GetString(byte1);



重要な問題は、文字列内のグリフが32ビット(文字コードの場合は16ビット)を取るが、バイトはスペアに8ビットしかないということです。ASCII文字のみを含む文字列に自分自身を制限しない限り、1対1のマッピングは存在しません。System.Text.Encodingには、文字列をbyte []に​​マップする方法がたくさんあります。情報の損失を避けるために、バイト[]を文字列に戻す必要があるときにクライアントが使いやすいものを選択する必要があります。

Utf8は一般的なエンコーディングであり、コンパクトで損失のないものです。




つかいます:

    string text = "string";
    byte[] array = System.Text.Encoding.UTF8.GetBytes(text);

結果は次のとおりです。

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103



OPの質問に最も近いアプローチは、Tom Blodget'sです。実際にオブジェクトに入り、バイトを抽出します。Stringオブジェクトの実装に依存するので、私は最も近いと言います。

"Can't I simply get what bytes the string has been stored in?"

確かに、それは問題の根本的な誤りが起こるところです。Stringは興味深いデータ構造を持つオブジェクトです。これは、対になっていないサロゲートが格納されるためです。それは長さを保存するかもしれません。それは、それぞれのペアリングされたサロゲートへのポインタを保持して、迅速なカウントを可能にするかもしれません。これらの余分なバイトはすべて文字データの一部ではありません。

あなたが望むのは、配列内の各文字のバイトです。そして、それは 'encoding'が入るところです。デフォルトでは、UTF-16LEが得られます。ラウンドトリップ以外のバイト自体は気にしない場合は、 'default'を含む任意のエンコーディングを選択し、後で元に戻すことができます(デフォルトのエンコーディングと同じパラメータ、コードポイント、バグ修正、対になっていないサロゲートなどの許可されたもの

しかし、なぜ「エンコーディング」を魔法のままにしておきますか?エンコードを指定して、取得するバイト数を知っているのはなぜですか?

"Why is there a dependency on character encodings?"

エンコーディング(この文脈では)は単に文字列を表すバイトを意味します。文字列オブジェクトのバイトではありません。あなたは文字列が格納されているバイト数を望んでいました。これは疑問の質問です。stringのバイトは、文字列を表す連続した配列で、文字列オブジェクトに含まれる可能性のある他のバイナリデータのすべてではないことが必要でした。

つまり、文字列の格納方法は無関係です。文字列 "Encoded"をバイト配列内のバイトに置きたい。

私はTom Blogetの答えが好きです。なぜなら、彼はあなたを「文字列オブジェクトのバイト」方向に連れて行きました。しかし、それは実装に依存しています。そして、彼が内部を覗いているので、文字列のコピーを再構成するのが難しいかもしれません。

Mehrdadの反応は、概念レベルでは誤解を招くので間違っています。あなたはまだエンコードされたバイトのリストを持っています。彼の特別な解決策では、対になっていないサロゲートを保存することができます。これは実装に依存します。彼の特別な解決法はGetBytes、デフォルトでUTF-8の文字列を返すと文字列のバイトを正確に生成しません。

私はこれについて私の心を変えました(Mehrdadの解決策) - これは文字列のバイトを取得していません。文字列から作成された文字配列のバイトを取得しています。エンコードに関係なく、C#のcharデータ型は固定サイズです。これにより、一貫した長さのバイト配列を生成することができ、バイト配列のサイズに基づいて文字配列を再現することができます。したがって、エンコーディングがUTF-8であったにもかかわらず、各charが最大のutf8値に対応するために6バイトだった場合でも、それは動作します。文字のエンコーディングは重要ではありません。

しかし、変換が使用されました - 各文字は固定サイズのボックス(c#の文字タイプ)に配置されました。しかし、その表現が何であるかは問題ではありません。これは技術的にOPへの答えです。そう、もしあなたがとにかく変換​​しようとすれば...なぜエンコードしないの?




特定のエンコーディングを手動で指定せずに.NET(C#)の文字列をbyte []に​​変換するにはどうすればよいですか?

.NET のstringは、テキストをUTF-16コード単位のシーケンスとして表しているため、バイトはすでにUTF-16でメモリにエンコードされています。

Mehrdadの答え

あなたはMehrdadの答えを使うことができますが、文字はUTF-16なので実際にはエンコーディングを使用します。それはToCharArrayを呼び出し、ソースを見ることでa char[]を作成し、メモリを直接コピーします。次に、割り当てられたバイト配列にデータをコピーします。したがって、フードの下では、基になるバイトを2回コピーし、呼び出しの後に使用されないchar配列を割り当てます。

Tom Blodgetの答え

Tom Blodgetの答えは、Mehrdadよりも20-30%高速です。なぜなら、char配列を割り当ててバイトをコピーする中間段階をスキップするからです/unsafe。オプションを指定してコンパイルする必要があります。あなたが絶対にエンコーディングを使いたくないのなら、これはやり方だと思います。暗号化ログインをfixedブロック内に置くと、別のバイト配列を割り当ててそれにバイトをコピーする必要はありません。

また、なぜエンコーディングを考慮する必要がありますか?文字列がどのバイトに格納されているかを単に取得することはできませんか?なぜ文字エンコーディングに依存するのですか?

これが正しい方法ですから。string抽象です。

無効な文字で「文字列」がある場合、エンコードを使用すると問題が発生する可能性がありますが、それは起こりません。あなたが間違ったことをしている無効な文字であなたの文字列にデータを取得している場合。おそらくバイト配列またはBase64エンコーディングを使用して開始する必要があります。

使用するSystem.Text.Encoding.Unicodeと、コードはより弾力的になります。コードが実行されるシステムのendiannessを心配する必要はありません。CLRの次のバージョンで異なる内部文字エンコーディングを使用するかどうかは心配する必要はありません。

なぜあなたはエンコーディングについて心配したいのではなく、なぜそれを無視して別のものを使用したいのかという質問ではないと思います。エンコーディングは、文字列の抽象をバイト列で表現することを意味します。System.Text.Encoding.Unicodeリトルエンディアンのバイトオーダエンコーディングを提供し、現在、そして将来すべてのシステムで同じエンコーディングを実行します。




二通り:

public static byte[] StrToByteArray(this string s)
{
    List<byte> value = new List<byte>();
    foreach (char c in s.ToCharArray())
        value.Add(c.ToByte());
    return value.ToArray();
}

そして、

public static byte[] StrToByteArray(this string s)
{
    s = s.Replace(" ", string.Empty);
    byte[] buffer = new byte[s.Length / 2];
    for (int i = 0; i < s.Length; i += 2)
        buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
    return buffer;
}

私はトップよりもボトムを頻繁に使用する傾向があり、スピードのためにそれらをベンチマークしていません。




LINQを使った簡単なコード

string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();

編集:以下にコメントしたように、それは良い方法ではありません。

より適切なコーディングを使用してLINQを理解するために使用することはできます。

string s = "abc"
byte[] b = s.Cast<byte>().ToArray();



単にこれを使う:

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);



それはあなたがバイトを何にしたいかによって決まります

これは、タイラーのsaid、「文字列は純粋なデータではなく、informationも持っているから」この場合、情報は、ストリングが作成されたときに想定されたエンコーディングです。

文字列にバイナリデータ(テキストではなく)が格納されていると仮定します

これはOP自身の質問に対するOPのコメントに基づいており、OPのヒントをユースケースで理解すれば正しい質問です。

バイナリデータを文字列に格納することは、おそらく前述のエンコーディングのために間違ったアプローチです!どのバイナリデータが格納されているプログラムやライブラリstringbyte[]より適切な配列ではなく)は、開始前に既にバトルを失っています。RESTリクエスト/レスポンスや文字列を送信する必要のあるものでバイトを送信している場合は、Base64が適切な方法です。

エンコーディングが不明なテキスト文字列がある場合

誰もがこの間違った質問に間違って答えました。

文字列がそのまま見える場合は、エンコーディング(好ましくはUTFで始まるもの)を選択し、対応するSystem.Text.Encoding.???.GetBytes()関数を使用して、エンコーディングを選択したバイトを誰にでも伝えます。




Related