web-dev-qa-db-ja.com

`c> = '0'`または` c> = 48`をチェックする方が良いですか?

同僚と話し合った後、ベストプラクティスに従って、Javaでcharデータ型をどのように扱うかについて、「哲学的」な質問をしました。

簡単なシナリオを考えてみてください(これは明らかに私の質問に練習の意味を与えるための非常に単純な例にすぎません)ここで、入力として文字列 's'を指定すると、数値の数を数える必要がありますその中に存在する文字。

これらは2つの可能なソリューションです。

1)

    for(int i=0; i<s.length(); i++) {
        if(s.charAt(i) >= 48 && s.charAt(i) <= 57) {
            n++;
        }
    }

2)

    for(int i=0; i<s.length(); i++) {
        if(s.charAt(i) >= '0' && s.charAt(i) <= '9' ) {
            n++;
        }
    }

2つのうちどちらがより「クリーン」で、Javaベストプラクティスに準拠していますか?

46
wyr0

どちらもひどいですが、最初のものはもっとひどいです。

どちらも、(Characterのメソッドを介して)どの文字が「数値」であるかを決定するJavaの組み込み機能を無視します。しかし、最初のものは、文字列のUnicodeの性質を無視するだけでなく、0123456789しか存在できないと仮定して、alsoは、履歴について何か知っている場合にのみ意味のある文字コードを使用することにより、この無効な推論を覆い隠します文字エンコーディングの。

124
Kilian Foth

どちらでもない。 Javaの組み込み Character クラスがそれを理解できるようにします。

_for (int i = 0; i < s.length(); ++i) {
  if (Character.isDigit(s.charAt(i))) {
    ++n;
  }
}
_

数字として数えるASCII数字よりも文字の範囲がいくつかあり、投稿したどちらの例もそれらを数えません。 JavaDoc for Character.isDigit()は、これらの文字範囲を有効な数字としてリストします。

数字を含むいくつかのUnicode文字範囲:

  • '\ u0030'から '\ u0039'、ISO-LATIN-1桁( '0'から '9')
  • '\ u0660'〜 '\ u0669'、アラビア語-インド数字
  • '\ u06F0'から '\ u06F9'、拡張アラビア語-インド数字
  • '\ u0966'〜 '\ u096F'、デバナーガリ数字
  • '\ uFF10'〜 '\ uFF19'、全角数字

他の多くの文字範囲にも数字が含まれています。

そうは言っても、このリストがあってもCharacter.isDigit()に委譲する必要があります。新しいUnicodeプレーンが入力されると、Javaコードが更新されます。JVMをアップグレードすると、古いコードで新しい数字文字をシームレスに機能させることができます。また、 [〜#〜] dry [〜#〜] :「これは数字です」コードを他の場所で参照される1つの場所にローカライズすることで、コードの重複の否定的な側面(バグ)を回避できます。最後に、最後の行に注意してください:このリストは網羅的で、他の数字があります。

個人的には、コアのJava=ライブラリーに委任し、「数字とは何か」を設定するよりも生産的なタスクに時間を費やしたいと思っています。


このルールの唯一の例外は、リテラルASCII桁であり、他の桁ではでないかどうかをテストする必要がある場合です) 。たとえば、ストリームを解析していて、onlyASCII)桁(他の桁ではなく)に特別なつまり、Character.isDigit()を使用するのは適切ではありませんnot

その場合、別のメソッドを記述します。 MyClass.isAsciiDigit()そしてロジックをそこに配置します。コードを再利用するのと同じ利点が得られ、名前が何をチェックしているかが非常に明確で、ロジックが正しいです。

163
user22815

基本的な文字セットとしてEBCDICを使用し、ASCII文字を処理する必要があるアプリケーションをCで作成する場合は、_48_および_57_を使用します。それを行っていますか?私はそうは思いません。

isDigit()の使用について:状況によって異なります。 JSONパーサーを作成していますか?数字として受け入れられるのは_0_から_9_までなので、isDigit()は使用せず、_>= '0'_および_<= '9'_を確認してください。ユーザー入力を処理していますか?コードの残りの部分が実際に文字列を処理し、それを数値に正しく変換できる限り、isDigit()を使用します。

27
gnasher729

2番目の例は明らかに優れています。 2番目の例の意味は、コードを見るとすぐにわかります。最初の例の意味は、ASCIIテーブル全体を頭に覚えている場合にのみ明らかです。

特定の文字をチェックするか、文字の範囲またはクラスをチェックするかを区別する必要があります。

1)特定の文字をチェックします。

通常の文字の場合、文字リテラルを使用します(例:if(ch=='z')...)。タブや改行などの特殊文字をチェックする場合は、if (ch=='\n')...などのエスケープを使用する必要があります。チェックしている文字が異常な場合(たとえば、すぐに認識できない、または標準キーボードで使用できない場合)、リテラル文字ではなく16進文字コードを使用できます。しかし、16進コードは「マジックバリュー」なので、定数に抽出してドキュメント化します。

const char snowman = 0x2603; // snowman char used to detect encoding issues
...
if (ch==showman)...

16進コードは、文字コードを指定する標準的な方法です。

2)文字クラスまたは範囲の確認

アプリケーションコードで直接これを行うべきではありませんが、文字の分類のみに関係する別のクラスにカプセル化する必要があります。そして、この目的のためにライブラリがすでに存在しているため、これはさまざまである必要があります。少なくともASCII範囲外の文字を考慮する場合、文字の分類は通常、思ったよりも複雑です。

ASCII範囲内の文字のみを懸念している場合は、このライブラリで文字リテラルを使用できます。それ以外の場合は、16進リテラルを使用することになります。 Java組み込み文字ライブラリ。これは、Unicode標準での指定方法であるため、16進数を使用して文字値と範囲も参照します。

12
JacquesB