どのようにして変換しないstring
にbyte[]
手動で特定のエンコーディングを指定せずに、.NET(C#)で?
文字列を暗号化します。変換せずに暗号化することはできますが、ここでエンコードが機能する理由を知りたいのですが。
また、エンコードを考慮する必要があるのはなぜですか?文字列が格納されているバイトを簡単に取得できませんか?なぜ文字エンコーディングに依存しているのですか?
どのようにして変換しないstring
にbyte[]
手動で特定のエンコーディングを指定せずに、.NET(C#)で?
文字列を暗号化します。変換せずに暗号化することはできますが、ここでエンコードが機能する理由を知りたいのですが。
また、エンコードを考慮する必要があるのはなぜですか?文字列が格納されているバイトを簡単に取得できませんか?なぜ文字エンコーディングに依存しているのですか?
回答:
あなたが述べたように、あなたの目標は、単に、「文字列が格納されているバイトを取得する」ことです。
(そしてもちろん、バイトから文字列を再構築できるようにするためです。)
代わりにこれを実行してください:
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;
}
// Do NOT use on arbitrary bytes; only use on GetBytes's output on the SAME system
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);
}
あなたのプログラム(または他のプログラム)がバイトを何らかの方法で解釈しようとしない限り、あなたがそうするつもりであるとは明らかに言わなかったので、このアプローチには何の問題もありません!エンコーディングを心配すると、実際の理由もなく、生活が複雑になります。
バイトを表示しているだけなので、エンコードとデコードは同じです。
ただし、特定のエンコードを使用した場合、無効な文字のエンコード/デコードに問題が発生します。
GetString
とGetBytes
仕事を同じエンディアンを持つシステム上で実行する必要があります。したがって、これを使用して、他の場所で文字列に変換したいバイトを取得することはできません。そのため、これを使用したい状況を思い付くのに苦労しています。
文字列のエンコーディング(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フレームワークはUTF-16を使用して文字列を表すため、.NETが使用する正確なバイト数を取得したいだけの場合は、を使用しますSystem.Text.Encoding.Unicode.GetBytes (...)
。
詳細については、.NET Framework(MSDN)の文字エンコーディングを参照してください。
受け入れられた答えは非常に非常に複雑です。これには、含まれている.NETクラスを使用します。
const string data = "A string with international characters: Norwegian: ÆØÅæøå, Chinese: 喂 谢谢";
var bytes = System.Text.Encoding.UTF8.GetBytes(data);
var decoded = System.Text.Encoding.UTF8.GetString(bytes);
必要がない場合は、ホイールを再発明しないでください...
System.Text.Encoding.Unicode
Mehrdadの答えと同等でなければなりません。
System.Text.Encoding.Unicode.GetBytes
おそらくより正確になります。
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());
1文字は1 バイト以上(最大約6)で表すことができるため、エンコードを考慮する必要があります。エンコードが異なると、これらのバイトの扱いも異なります。
Joelはこれについて投稿しています:
すべてのソフトウェア開発者の絶対最小値は絶対に、確実にUnicodeと文字セットについて知っている必要があります(言い訳はありません!)
これはよくある質問です。質問者が何を求めているか、そして最も一般的なニーズである可能性が高いものとは異なることを理解することが重要です。不要なコードの誤用を防ぐために、最初に後者に回答しました。
すべての文字列には、文字セットとエンコーディングがあります。System.String
オブジェクトを配列に変換し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が文字セットの名前を、その文字セットの特定の1つのエンコーディングの名前に使用していることです。Encoding.Unicode
呼び出す必要がありますEncoding.UTF16
。
ほとんどの用途でそれで終わりです。それが必要な場合は、ここを読むのをやめてください。エンコーディングとは何かわからない場合は、楽しいJoel Spolskyの記事を参照してください。
さて、質問の作者は尋ねます、「すべての文字列はバイトの配列として保存されますよね?なぜそれらのバイトを単に持つことができないのですか?」
彼は改心を望んでいません。
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回コピーされます。また、エンコーディング固有のコードであるdatatypeも明示的に使用していますSystem.Char
。
文字列が格納されている実際のバイトを取得する唯一の方法は、ポインタを使用することです。このfixed
ステートメントでは、値のアドレスを取得できます。C#仕様から:
[For]文字列型の式、...初期化子は、文字列の最初の文字のアドレスを計算します。
これを行うために、コンパイラはで文字列オブジェクトの他の部分をスキップするコードを記述し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が指摘したように、結果はマシンのエンディアンに依存します。しかし、質問の作者はそれを気にしていません。
Length
[のString
] プロパティは、Char
このインスタンスのオブジェクトの数を返します。Unicode文字の数ではありません。」したがって、サンプルコードは記述どおりに正しいものです。
new String(new []{'\uD800', '\u0030'})
Globalization.SortKey
、抽出物KeyData
にそれぞれから、およびパック結果のバイトをString
[1文字ごとに2バイト、MSBが最初 ]、呼び出しString.CompareOrdinal
結果の文字列の際は呼び出しよりも実質的に速くなるSortKey.Compare
のインスタンス上でSortKey
、またはmemcmp
それらのインスタンスを呼び出すことさえ。それを考えると、なぜKeyData
a Byte[]
ではなくaを返すのString
でしょうか?
質問の最初の部分(バイトの取得方法)はすでに他の人から回答されていSystem.Text.Encoding
ます。名前空間を調べてください。
私はあなたのフォローアップ質問に取り組みます:なぜあなたはエンコーディングを選ぶ必要があるのですか?文字列クラス自体からそれを取得できないのはなぜですか?
答えは2つの部分に分かれています。
まず第一に、文字列クラスによって内部的に使用されるバイトは重要ではありません、そして、あなたがそれらを仮定するときはいつでも、バグを導入している可能性があります。
プログラムが完全に.Netの世界にある場合は、ネットワークを介してデータを送信する場合でも、文字列のバイト配列の取得について心配する必要はありません。代わりに、.Netシリアライゼーションを使用して、データの送信について心配してください。実際のバイト数を気にする必要はありません。シリアライゼーションフォーマッターが自動的に行います。
一方、これらのバイトを.Netシリアル化ストリームからのデータのプルを保証できない場所に送信するとどうなるでしょうか。この場合、明らかにこの外部システムが気にするので、エンコーディングについて心配する必要はありません。繰り返しになりますが、文字列で使用される内部バイトは関係ありません。.Netで内部的に使用されているのと同じエンコーディングであっても、受信側でこのエンコーディングを明示できるようにエンコーディングを選択する必要があります。
この場合、文字列変数によってメモリに格納されている実際のバイトを可能な限り使用し、バイトストリームを作成する作業を省くことができることを理解しています。ただし、出力が相手側で理解されていることを確認することと、エンコーディングを明示する必要があることを保証することとを比較することは、重要ではないことを説明します。さらに、内部バイトを本当に一致させたい場合は、すでにUnicode
エンコーディングを選択するだけで、パフォーマンスを節約できます。
これにより、2番目の部分に移動します... Unicode
エンコーディングを選択すると、.Netが基になるバイトを使用するように指示されます。新しいエンコーディングのUnicode-Plusが出てきたとき、.Netランタイムは、プログラムを壊すことなく、この新しいより優れたエンコーディングモデルを自由に使用できる必要があるため、このエンコーディングを選択する必要があります。しかし、現時点では(そして近い将来)、Unicodeエンコーディングを選択するだけで、必要なものが得られます。
文字列をワイヤに書き直す必要があることを理解することも重要です。これには、一致するエンコーディングを使用する場合でも、ビットパターンの少なくとも一部の変換が含まれます。コンピューターは、ビッグエンディアンとリトルエンディアン、ネットワークバイトオーダー、パケット化、セッション情報などを考慮する必要があります。
ただ、Mehrdradのサウンドいることを実証するために、解答彼のアプローチをしても持続することができ、作品を不対サロゲート文字を例えば、多くの人が私の答えに対して水平にいたの(しかし、誰もが平等に有罪であるとSystem.Text.Encoding.UTF8.GetBytes
、System.Text.Encoding.Unicode.GetBytes
これらのエンコード方法は、上位サロゲートを持続することはできません。d800
たとえば、文字、および単にサロゲート文字をvalueに置き換えるだけですfffd
):
using System;
class Program
{
static void Main(string[] args)
{
string t = "爱虫";
string s = "Test\ud800Test";
byte[] dumpToBytes = GetBytes(s);
string getItBack = GetString(dumpToBytes);
foreach (char item in getItBack)
{
Console.WriteLine("{0} {1}", item, ((ushort)item).ToString("x"));
}
}
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);
}
}
出力:
T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74
System.Text.Encoding.UTF8.GetBytesまたはSystem.Text.Encoding.Unicode.GetBytesでそれを試してください、それらは単に高い代理文字を値fffdで置き換えます
この質問に動きがあるたびに、ペアリングされていないサロゲート文字が含まれている場合でも文字列を永続化できるシリアライザ(Microsoftまたはサードパーティコンポーネントからのもの)をまだ考えています。私は時々これをグーグルします:直列化の対になっていないサロゲート文字.NET。これによって睡眠が失われることはありませんが、私の回答に欠陥があるとコメントする人がときどきいるのはちょっと不快ですが、ペアになっていないサロゲートキャラクターに関しては、彼らの回答にも同じように欠陥があります。
くそー、マイクロソフトはSystem.Buffer.BlockCopy
そのBinaryFormatter
ツで使用する必要があった
谢谢!
System.Buffer.BlockCopy
内部で使用するシリアライゼーションライブラリがある場合、すべてのエンコーディングアドボカシー関係者の議論は議論の余地があります
FFFD
その文字でエラーが発生するのは当然です。手動で文字列を操作する場合は、推奨されるようにchar []を使用します。
System.String
不変のシーケンスですChar
。.NETは、元のString
オブジェクトにペアになっていないサロゲートが含まれている場合でも、オブジェクトを任意のオブジェクトから作成し、同じChar[]
コンテンツをChar[]
含むにエクスポートすることを常に許可していますChar[]
。
次のコードを試してみてください。
System.Text.Encoding.UTF8.GetBytes("TEST String");
System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);
、泣いてください!これは動作しますが、System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Length
しばらく"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
ええと、私はすべての回答を読みましたが、それらはエンコーディングの使用に関するものか、ペアになっていないサロゲートをドロップするシリアル化に関するものでした。
たとえば、文字列が、パスワードハッシュなどのバイト配列を格納するバイト配列から構築されたSQL Serverからのものである場合は、問題です。そこから何かを削除すると、無効なハッシュが格納され、XMLで保存したい場合はそのままにしておきます(XMLライターは、ペアになっていないサロゲートが検出すると例外をドロップするため)。
だから私はそのような場合にバイト配列のBase64エンコーディングを使用しますが、ねえ、インターネットではこれに対するC#での解決策は1つしかなく、それにはバグがあり、それは1つの方法に過ぎないため、バグを修正して書き戻しました手順。ここに、将来のグーグル:
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);
}
Convert.ToBase64String(arr);
base64変換には使用しましたbyte[] (data) <-> string (serialized data to store in XML file)
。しかし、イニシャルを取得するには、バイナリデータを含むbyte[] (data)
を使用して何かを行う必要がありました(MSSQLがそれを返す方法です)。したがって、上記の関数はのためのものです。String
String (binary data) <-> byte[] (easy accessible binary data)
また、エンコーディングを考慮する必要がある理由を説明してください。文字列が格納されているバイトを簡単に取得できませんか?なぜエンコーディングに依存するのですか?!!!
「文字列のバイト」なんてないから。
文字列(または、より一般的には、テキスト)は、文字、数字、その他の記号などの文字で構成されます。それで全部です。しかし、コンピュータはキャラクターについて何も知りません。バイトしか処理できません。したがって、コンピュータを使用してテキストを保存または送信する場合は、文字をバイトに変換する必要があります。どうやってやるの?ここで、エンコーディングがシーンに登場します。
エンコーディングは、論理文字を物理バイトに変換するための規則にすぎません。最も単純で最もよく知られているエンコーディングはASCIIであり、英語で書く場合はそれで十分です。他の言語では、より完全なエンコーディングが必要になります。これは、Unicodeフレーバーのいずれかであるため、現在最も安全な選択肢です。
つまり、「エンコーディングを使用せずに文字列のバイトを取得する」ことは、「言語を使用せずにテキストを書き込む」ことと同じくらい不可能です。
ちなみに、私はあなた(そして誰にとっても)のこの小さな知識を読むことを強くお勧めします:絶対最小値すべてのソフトウェア開発者絶対に、積極的にUnicodeと文字セットについて知っておく必要があります(言い訳はありません!)
string
aをbyte
配列に変換するC#:
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding=new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}
byte[] strToByteArray(string str)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.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);
Span<T>
C#7.2 でリリースされたので、文字列の基礎となるメモリ表現をマネージバイト配列にキャプチャする正規の手法は次のとおりです。
byte[] bytes = "rubbish_\u9999_string".AsSpan().AsBytes().ToArray();
実際に何らかの方法でデータを解釈していることを意味しますが、完全を期すために、データを元に戻すことは重要ではありません。
string s;
unsafe
{
fixed (char* f = &bytes.AsSpan().NonPortableCast<byte, char>().DangerousGetPinnableReference())
{
s = new string(f);
}
}
名前はNonPortableCast
、DangerousGetPinnableReference
おそらくこれを行うべきではないという議論を促進するはずです。
で作業するにSpan<T>
は、System.Memory NuGetパッケージをインストールする必要があることに注意してください。
かかわらず、実際の元の質問とフォローアップのコメントは、基礎となるメモリを示し、(私は変更またはそのままそれを記述する必要を超えて読まれていない手段を想定している)「解釈」されていないことを意味するものではそのいくつかの実装Stream
クラスデータを文字列としてまったく推論する代わりに使用する必要があります。
よくわかりませんが、文字列はその情報を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));
}
Unicodeの回答はどちらの場合も14バイトですが、UTF-8の回答は最初の回答では9バイト、2番目の回答では7バイトのみです。
したがって、文字列で使用されるバイトだけが必要な場合は、単にを使用しますがEncoding.Unicode
、ストレージスペースが非効率的です。
重要な問題は、文字列内のグリフが32ビット(文字コードの場合は16ビット)を必要とするが、1バイトには8ビットしか余裕がないことです。ASCII文字のみを含む文字列に制限しない限り、1対1のマッピングは存在しません。System.Text.Encodingには、文字列をbyte []にマッピングする方法がたくさんあります。情報の損失を回避し、byte []を文字列にマッピングする必要があるときにクライアントが使いやすい方法を選択する必要があります。 。
Utf8は人気のあるエンコーディングであり、コンパクトで損失がありません。
最速の方法
public static byte[] GetBytes(string text)
{
return System.Text.ASCIIEncoding.UTF8.GetBytes(text);
}
マコトサンがコメントしたように編集することは、これが今や最善の方法です:
Encoding.UTF8.GetBytes(text)
特定のエンコーディングを手動で指定せずに、.NET(C#)で文字列をbyte []に変換するにはどうすればよいですか?
.NET の文字列は、テキストをUTF-16コード単位のシーケンスとして表します。そのため、バイトはメモリ内ですでにUTF-16でエンコードされています。
Mehrdadの回答
Mehrdadの回答を使用できますが、文字はUTF-16であるため、実際にはエンコーディングを使用します。ToCharArrayを呼び出して、ソースを見るとが作成されchar[]
、メモリが直接コピーされます。次に、割り当てられているバイト配列にデータをコピーします。したがって、内部的には、基礎となるバイトを2回コピーし、呼び出し後に使用されないchar配列を割り当てます。
トム・ブロジェットの答え
Tom Blodgetの回答はMehrdadよりも20〜30%高速です。これは、char配列を割り当ててそこにバイトをコピーする中間ステップをスキップするためですが、/unsafe
オプションを指定してコンパイルする必要があります。どうしてもエンコーディングを使いたくないのなら、これでいいと思います。暗号化ログインをfixed
ブロック内に配置する場合、別のバイト配列を割り当てて、そこにバイトをコピーする必要さえありません。
また、エンコードを考慮する必要があるのはなぜですか?文字列が格納されているバイトを簡単に取得できませんか?なぜ文字エンコーディングに依存しているのですか?
それが適切な方法だからです。 string
抽象化です。
エンコーディングを使用すると、無効な文字を含む「文字列」がある場合に問題が発生する可能性がありますが、これは起こりません。無効な文字を含む文字列にデータを取得している場合、それは間違っています。最初に、バイト配列またはBase64エンコーディングを使用しているはずです。
を使用するSystem.Text.Encoding.Unicode
と、コードの耐障害性が向上します。コードが実行されるシステムのエンディアンについて心配する必要はありません。CLRの次のバージョンが別の内部文字エンコードを使用するかどうかを心配する必要はありません。
問題は、なぜエンコーディングを心配したいのかではなく、なぜそれを無視して別のものを使用したいのかだと思います。エンコーディングは、文字列の抽象化を一連のバイトで表すことを目的としています。 System.Text.Encoding.Unicode
リトルエンディアンのバイトオーダーエンコーディングを提供し、現在および将来のすべてのシステムで同じように実行します。
OPの質問に最も近いアプローチは、実際にはオブジェクトに入り、バイトを抽出するTom Blodgetの質問です。Stringオブジェクトの実装に依存するため、最も近いと言います。
"Can't I simply get what bytes the string has been stored in?"
もちろん、それが問題の根本的なエラーが発生する場所です。文字列は、興味深いデータ構造を持つオブジェクトです。ペアリングされていないサロゲートを保存できるため、すでにわかっています。長さを保存する場合があります。それは、迅速なカウントを可能にする「ペアにされた」代理のそれぞれへのポインタを保持するかもしれません。等これらの余分なバイトはすべて文字データの一部ではありません。
必要なのは、配列内の各文字のバイトです。そして、それが「エンコーディング」の出番です。デフォルトでは、UTF-16LEを取得します。往復以外のバイト自体を気にしない場合は、「デフォルト」を含む任意のエンコーディングを選択し、後で変換し直すことができます(デフォルトのエンコーディングと同じパラメータ、コードポイント、バグ修正などを想定) 、許可されていない代理母などの許可されたもの
しかし、なぜ「エンコーディング」を魔法のままにしておくのでしょうか。取得するバイトがわかるようにエンコードを指定しないのはなぜですか?
"Why is there a dependency on character encodings?"
エンコーディング(この場合)は、単に文字列を表すバイトを意味します。文字列オブジェクトのバイトではありません。あなたは文字列が格納されているバイトを望んでいました-これは素朴に質問された場所です。文字列を表す連続した配列内の文字列のバイトが必要であり、文字列オブジェクトに含まれる可能性のある他のすべてのバイナリデータは必要ではありませんでした。
つまり、文字列がどのように格納されるかは関係ありません。文字列をバイト配列のバイトに「エンコード」したい。
トム・ブログの答えが気に入ったのは、彼が「文字列オブジェクトのバイト数」の方向に連れて行ったからです。それは実装に依存しますが、彼は内部を覗いているので、文字列のコピーを再構成するのは難しいかもしれません。
Mehrdadの対応は概念レベルで誤解を招くため、間違っています。エンコードされたバイトのリストがまだあります。彼の特定のソリューションでは、ペアになっていないサロゲートを保持できます。これは実装に依存します。彼の特定のソリューションはGetBytes
、デフォルトで文字列をUTF-8で返した場合、文字列のバイトを正確に生成しませんでした。
私はこれについて変更しました(Mehrdadのソリューション)-これは文字列のバイトを取得していません。むしろ、文字列から作成された文字配列のバイトを取得しています。エンコードに関係なく、c#のcharデータ型は固定サイズです。これにより、一貫した長さのバイト配列を生成でき、バイト配列のサイズに基づいて文字配列を再現できます。したがって、エンコードがUTF-8であるが、各文字が最大のutf8値に対応するために6バイトであった場合でも、それは機能します。実際、文字のエンコーディングは重要ではありません。
ただし、変換が使用されました。各文字は固定サイズのボックス(c#の文字タイプ)に配置されました。ただし、その表現が何であるかは問題ではありません。これは、技術的にはOPに対する答えです。それで-とにかく変換するつもりなら...なぜ 'エンコード'しないのですか?
&(Char) 55906
&(Char) 55655
。したがって、あなたは間違っている可能性があり、Mehrdadの答えは、使用されているエンコーディングのタイプを考慮せずに安全な変換です。
あなたは.NETでa string
をa byte array
に変換するために次のコードを使うことができます
string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
文字列の基礎となるバイトのコピーが本当に必要な場合は、次のような関数を使用できます。ただし、その理由を理解するために読み進めないでください。
[DllImport(
"msvcrt.dll",
EntryPoint = "memcpy",
CallingConvention = CallingConvention.Cdecl,
SetLastError = false)]
private static extern unsafe void* UnsafeMemoryCopy(
void* destination,
void* source,
uint count);
public static byte[] GetUnderlyingBytes(string source)
{
var length = source.Length * sizeof(char);
var result = new byte[length];
unsafe
{
fixed (char* firstSourceChar = source)
fixed (byte* firstDestination = result)
{
var firstSource = (byte*)firstSourceChar;
UnsafeMemoryCopy(
firstDestination,
firstSource,
(uint)length);
}
}
return result;
}
この関数は、文字列の基礎となるバイトのコピーを非常に迅速に取得します。これらのバイトは、システムでエンコードしている方法で取得されます。このエンコーディングはほぼ間違いなくUTF-16LEですが、これは気にする必要のない実装の詳細です。
呼び出すだけの方が安全でシンプルで信頼性が高い
System.Text.Encoding.Unicode.GetBytes()
おそらくこれにより同じ結果が得られ、入力が簡単になり、バイトは常にへの呼び出しでラウンドトリップします
System.Text.Encoding.Unicode.GetString()
ここでの私の危険な実装である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;
}
エレガントではないにしても、受け入れられているanwserのものよりもはるかに高速です。以下は、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に従って、このメソッドは文字列拡張としても使用できます。
public static unsafe class StringExtensions
{
public static Byte[] ToByteArray(this String s)
{
// Method Code
}
}
RuntimeHelpers.OffsetToStringData
Itaniumバージョンの.NET の値は8の倍数ですか?それ以外の場合は、アラインされていない読み取りが原因で失敗します。
memcpy
か?stackoverflow.com/a/27124232/659190
単にこれを使用してください:
byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();
返し"Árvizturo tukörfurogép."
ます。(すべての文字が
次の事実により、文字列はいくつかの異なる方法でバイト配列に変換できます。.NETはUnicodeをサポートしており、UnicodeはUTFと呼ばれるいくつかの異なるエンコーディングを標準化しています。それらは異なるバイト表現の長さを持っていますが、文字列がエンコードされるとき、それは文字列にコード化できるという意味で同等ですが、文字列が1つのUTFでエンコードされ、異なるUTFを想定してデコードされる場合、ねじ込むことができます。アップ。
また、.NETは非Unicodeエンコーディングをサポートしますが、一般的には無効です(ASCIIなどの実際の文字列でUnicodeコードポイントの限定されたサブセットが使用されている場合にのみ有効です)。内部的には、.NETはUTF-16をサポートしていますが、ストリーム表現には通常UTF-8が使用されます。また、インターネットの標準デファクトでもあります。
当然のことながら、バイトの配列への文字列のシリアル化と逆シリアルSystem.Text.Encoding
化は、抽象クラスであるclass でサポートされています。その派生クラスは具体的なエンコーディングをサポートします: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のコメントに基づいており、ユースケースでのOPのヒントを理解している場合は正しい質問です。
バイナリデータを文字列に格納することは、上記の想定されたエンコーディングのため、おそらく間違ったアプローチです!プログラムやライブラリがそのバイナリデータをstring
(byte[]
より適切な配列ではなく)に格納したものは、戦闘が始まる前にすでに負けています。RESTリクエスト/レスポンス、または文字列を送信する必要のあるものでバイトを送信している場合は、Base64が適切なアプローチです。
他の誰もがこの誤った質問に誤って答えた。
文字列がそのままのように見える場合は、エンコーディング(できればUTFで始まるもの)を選択し、対応するSystem.Text.Encoding.???.GetBytes()
関数を使用して、選択したエンコーディングにバイトを与える人に通知します。
バイトをどうするつもりなのか尋ねられたら、あなたは答えました:
暗号化します。変換せずに暗号化できますが、ここでエンコーディングが機能する理由を知りたいのですが。ちょうど私に言うバイトは私が言うことです。
この暗号化されたデータをネットワーク経由で送信するのか、後でメモリにロードするのか、または別のプロセスにスチームするのかに関係なく、ある時点でデータを解読するつもりであることは明らかです。その場合の答えは、通信プロトコルを定義しているということです。通信プロトコルは、プログラミング言語とそれに関連するランタイムの実装の詳細に関して定義すべきではありません。これにはいくつかの理由があります。
通信する場合(完全に異なるプロセスまたは将来同じプログラムと通信する場合)、プロトコルを厳密に定義して、プロトコルを操作したり、誤ってバグを作成したりする難しさを最小限に抑える必要があります。.NETの内部表現によっては、厳密で明確ではなく、一貫した定義であることが保証されていません。標準エンコーディングは、将来的に失敗することのない厳密な定義です。
つまり、エンコーディングを指定せずに整合性の要件を満たすことはできません。
あなたは可能確かに .NETが内部またはその他の理由のためにそれを使用しますが、明示的にエンコードすることを選択して、あなたのコード内で明示的にそれらの変換を実行する必要がなく、依存するので、あなたが有意に良好あなたのプロセスを実行することを発見した場合、直接UTF-16を使用することを選択します.NETの内部実装。
したがって、エンコーディングを選択して使用します。
using System.Text;
// ...
Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")
ご覧のとおり、組み込みのエンコーディングオブジェクトを使用する方が、独自のリーダー/ライターメソッドを実装するよりも実際にはコードが少なくなっています。
二通り:
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;
}
私は一番下よりも一番下を使用する傾向がありますが、速度についてはベンチマークしていません。
bytes[] buffer = UnicodeEncoding.UTF8.GetBytes(string something); //for converting to UTF then get its bytes
bytes[] buffer = ASCIIEncoding.ASCII.GetBytes(string something); //for converting to ascii then get its bytes