Javaの「サロゲートペア」とは何ですか?


149

私はのドキュメントStringBuffer、特にreverse()メソッドを読んでいました。そのドキュメントは、サロゲートペアについて何かを述べています。このコンテキストでサロゲートペアとは何ですか?そして、および代理は何ですか?


3
これはUTF-16の用語であり、ここで説明されています
。download.oracle.com/ javase /

1
その方法は、バグである:それは、完全な文字ᴀᴋᴀコードポイントを逆にすべきである- ないそれらの別個の部品、ᴀᴋᴀコードユニット。バグは、特定のレガシーメソッドがコードポイントではなく個々の文字ユニットでのみ機能することです。これは、文字ユニットだけでなく、構成する必要 があるものですString。Javaが悪すぎると、OOを使用してそれを修正することはできませんが、Stringクラスとクラスの両方StringBufferfinal拡張されています。殺されたというのは、それは婉曲表現ではないでしょうか。:)
tchrist

2
@tchristドキュメント(およびソース)は、コードポイントの文字列として反転することを述べています。(恐らく1.0.2はそれをしなかった、そしてあなたは最近このような振る舞いの変化を得ることは決してないであろう。)
トム・ホーティン-タックライン

回答:


128

「サロゲートペア」という用語は、UTF-16エンコードスキームで高コードポイントでUnicode文字をエンコードする手段を指します。

Unicode文字エンコーディングでは、文字は0x0〜0x10FFFFの値にマッピングされます。

内部的には、JavaはUTF-16エンコードスキームを使用してUnicodeテキストの文字列を格納します。UTF-16では、16ビット(2バイト)コード単位が使用されます。16ビットには0x0から0xFFFFまでの文字の範囲しか含めることができないため、この範囲(0x10000から0x10FFFF)を超える値を格納するために、さらに複雑さが使用されます。これは、サロゲートと呼ばれるコード単位のペアを使用して行われます。

サロゲートコードユニットは、2つのコードユニットシーケンスの先頭または末尾で許可されているかどうかに応じて、「高サロゲート」および「低サロゲート」と呼ばれる2つの範囲にあります。


4
これは最高票を獲得していますが、単一のコード例は提供していません。実際の使用方法についても、これらの答えはありません。これが、これが反対投票されている理由です。
ジョージザビエル

57

初期のJavaバージョンは、16ビットcharデータ型を使用してUnicode文字を表していました。すべてのUnicode文字が65,535(0xFFFF)未満の値を持ち、16ビットで表現できるため、この設計は当時は意味がありました。しかし、その後、Unicodeは最大値を1,114,111(0x10FFFF)に増やしました。16ビット値は小さすぎてUnicodeバージョン3.1のすべてのUnicode文字を表すことができないため、32ビット値(コードポイントと呼ばれる)がUTF-32エンコードスキームに採用されました。ただし、メモリを効率的に使用するには、16ビット値が32ビット値よりも優先されるため、Unicodeは、16ビット値の継続的な使用を可能にする新しい設計を導入しました。この設計は、UTF-16エンコードスキームで採用されており、1,024個の値を16ビットの上位サロゲート(U + D800からU + DBFFの範囲内)に割り当て、別の1,024個の値を16ビットの下位サロゲート(U + DC00の範囲内)に割り当てます。 U + DFFFへ)。


7
Unicode 3.1が元の65535から1024 + 1024(高+低)の値を予約して1024 * 1024の新しい値を取得する方法を説明しているため、これは受け入れられた回答よりも優れています。ストリング。
Eric Hirst

1
UTF-16が最もメモリ効率の良いUnicodeエンコーディングであることを暗示するこの回答は好きではありません。UTF-8が存在し、ほとんどのテキストを2バイトとしてレンダリングしません。今日では、UTF-16が主に使用されています。これは、メモリ効率のためではなく、UTF-32が登場する前にMicrosoftが選択したためです。唯一の時間について、あなたは実際に思いたいあなたがWindows上でファイル操作をたくさんやっている時にUTF-16であるため、読み取りの両方である、それをたくさん書きます。それ以外の場合、高速(b / c定数オフセット)の場合はUTF-32、低メモリ(b / c最小1バイト)の場合はUTF-8
Funda Monicaの訴訟

23

そのドキュメントが言っていることは、無効なUTF-16文字列reverseは有効な文字列の逆になる可能性があるため、メソッドを呼び出した後に有効になる可能性があるということです。サロゲートペア(ここで説明)は、単一のUnicodeコードポイントをエンコードするUTF-16の16ビット値のペアです。低サロゲートと高サロゲートは、そのエンコーディングの2つの半分です。


6
明確化。文字列は、「true」文字(別名「graphemes」または「text elements」)で逆にする必要があります。単一の「文字」コードポイントは1つまたは2つの「文字」チャンク(サロゲートペア)であり、書記素はそれらのコードポイントの1つ以上(つまり、基本文字コードと1つ以上の結合文字コード)である場合があります。 1つまたは2つの16ビットチャンクまたは「chars」の長さである可能性があります)。したがって、1つの書記素は、2つの文字がそれぞれ3つの結合文字で、合計6つの文字になります。文字列全体を反転するときは、6つの「文字」すべてを順番に(つまり、反転せずに)まとめておく必要があります。
Triynko 2013

