サーチ…
備考
.NET文字列のSystem.String
はSystem.Char
文字列で、各文字はUTF-16でコード化されたコード単位です。 文字と.NET(および他の多くの言語)の文字の定義の話し言葉の定義が異なっているので、この区別は重要です。
1つの文字は 、正しく書記体と呼ばれるはずですが、 グリフとして表示され、1つ以上のUnicode コードポイントで定義されます。各コードポイントは、コードユニットのシーケンスでコード化されます。今では、単一のSystem.Char
が必ずしも書記素を表しているわけではないことが明らかになるはずです。現実世界でどのように異なるのかを見てみましょう。
- 一の書記素は、ための結合文字 、二つ以上のコードポイントをもたらし得る。A 2つのコードポイントによって構成されている:U + 0061ラテン小文字AとU + 0300 GRAVEアクセントを組み合わせます 。これは最も一般的な間違いです。なぜなら、
"à".Length == 2
、あなたが1
期待しているからです。 - 上述したように、例えばaが GRAVEまたは2つのコードポイントを有する単一のコードポイントU + 00E0ラテン小文字のAとすることができるため、文字が重複しています。明らかに、
"\u00e0" == "\u0061\u0300"
(たとえ"\u00e0".Length != "\u0061\u0300".Length
)でも同じものを比較しなければなりません。これは、String.Normalize()
メソッドによって実行される文字列正規化のために可能です。 - 例えば文字한 U + D55C HAN文字は 、 ᄒ単一コードポイント(UTF-16で単一のコード単位として符号化)またはその音節の分解配列であってもよいためᅡ Unicodeシーケンスは、合成または分解配列を含んでいてもよいですそしてᆫ 。それらは等しいと比較されなければならない。
- 1つのコードポイントが複数のコードユニットにエンコードされている可能性があります。文字𠂊 U + 2008A HAN CHARACTERは単なるコードポイントでも2つの
System.Char
("\ud840\udc8a"
)としてエンコードされます:UTF-16エンコーディングは固定サイズではありません!たとえば、アプリケーションが最大長を適用し、文字列を盲目的に切り捨てて無効な文字列を作成した場合、これは無数のバグ(深刻なセキュリティバグ)の原因です。 - いくつかの言語が持つ有向グラフ例えばチェコのCH( 時間後と文字列のリストを注文するとき、私は 、あなたがケミー前fyzikaを持っています前に、スタンドアロンの手紙で、トリグラフを。
テキスト処理に関しては、もっと多くの問題があります。たとえば、文字比較でUnicode対応文字を実行するにはどうすればよいですか?を参照してください。より幅広い紹介や関連する議論へのリンクがたくさんあります。
一般的に、 国際的なテキストを扱うときは、文字列内のテキスト要素を列挙するためにこの単純な関数を使うことができます(Unicodeサロゲートやエンコーディングを壊さないようにする)。
public static class StringExtensions
{
public static IEnumerable<string> EnumerateCharacters(this string s)
{
if (s == null)
return Enumerable.Empty<string>();
var enumerator = StringInfo.GetTextElementEnumerator(s.Normalize());
while (enumerator.MoveNext())
yield return (string)enumerator.Value;
}
}
異なる文字をカウントする
あなたは理由は備考セクションで説明したため、その後、個別の文字をカウントする必要がある場合は、単純に使用することはできませんLength
、それは配列の長さだからプロパティをSystem.Char
文字が、コード・ユニット(ないUnicodeのコードポイントではありませんグラフェムもありません)。たとえば、単にtext.Distinct().Count()
実行すると、誤った結果が得られますtext.Distinct().Count()
コードを修正してください:
int distinctCharactersCount = text.EnumerateCharacters().Count();
1つのステップは、パフォーマンスが問題ではない場合は、このように(この例では大文字小文字に関係なく)簡単に行うことができる場合は、 各文字の出現をカウントすることです 。
var frequencies = text.EnumerateCharacters()
.GroupBy(x => x, StringComparer.CurrentCultureIgnoreCase)
.Select(x => new { Character = x.Key, Count = x.Count() };
カウント文字
あなたはその後、 文字をカウントする必要がある場合は、理由のために、それは配列の長さだからあなたは、単にLengthプロパティを使用することはできません、 備考セクションで説明したSystem.Char
文字が、コード単位は(ではないではないのUnicodeコードポイントもグラフェム)。正しいコードは次のとおりです。
int length = text.EnumerateCharacters().Count();
小さな最適化は、この目的のために特にEnumerateCharacters()
拡張メソッドを書き換えるかもしれません:
public static class StringExtensions
{
public static int CountCharacters(this string text)
{
if (String.IsNullOrEmpty(text))
return 0;
int count = 0;
var enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
++count;
return count;
}
}
文字の出現をカウントする
解説セクションで説明されている理由により、(特定のコードユニットの出現数をカウントしない限り)これを単に行うことはできません:
int count = text.Count(x => x == ch);
より複雑な関数が必要です。
public static int CountOccurrencesOf(this string text, string character)
{
return text.EnumerateCharacters()
.Count(x => String.Equals(x, character, StringComparer.CurrentCulture));
}
文字列の比較(文化の不変である文字比較とは対照的)は、ルールに従って特定の文化に常に実行されなければならないことに注意してください。
文字列を固定長ブロックに分割
(ので、我々は、任意の点に文字列を壊すことができないSystem.Char
一人で有効ではない可能性があり、それは代理の結合文字または一部だから)、その後のコードは、アカウントにそれを取る必要があります( 長さのことに注意してください、私は書記素の数を意味するものではありませコードユニットの数):
public static IEnumerable<string> Split(this string value, int desiredLength)
{
var characters = StringInfo.GetTextElementEnumerator(value);
while (characters.MoveNext())
yield return String.Concat(Take(characters, desiredLength));
}
private static IEnumerable<string> Take(TextElementEnumerator enumerator, int count)
{
for (int i = 0; i < count; ++i)
{
yield return (string)enumerator.Current;
if (!enumerator.MoveNext())
yield break;
}
}
文字列を別のエンコーディングに/から変換する
.NET文字列には、 System.Char
(UTF-16コード単位)が含まれています。別のエンコーディングでテキストを保存(または管理)する場合は、 System.Byte
配列で作業する必要がありSystem.Byte
。
変換は、 System.Text.Encoder
およびSystem.Text.Decoder
から派生したクラスによって実行されます。これらのクラスは、別のエンコーディング(バイトXエンコードされた配列byte[]
からUTF-16でエンコードされたSystem.String
およびvice -versa)。
エンコーダ/デコーダは通常、相互に非常に近く動作するため、 System.Text.Encoding
から派生したクラスでグループ化されていSystem.Text.Encoding
。派生クラスは一般的なエンコーディング(UTF-8、UTF-16など)との変換を提供します。
例:
文字列をUTF-8に変換する
byte[] data = Encoding.UTF8.GetBytes("This is my text");
UTF-8データを文字列に変換する
var text = Encoding.UTF8.GetString(data);
既存のテキストファイルのエンコードを変更する
このコードは、UTF-8でエンコードされたテキストファイルの内容を読み取り、UTF-16としてエンコードして保存します。このコードは、ファイルが大きければすべてのコンテンツをメモリに読み込むので最適ではないことに注意してください。
var content = File.ReadAllText(path, Encoding.UTF8);
File.WriteAllText(content, Encoding.UTF16);
Object.ToString()仮想メソッド
.NETのすべてがオブジェクトであるため、すべての型はオーバーライドできるObject
クラスで定義されたToString()
メソッドを持っています。このメソッドのデフォルトの実装は、型の名前を返します。
public class Foo
{
}
var foo = new Foo();
Console.WriteLine(foo); // outputs Foo
ToString()
は、valueと文字列をconcatinatingするときに暗黙的に呼び出されます。
public class Foo
{
public override string ToString()
{
return "I am Foo";
}
}
var foo = new Foo();
Console.WriteLine("I am bar and "+foo);// outputs I am bar and I am Foo
このメソッドの結果は、デバッグツールでも広く使用されています。何らかの理由でこのメソッドをオーバーライドするのではなく、 デバッガで型の値がどのように表示されるかをカスタマイズするには、 DebuggerDisplay Attribute ( MSDN )を使用します。
// [DebuggerDisplay("Person = FN {FirstName}, LN {LastName}")]
[DebuggerDisplay("Person = FN {"+nameof(Person.FirstName)+"}, LN {"+nameof(Person.LastName)+"}")]
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set;}
// ...
}
弦の不変性
文字列は不変です。既存の文字列を変更することはできません。文字列上の操作によって、新しい値を持つ文字列の新しいインスタンスが作成されます。これは、非常に長い文字列の中の1文字を置換する必要がある場合、メモリは新しい値に割り当てられることを意味します。
string veryLongString = ...
// memory is allocated
string newString = veryLongString.Remove(0,1); // removes first character of the string.
文字列値で多くの操作を実行する必要がある場合は、効率的な文字列操作用に設計されたStringBuilder
クラスを使用します。
var sb = new StringBuilder(someInitialString);
foreach(var str in manyManyStrings)
{
sb.Append(str);
}
var finalString = sb.ToString();
弦楽器
String
にもかかわらず、参照型==
演算子は参照ではなく文字列値を比較します。
ご存知のとおり、 string
は単なるstring
配列です。しかし、文字列の平等チェックと比較が文字単位で行われると考えると、間違っています。この操作はカルチャー固有です(下記の「備考」を参照してください)。いくつかの文字シーケンスは、 カルチャに応じて等しく扱うことができます。
2つの文字列のLength
プロパティを比較することにより、短絡チェックの前に2回考えます。
デフォルトの動作を変更する必要がある場合は、追加のStringComparison
列挙値を受け入れるString.Equals
メソッドのオーバーロードを使用します。