25

java.text.CharacterIteratorドキュメントからの抜粋は次のとおりです。

  • これinterfaceは、テキストに対する双方向反復のプロトコルを定義します。イテレータは、限定された一連の文字を繰り返し処理します。[...] メソッドprevious()next()は反復に使用されます。[...] の場合に返さDONEれ、イテレータがシーケンスの最後に到達したことを示します。

  • static final char DONE: 反復子がテキストの末尾または先頭に到達したときに返される定数。値は、有効な Unicode 文字列に出現してはならない\uFFFF「文字ではない」値です。

イタリック体の部分は、私が理解するのに苦労しているものです。私のテストでは、JavaにStringは が含まれている可能性が最も高い\uFFFFように見えCharacterIteratorます。誤検知 (たとえば、実際には「完了」していない場合にnext()返されます)。'\uFFFF' == DONE

「問題」を説明するためのスニペットを次に示します ( ideone.com も参照)。

import java.text.*;
public class CharacterIteratorTest {

    // this is the prescribed traversal idiom from the documentation
    public static void traverseForward(CharacterIterator iter) {
       for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
          System.out.print(c);
       }
    }

    public static void main(String[] args) {
        String s = "abc\uFFFFdef";

        System.out.println(s);
        // abc?def

        System.out.println(s.indexOf('\uFFFF'));
        // 3
        
        traverseForward(new StringCharacterIterator(s));
        // abc
    }
}

それで、ここで何が起こっているのですか?

  • 規定されたトラバーサル イディオムは「壊れている」の\uFFFFでしょうか?
  • たとえば、有効な Unicode 文字列で実際に禁止されていない場合、StringCharacterIterator実装は「壊れている」のでしょうか?throwIllegalArgumentException\uFFFF
  • 有効な Unicode 文字列に が含まれてはならないというのは本当\uFFFFですか?
  • それが本当なら、(ほとんどの部分で)とにかくString含むことを許可することによって、Unicode 仕様に違反するために Java は「壊れている」のでしょうか?\uFFFF
4

4 に答える 4

29

編集 (2013-12-17): ピーター O.は、以下の優れた点を提起しているため、この回答は間違っています。歴史的な正確さのために、以下の古い回答。


質問への回答:

\uFFFF について間違った仮定をしているため、規定されたトラバーサル イディオムは「壊れている」のでしょうか?

いいえ、U+FFFF はいわゆる非文字です。Unicode 標準のセクション 16.7から:

非文字は、内部使用のために Unicode 標準で永続的に予約されているコード ポイントです。Unicode テキスト データのオープンな交換での使用は禁止されています。

...

Unicode 標準では、66 個の非文字コード ポイントが確保されています。各プレーンの最後の 2 つのコード ポイントは非文字です。BMP では U+FFFE と U+FFFF、プレーン 1 では U+1FFFE と U+1FFFF など、プレーン 16 では U+10FFFE と U+10FFFF まで続きます。合計 34 のコード ポイント。さらに、BMP には別の 32 個の非文字コード ポイントの連続した範囲があります: U+FDD0..U+FDEF。

たとえば、有効な Unicode 文字列で実際に \uFFFF が禁止されている場合に IllegalArgumentException をスローしないため、StringCharacterIterator の実装は「壊れている」のでしょうか?

そうではありません。アプリケーションは、これらのコード ポイントを任意の方法で内部的に使用できます。標準を再度引用する:

アプリケーションは、これらの非文字コード ポイントを内部で自由に使用できますが、それら を交換しようとしないでください。非文字がオープン インターチェンジで受信された場合、アプリケーションはそれを解釈する必要はありません。ただし、それを非文字として認識し、U+FFFD REPLACEMENT CHARACTER に置き換えるなどの適切なアクションを実行して、テキスト内の問題を示すことをお勧めします。解釈されていない文字を削除するとセキュリティ上の問題が発生する可能性があるため、そのようなテキストから文字以外のコード ポイントを単純に削除することはお勧めしません。