4
したがって、「char」データ型は誤解を招く可能性があります。「キャラクター」はゆるい言葉です。「char」タイプは、実際には単なるUTF16チャンクサイズであり、サロゲートペアの相対的な希少性が発生するため(つまり、通常は文字コードポイント全体を表す)、これを文字と呼びます。したがって、「文字」は実際には単一のUnicodeコードポイントを指します。 、ただし結合文字を使用すると、単一の「文字/書記素/テキスト要素」として表示される一連の文字を持つことができます。これはロケット科学ではありません。概念は単純ですが、言語は混乱します。
Triynko 2013

Javaが開発されていた当時、Unicodeはまだ始まったばかりでした。Javaが約5年間存在してからUnicodeがサロゲートペアを取得するようになりました。そのため、当時は16ビット文字が非常によく適合していました。これで、UTF-16よりもUTF-8とUTF-32を使用するほうがはるかに便利です。
ジョナサンボールドウィン

23

この投稿の上記の回答にいくつかの情報を追加しています。

Java-12でテスト済み、5より上のすべてのJavaバージョンで動作するはずです。

ここで述べたように:https : //stackoverflow.com/a/47505451/2987755
どちらの文字(UnicodeがU + FFFFを超える)も、Javaがchar値のペアとして格納するサロゲートペア、つまり単一のUnicodeとして表されます文字は、隣接する2つのJava文字として表されます。
次の例でわかるように。
1.長さ:

"🌉".length()  //2, Expectations was it should return 1

"🌉".codePointCount(0,"🌉".length())  //1, To get the number of Unicode characters in a Java String  

2.等価性:以下のように
Unicode \ud83c\udf09を使用して文字列に「🌉」を表し、等価性を確認します。

"🌉".equals("\ud83c\udf09") // true

JavaはUTF-32をサポートしていません

"🌉".equals("\u1F309") // false  

3. Unicode文字をJava文字列に変換できます

"🌉".equals(new String(Character.toChars(0x0001F309))) //true

4. String.substring()は補助文字を考慮しません

"🌉🌐".substring(0,1) //"?"
"🌉🌐".substring(0,2) //"🌉"
"🌉🌐".substring(0,4) //"🌉🌐"

これを解決するために使用できます String.offsetByCodePoints(int index, int codePointOffset)

"🌉🌐".substring(0,"🌉🌐".offsetByCodePoints(0,1) // "🌉"
"🌉🌐".substring(2,"🌉🌐".offsetByCodePoints(1,2)) // "🌐"

5.反復処理Unicode文字列のBreakIterator
ユニコード6.ソートストリングjava.text.Collat​​or
7キャラクタのtoUpperCase()toLowerCase()特定のロケールの、方法が使用されるべきではなく、代わりに、使用文字列大文字と小文字。
8. Character.isLetter(char ch)はサポートされていませんが、よく使用されます。CharacterクラスのメソッドCharacter.isLetter(int codePoint)ごとmethodName(char ch)に、methodName(int codePoint)補助文字を処理できるタイプがあります。
9.で文字セットを指定しString.getBytes()、バイトから文字列に変換しInputStreamReaderOutputStreamWriter

参照:https :
//coolsymbol.com/emojis/emoji-for-copy-and-paste.html#objects
https://www.online-toolz.com/tools/text-unicode-entities-convertor.php
https: //www.ibm.com/developerworks/library/j-unicode/index.html
https://www.oracle.com/technetwork/articles/javaee/supplementary-142654.html

例の詳細image1 image2
探索する価値のあるその他の用語:正規化BiDi


2
この回答に投票するために特別にサインインしました(つまり、ウィンドウをシークレットモードから通常のウィンドウに変更しました:P)。初心者のための最良の説明
N-JOY

1
ありがとう!助けてくれてうれしいですが、元の投稿者には感謝する価値があります。
dkb

素晴らしい例!私もそれを賛成するためにログインしました:)そして、再び、Javaがコード内で既知のバグを存続させる理由が本当に理解できなくなった(再び)と思いました。私は彼らが既存のコードを壊したくないことを完全に尊重しますが、どうぞ...これらのバグを回避するために何時間も失われましたか?壊れている場合は、修正してください。
フランツD.

6

サロゲートペアは、特定の文字をエンコードするUTF-16の方法を参照します。http://en.wikipedia.org/wiki/UTF-16/UCS-2#Code_points_U.2B10000..U.2B10FFFFを参照してください


11
「キャラクター」はそのようなロードされた用語です。
Triynko 2013

1
Unicodeには文字はありませんが、コードポイントはあります。各コードポイントは、ゼロから複数の文字としてレンダリングできます。
Nick Volynkin、2016

6

