ある文字列から別の文字列に高Unicode文字を読み取ろうとしています。簡潔にするために、以下に示すようにコードを簡略化します。
public static void UnicodeTest()
{
var highUnicodeChar = "????"; //Not the standard A
var result1 = highUnicodeChar; //this works
var result2 = highUnicodeChar[0].ToString(); // returns \ud835
}
highUnicodeChar
をresult1
に直接割り当てると、リテラル値????
が保持されます。インデックスでアクセスしようとすると、\ud835
が返されます。私が理解しているように、これはUTF-32文字を表すために使用されるUTF-16文字の代理ペアです。この問題は、暗黙的にchar
をstring
に変換しようとすることに関係していると確信しています。
結局、私はresult2
がresult1
と同じ値を生成することを望んでいます。これどうやってするの?
nicode には、 コードポイント があります。これらは21ビット長です。あなたのキャラクター ????、 - Mathematical Bold Capital A
、U + 1D400のコードポイントがあります。
Unicodeエンコーディングでは、コード単位があります。エンコーディングの自然単位は次のとおりです。 8ビット for TF-8 、 16ビット for TF-16 など。 1つ以上のコードユニットが単一のコードポイントをエンコードします。
UTF-16では、単一のコードポイントを形成する2つのコードユニットは、サロゲートペアと呼ばれます。サロゲートペアは、16ビットを超えるコードポイント(U + 10000以上)をエンコードするために使用されます。
.NET Char
は単一のUTF-16コードユニットを表し、.NET String
はコードユニットのコレクションであるため、これは.NETでは少し注意が必要です。
だからあなたのコードポイント???? (U + 1D400)は16ビットに収まらず、サロゲートペアが必要です。つまり、文字列には2つのコードユニットが含まれています。
var highUnicodeChar = "????";
char a = highUnicodeChar[0]; // code unit 0xD835
char b = highUnicodeChar[1]; // code unit 0xDC00
つまり、そのように文字列にインデックスを付けると、実際にはサロゲートペアの半分しか取得できません。
IsSurrogatePair を使用して、サロゲートペアをテストできます。例えば:
string GetFullCodePointAtIndex(string s, int idx) =>
s.Substring(idx, char.IsSurrogatePair(s, idx) ? 2 : 1);
Unicodeでの可変エンコーディングのうさぎの穴はコードポイントで終わらないことに注意することが重要です。 書記素クラスターは、尋ねられたときにほとんどの人が最終的に「キャラクター」と呼ぶ「目に見えるもの」です。書記素クラスターは、1つ以上のコードポイント(基本文字と0個以上の結合文字)から作成されます。結合文字の例は、追加したいumlautまたはその他のさまざまな装飾/修飾子です。結合文字で何ができるかの恐ろしい例については、 この回答 を参照してください。
結合文字をテストするには、 GetUnicodeCategory を使用して、囲みマーク、非スペーシングマーク、またはスペーシングマークを確認できます。
highUnicodeChar
文字列からユーザーの観点から最初の「アトミック」文字(つまり、最初のUnicode grapheme cluster )を抽出したいようです。ここで、「アトミック」文字には、 代理ペア 。
StringInfo.GetTextElementEnumerator()
を使用してこれを行うことができ、string
をアトミックチャンクに分割してから、最初のチャンクを取得します。
まず、次の拡張メソッドを定義します。
_public static class TextExtensions
{
public static IEnumerable<string> TextElements(this string s)
{
// StringInfo.GetTextElementEnumerator is a .Net 1.1 class that doesn't implement IEnumerable<string>, so convert
if (s == null)
yield break;
var enumerator = StringInfo.GetTextElementEnumerator(s);
while (enumerator.MoveNext())
yield return enumerator.GetTextElement();
}
}
_
今、あなたはすることができます:
_var result2 = highUnicodeChar.TextElements().FirstOrDefault() ?? "";
_
StringInfo.GetTextElementEnumerator()
もUnicode 結合文字 をグループ化するため、文字列_Ĥ=T̂+V̂
_の最初の書記素クラスターはH
ではなく_Ĥ
_になることに注意してください。
サンプルフィドル ここ 。