c# - 比較 - vb.net string 文字コード 変換
手動でエンコーディングを指定せずにC#で文字列の一貫したバイト表現を取得するにはどうすればよいですか? (20)
なぜエンコーディングを考慮すべきかを説明してください。 文字列がどのバイトに格納されているかを単に取得することはできませんか? なぜこのエンコーディングに依存するのですか?
"文字列のバイト"のようなものは存在しないからです。
文字列(またはより一般的にはテキスト)は、文字、数字、およびその他の記号で構成されます。 それで全部です。 しかし、コンピュータは文字について何も知らない。 バイトのみを処理できます。 したがって、コンピュータを使用してテキストを保存または送信する場合は、文字をバイトに変換する必要があります。 あなたはどうやってそれをしますか? ここで、エンコードがシーンに来る場所があります。
エンコーディングは、論理文字を物理バイトに変換する慣例に過ぎません。 最も簡単で最もよく知られているエンコーディングはASCIIです。英語で書くのに必要なものはすべてです。 他の言語では、より完全なエンコーディングが必要です。現在、最も安全な選択肢はUnicodeです。
要するに、「エンコーディングを使用せずに文字列のバイトを取得する」ことは、「言語を使用せずにテキストを書き込む」ほど不可能です。
ところで、私はこの知恵の小さな部分を読むことを強くお勧めします。 joelonsoftware.com/articles/Unicode.html
特定のエンコーディングを手動で指定せずに.NET(C#)のstring
をbyte[]
に変換するにはどうすればよいですか?
私は文字列を暗号化するつもりです。 私は変換せずにそれを暗号化することができますが、私はまだエンコーディングがここでプレーするようになる理由を知りたいです。
また、なぜエンコーディングを考慮する必要がありますか? 文字列がどのバイトに格納されているかを単に取得することはできませんか? なぜ文字エンコーディングに依存するのですか?
特定のエンコーディングを手動で指定せずに.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
リトルエンディアンのバイトオーダエンコーディングを提供し、現在、そして将来すべてのシステムで同じエンコーディングを実行します。
ここの答えとは逆に、バイトを解釈する必要がない場合は、エンコードについて心配する必要はありません!
あなたが言及したように、あなたの目標は、単純に、 "文字列が格納されているバイト数を取得する"ことです。
(もちろん、バイトから文字列を再構成できるようにするためです。)
これらの目標のために、私は正直なところ、人々があなたにエンコーディングが必要であることを伝え続けている理由を理解していません 。 あなたは確かにこれのためのエンコーディングを心配する必要はありません。
代わりにこれを行う:
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
static string GetString(byte[] bytes)
{
char[] chars = new char[bytes.Length / sizeof(char)];
System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}
あなたのプログラム(または他のプログラム)が何らかの理由でバイトを解釈しようとしていない限り、あなたが意図していないことは明らかですが、このアプローチには何も問題ありません ! エンコードについて心配しておけば、本当の理由がなくてもあなたの人生はもっと複雑になります。
このアプローチのさらなる利点:
とにかく元の文字列を再構築することができるので、文字列に無効な文字が含まれていても問題ありません!
あなたはバイトだけを見ているので、それは同じようにエンコードされ、デコードされます 。
特定のエンコーディングを使用した場合、無効な文字をエンコード/デコードする際に問題が発生します。
1文字は1バイト以上 (約6まで)で表現でき、異なるエンコーディングはこれらのバイトを別々に扱うため、エンコーディングを考慮する必要があります。
ジョエルはこれに関する投稿をしています:
string
をbyte
配列に変換するC#
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding=new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}
LINQを使った簡単なコード
string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();
編集:以下にコメントしたように、それは良い方法ではありません。
より適切なコーディングを使用してLINQを理解するために使用することはできます。
string s = "abc"
byte[] b = s.Cast<byte>().ToArray();
あなたの文字列( ASCII 、 UTF-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)の文字エンコーディングを参照してください。
あなたの質問の最初の部分(バイトを取得する方法)は既に他の人によって答えられましたSystem.Text.Encoding
名前空間を調べてください。
私はあなたのフォローアップの質問に対処します:なぜあなたはエンコーディングを選択する必要がありますか? なぜあなたは文字列クラス自体からそれを得ることができないのですか?
答えは2つの部分に分かれています。
まず第一に、文字列クラスによって内部的に使用されるバイトは重要ではありません。あなたが想定している限り、バグを導入する可能性があります。
あなたのプログラムが完全に.Netの世界の中にあるなら、たとえあなたがネットワークを介してデータを送信しているとしても、文字列のバイト配列をまったく得ることを心配する必要はありません。 代わりに.Net Serializationを使用して、データの送信を心配してください。 実際のバイトはそれ以上心配する必要はありません。直列化フォーマッタがそれを行います。
一方、これらのバイトをどこかで送信して、.Netシリアル化ストリームからデータを引き出すことが保証できない場合はどうしたらよいでしょうか? このケースでは、明らかにこの外部システムが気にするので、エンコードについて心配する必要はありません。 したがって、文字列で使用される内部バイトは重要ではありません。エンコードを選択して、受信側でこのエンコードを明示できるようにする必要があります。
この場合、可能であれば文字列変数に格納されている実際のバイトをメモリ内で使用するほうが良いかもしれないと理解しています。 しかし、私はあなたの出力が他端で理解されていることを確認することと、あなたがあなたのエンコーディングを明示しなければならないことを保証することと比べて重要ではないことをあなたに伝えます。 さらに、内部バイトを実際に照合したい場合は、すでにUnicode
エンコーディングを選択して、そのパフォーマンスを節約することができます。
2番目の部分に私をもたらします... Unicode
エンコーディングを選ぶことは.Netに基礎となるバイトを使用するように指示します。 このエンコーディングを選択する必要があります。なぜなら、新しく育ったUnicode-Plusが出てきたときに、.Netランタイムは、あなたのプログラムを壊すことなく、より新しい、より良いエンコーディングモデルを自由に使用する必要があるからです。 しかし、現時点では、Unicodeエンコーディングを選択するだけで、必要なものが得られます。
文字列をワイヤに書き直さなければならないことを理解することも重要です。 一致するエンコーディングを使用する場合でも 、少なくともビットパターンの変換が必要です 。 コンピュータは、Big vs Little Endian、ネットワークバイトオーダー、パケット化、セッション情報などを考慮する必要があります。
これを試してみましょう。
System.Text.Encoding.UTF8.GetBytes("TEST String");
つかいます:
string text = "string";
byte[] array = System.Text.Encoding.UTF8.GetBytes(text);
結果は次のとおりです。
[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
二通り:
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;
}
私はトップよりもボトムを頻繁に使用する傾向があり、スピードのためにそれらをベンチマークしていません。
単にこれを使う:
byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
文字列とバイト配列の間の変換には、次のコードを使用できます。
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);
.NETはUnicodeをサポートし、UnicodeはUTFと呼ばれるいくつかの差分エンコーディングを標準化しています。次の事実により、文字列はバイト配列に変換できます。それらはバイト表現の長さが異なりますが、文字列がエンコードされている場合はそれを文字列に戻すことができますが、文字列が1つのUTFでエンコードされ、異なるUTFの前提でデコードできればアップ。
また、.NETは非Unicodeエンコーディングをサポートしますが、一般的な場合は有効ではありません(Unicodeコードポイントの限定されたサブセットがASCIIなどの実際の文字列で使用されている場合にのみ有効です)。内部的には、.NETはUTF-16をサポートしますが、ストリーム表現では通常UTF-8が使用されます。それはまた、インターネットの標準事実でもあります。
驚くことではないが、文字列をバイト配列と直列化した配列にシリアライズSystem.Text.Encoding
することは、抽象クラスであるクラスによってサポートされています。その派生クラスは具体的なエンコーディングをサポートしています:ASCIIEncoding
4つのSystem.Text.UnicodeEncoding
UTF (UTF-16をサポート)
を使用してバイトの配列にシリアル化しますSystem.Text.Encoding.GetBytes
。逆の操作で使用しますSystem.Text.Encoding.GetChars
。この関数は文字の配列を返します。したがって、文字列を取得するには、文字列コンストラクタを使用しますSystem.String(char[])
。
このページを参照してください。
例:
string myString = //... some string
System.Text.Encoding encoding = System.Text.Encoding.UTF8; //or some other, but prefer some UTF is Unicode is used
byte[] bytes = encoding.GetBytes(myString);
//next lines are written in response to a follow-up questions:
myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);
myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);
//how many times shall I repeat it to show there is a round-trip? :-)
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への答えです。そう、もしあなたがとにかく変換しようとすれば...なぜエンコードしないの?
ここでの私の危険な実装であるString
へByte[]
の変換は:
public static unsafe Byte[] GetBytes(String s)
{
Int32 length = s.Length * sizeof(Char);
Byte[] bytes = new Byte[length];
fixed (Char* pInput = s)
fixed (Byte* pBytes = bytes)
{
Byte* source = (Byte*)pInput;
Byte* destination = pBytes;
if (length >= 16)
{
do
{
*((Int64*)destination) = *((Int64*)source);
*((Int64*)(destination + 8)) = *((Int64*)(source + 8));
source += 16;
destination += 16;
}
while ((length -= 16) >= 16);
}
if (length > 0)
{
if ((length & 8) != 0)
{
*((Int64*)destination) = *((Int64*)source);
source += 8;
destination += 8;
}
if ((length & 4) != 0)
{
*((Int32*)destination) = *((Int32*)source);
source += 4;
destination += 4;
}
if ((length & 2) != 0)
{
*((Int16*)destination) = *((Int16*)source);
source += 2;
destination += 2;
}
if ((length & 1) != 0)
{
++source;
++destination;
destination[0] = source[0];
}
}
}
return bytes;
}
たとえエレガントではないとしても、受け入れられたアンサーのものよりも速いです。ストップウォッチのベンチマークは10000000回を超えています:
[Second String: Length 20]
Buffer.BlockCopy: 746ms
Unsafe: 557ms
[Second String: Length 50]
Buffer.BlockCopy: 861ms
Unsafe: 753ms
[Third String: Length 100]
Buffer.BlockCopy: 1250ms
Unsafe: 1063ms
それを使用するには、プロジェクトのビルドプロパティで「危険なコードを許可する」にチェックを入れなければなりません。.NET Framework 3.5では、このメソッドをString拡張として使用することもできます。
public static unsafe class StringExtensions
{
public static Byte[] ToByteArray(this String s)
{
// Method Code
}
}
次のコードを使用しstring
て、a byte array
を.NET に変換できます。
string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
私は確信していませんが、文字列はCharsの配列として情報を格納していると思います。これはバイトでは効率が悪いです。特に、Charの定義は "Unicode文字を表します"。
このサンプルサンプルを取る:
String str = "asdf éß";
String str2 = "asdf gh";
EncodingInfo[] info = Encoding.GetEncodings();
foreach (EncodingInfo enc in info)
{
System.Console.WriteLine(enc.Name + " - "
+ enc.GetEncoding().GetByteCount(str)
+ enc.GetEncoding().GetByteCount(str2));
}
UTF-8の回答は最初の9バイトだけで、2番目の質問ではわずか7バイトであるのに対し、Unicodeの回答は両方のインスタンスで14バイトであることに注意してください。
したがって、文字列によって使用されるバイトだけを使用したい場合は、単純に使用しますがEncoding.Unicode
、格納領域では非効率です。
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());
byte[] strToByteArray(string str)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetBytes(str);
}