小さな序文

  • Unicodeはコードポイントを表します。各コードポイントは、Unicode規格に従って、8、16、または32ビットブロックでエンコードできます。
  • バージョン3.1以前は、主に使用されていたのは、UTF-8と呼ばれる8ビットのエンコードと、UCS-2または「2オクテットでコード化されたユニバーサル文字セット」と呼ばれる16ビットエンコーディングでした。UTF-8はUnicodeポイントを1バイトブロックのシーケンスとしてエンコードしますが、UCS-2は常に2バイトを使用します。

    A = 41 -UTF-8の8ビットの1ブロック
    A = 0041 -UCS-2の16ビットの1ブロック
    Ω= CE A9 -UTF-8の8ビットの2ブロック
    Ω= 03A9-の 1ブロックUCS-2の16ビット

問題

コンソーシアムは、人間が読める言語をすべてカバーするには16ビットで十分であると考えました。これにより、2 ^ 16 = 65536の可能なコード値が得られます。これは、BPMまたはBasic Multilingual Planeとしても知られるPlane 0にも当てはまり、現在、65536個のコードポイントのうち55,445個が含まれています。BPMは、中日韓記号(CJK)を含め、世界中のほぼすべての人間の言語をカバーしています。

時間の経過と新しいアジアの文字セットが追加され、中国のシンボルだけで70,000ポイント以上を獲得しました。現在、標準のpartの一部として絵文字ポイントさえあります。新しい16個の「追加」プレーンが追加されました。UCS-2ルームは、Plane-0より大きなものをカバーするには十分ではありませんでした。

Unicodeの決定

  1. Unicodeを17プレーン×プレーンあたり65 536文字= 1 114 112最大ポイントに制限します。
  2. 各コードポイントの32ビットを保持し、すべてのプレーンをカバーするために、以前はUCS-4として知られていたUTF-32を提示します。
  3. UTF-8を引き続き動的エンコーディングとして使用し、UTF-8を各コードポイントの最大4バイトに制限します。つまり、ポイントあたり1〜4バイトです。
  4. UCS-2を廃止
  5. UCS-2に基づいてUTF-16を作成します。UTF-16を動的にすることで、ポイントあたり2バイトまたは4バイトがかかります。1024ポイントU + D800–U + DBFF(High Surrogatesと呼ばれる)をUTF-16に割り当てます。1024個のシンボルU + DC00–U + DFFF(Low Surrogatesと呼ばれる)をUTF-16に割り当てます。

    これらの変更により、BPMはUTF-16の16ビットの1ブロックでカバーされますが、すべての「補足文字」は、それぞれ16ビットで2ブロックを示すサロゲートペアでカバーされ、合計1024x1024 = 1 048 576ポイントになります。

    高いサロゲートが低いサロゲートに先行します。このルールからの逸脱は、不適切なエンコーディングと見なされます。たとえば、ペアのないサロゲートは正しくありません。高サロゲートの前にある低サロゲートは正しくありません。

    M、「MUSICAL SYMBOL G CLEF」は、サロゲートのペアとして0xD834 0xDD1E(2 x 2バイト)、
    UTF-8として0xF0 0x9D 0x84 0x9E(4 x 1バイト)、
    UTF-32としてUTF-16でエンコードされます0x0001D11E(1 x 4バイト)。

現在の状況

  • 標準によれば、サロゲートは特にUTF-16にのみ割り当てられていますが、歴史的に、一部のWindowsおよびJavaアプリケーションは、サロゲート範囲に予約されているUTF-8およびUCS-2ポイントを使用していました。
    不正なUTF-8 / UTF-16エンコーディングでレガシーアプリケーションをサポートするために、新しい標準の WTF-8であるWobbly Transformation Formatが作成されました。ペアになっていないサロゲートや不正なシーケンスなど、任意のサロゲートポイントをサポートしています。現在、一部の製品は標準に準拠しておらず、UTF-8をWTF-8として扱います。
  • サロゲートソリューションは、異なるエンコーディング間の変換で多くのセキュリティ問題を引き起こし、それらのほとんどは適切に処理されました。

トピックdetailsに従うために、多くの歴史的詳細が抑制されました。
最新のUnicode標準は、http://www.unicode.org/versions/latestにあります。


3

サロゲートペアは、UTF-16の2つの「コード単位」であり、1つの「コードポイント」を構成します。Javaのドキュメントでは、これらの「コードポイント」は引き続き有効であり、「コード単位」は逆の順序で正しく並べられていると述べています。さらに、ペアになっていない2つのサロゲートコードユニットを逆にして、有効なサロゲートペアを形成できると述べています。つまり、ペアになっていないコードユニットがある場合、逆の逆が同じではない可能性があります。

ただし、ドキュメントには、複数のコードポイントを組み合わせたGraphemesについては何も記載されていません。つまり、eとそれに伴うアクセントがまだ切り替えられている可能性があるため、eの前にアクセントを配置します。つまり、eの前に別の母音がある場合、eにあったアクセントが得られる可能性があります。

うわぁ!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.