したがって、ユーザー、別のアプリケーション、またはファイルからそのような文字列に遭遇することは決してありませんが、何をしているのかわかっている場合は、Java String に入れることもできます (これは基本的に、その文字列で CharacterIterator を使用できないことを意味します。けれど。

有効な Unicode 文字列に \uFFFF を含めてはならないというのは本当ですか?

上で引用したように、交換に使用される文字列にはそれらを含めてはなりません。アプリケーション内で、それらを好きなように自由に使用できます。

もちろん、 Javacharは 16 ビットの符号なし整数であるため、それが保持する値もあまり気にしません。

それが本当なら、Java は (ほとんどの部分で) String に \uFFFF を含むことを許可することで Unicode 仕様に違反したことで「壊れた」のでしょうか?

いいえ。実際、非文字に関するセクションでは、U+FFFF をセンチネル値として使用することを提案しています。

実際、非文字は、アプリケーション内部の私用コード ポイントと考えることができます。セクション 16.5, Private-Use Charactersで説明されている私用文字とは異なり、割り当てられた文字であり、オープン インターチェンジでの使用が意図されており、私的合意による解釈の対象となります。アプリケーション内部の私的な使用の可能性。

U+FFFF と U+10FFFF。これらの 2 つの非文字コード ポイントには、特定の Unicode エンコード形式の最大コード単位値に関連付けられるという属性があります。UTF-16 では、U+FFFF は最大の 16 ビット コード単位値 FFFF 16に関連付けられます。U+10FFFF は、最大の正当な UTF-32 32 ビット コード単位値である 10FFFF 16に関連付けられています。この属性は、これらの 2 つの非文字コード ポイントを、内部目的でセンチネルとして有効にします。たとえば、リストの終わりを示したり、有効な文字値よりも大きいことが保証されているインデックス内の値を表すために使用される場合があります。

CharacterIterator は、使用できる文字がなくなると U+FFFF を返すという点でこれに従います。もちろん、これは、アプリケーションでそのコード ポイントを別の用途に使用する場合、少なくとも CharacterIterator を使用している場合は、U+FFFF が既に使用されているため、その目的のために別の非文字を使用することを検討できることを意味します。

于 2010-08-14T09:09:04.937 に答える
20

これらの回答の一部は、その間に変更されました。

Unicode コンソーシアムは最近、Unicode 文字列における U+FFFF を含む非文字の役割を明確にするCorrigendum 9を発行しました。非文字は内部使用を目的としていますが、Unicode 文字列で合法的に発生する可能性があると述べています。

これは、「値は \uFFFF であり、有効な Unicode 文字列に出現してはならない「文字ではない」値です」というステートメントを意味します。U+FFFFは有効な Unicode 文字列で発生する可能性があるため、現在は正しくありません。

によると:

  • 有効な Unicode 文字列で \uFFFF が禁止されている場合、例外をスローしないため、StringCharacterIterator の実装は「壊れている」のでしょうか? U+FFFF は有効なので、ここでは当てはまりません。しかし、実装は、対になっていないサロゲート コード ポイントなど、他の理由で不正なテキストに遭遇した場合に、エラーを通知する幅広い柔軟性を備えています。

  • 有効な Unicode 文字列に \uFFFF を含めてはならないというのは本当ですか? U+FFFF は、有効な Unicode 文字列では違法ではありません。

    ただし、U+FFFF は非文字として予約されているため、通常、意味のあるテキストでは発生しません。正誤表は、非文字は「交換してはならない」というテキストを削除しました。これは、ここで問題となっている StringCharacterIterator API を含め、「Unicode 文字列が API の境界を越えるときはいつでも」発生すると正誤表に記載されています。

  • それが本当なら、文字列に \uFFFF を含めることを許可することで Unicode 仕様に違反したことで、Java は「壊れた」のでしょうか? の仕様にjava.lang.Stringは、「文字列は UTF-16 形式の文字列を表す」と書かれています。U+FFFF は Unicode 文字列では合法であるため、Java はそれを含む文字列で U+FFFF を許可することで Unicode に違反しません。

一般に、上位レベルのプロトコルは、Unicode 標準の上に独自のルールを課すことができます。これは、プロトコルによって受け入れられるドキュメントでどの文字が許可されるかという問題についてです。これは、たとえば XML 仕様の場合です。一般に、U+FFFF (およびその他の Unicode スカラー値) は、上位レベルのプロトコル (XML など) で特に指定されていない限り、テキスト文字列に有効に表示できます。実際、視覚的な混乱によるセキュリティ攻撃を減らすために、Rust などの特定のプログラミング言語で Unicode 双方向オーバーライド文字の使用を制限する現在の取り組み (2021 年 11 月 15 日現在) があります。

于 2013-05-18T01:41:59.887 に答える
3

たとえば、有効な Unicode 文字列で実際に \uFFFF が禁止されている場合に IllegalArgumentException をスローしないため、StringCharacterIterator の実装は「壊れている」のでしょうか?

厳密には Unicode に準拠しているわけではありませんが、Java の文字列処理インターフェイスの残りの部分と矛盾しており、その矛盾が非常に不快な影響を与える可能性があります。\0ターミネータとして処理する文字列処理と処理しない文字列処理から発生したすべてのセキュリティ ホールについて考えてみてください。

私はインターフェースを強く避けCharacterIteratorます。

于 2010-08-19T14:17:04.343 に答える
2

はい、CharacterIterator が DONE 値として 0xFFFF を使用するのは少し異常です。しかし、効率的なテキスト処理の観点からは、すべて理にかなっています。

String クラスは、0xFFFF の「非文字」およびその他の予約済みまたはマップされていない Unicode コードポイントを禁止しません。これを行うには、文字列コンストラクターが提供された各char値をチェックする必要があります。また、(JVM に関して) 将来のバージョンの Unicode で定義される Unicode コード ポイントを含むテキストを処理する際にも問題が発生します。

一方、CharacterIterator インターフェイスは、1 つのメソッドを呼び出すだけで反復できるように設計されています。すなわちnext()。他の代替手段があるため、彼らは識別char値を使用して「これ以上ない」ことを示すことにしました。

  • 例外をスローする (コストがかかりすぎる)、または
  • int呼び出し元にとってより複雑な戻り値の型として使用します。

CharacterIterator が「実際の」Unicode テキストに使用される場合、0xFFFF を含めることができないという事実は問題になりません。有効な Unicode テキストには、このコード ポイントは含まれません。(実際、0xFFFF が文字以外として予約されている理由は、Unicode テキストが文字以外の値で終了する文字列として表されるアプリケーションをサポートするためです。文字として 0xFFFF を使用すると、それが完全に壊れます。)

要点は次のとおりです。

  • 厳密な Unicode 文字列が必要な場合は、使用しないStringでください。
  • 0xFFFF 値を含む Java String を反復処理する場合は、CharacterIterator を使用しないでください。
于 2010-08-14T09:45:34.420 に答える