`c> = ‘0’` 또는`c> = 48`를 확인하는 것이 더 낫습니까?

동료들과 토론을 한 후 모범 사례에 따라 Java에서 char 데이터 유형을 처리하는 방법에 대한 ‘철학적’질문이 있습니다.

간단한 시나리오 (분명히 이것은 내 질문에 연습 의미를 부여하기 위해 매우 간단한 예일뿐입니다)를 가정하십시오. 여기서 String ‘s’가 입력으로 주어지면 그 안에 존재하는 숫자의 수를 세어야합니다.

가능한 두 가지 해결책은 다음과 같습니다.

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++;
        }
    }

둘 중 어느 것이 ‘깨끗하고’Java 우수 사례를 준수합니까?



답변

둘 다 끔찍하지만 첫 번째가 더 끔찍합니다.

둘 다 어떤 문자가 “숫자”인지를 결정하는 Java의 내장 기능을 무시합니다 (의 메소드 사용 Character). 그러나 첫 번째뿐만 아니라 단지 0123456789, 그것은있을 수 있음을 가정하고, 문자열의 유니 코드 특성을 무시 또한 당신이 문자 인코딩의 역사에 대해 뭔가를 알고있는 경우에만 의미가 문자 코드를 사용하여도이 잘못된 추론을 가린다.


답변

둘 다. Java의 기본 제공 문자 클래스를 통해이를 파악하십시오.

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

ASCII 숫자보다 자릿수로 계산되는 문자 범위가 몇 개 더 많으며 게시 한 예 중 어느 것도 문자 수로 계산하지 않습니다. 의 JavaDoc 에 대한 Character.isDigit()목록 유효 숫자 인 이러한 문자 범위 :

숫자가 포함 된 일부 유니 코드 문자 범위 :

  • ‘\ u0030’~ ‘\ u0039’, ISO-LATIN-1 숫자 ( ‘0’~ ‘9’)
  • ‘\ u0660’- ‘\ u0669’, 아라비아 숫자
  • ‘\ u06F0’- ‘\ u06F9’, 확장 아랍어-아라비아 숫자
  • ‘\ u0966’- ‘\ u096F’, 데바 나가리 숫자
  • ‘\ uFF10’- ‘\ uFF19’, 전각 자릿수

다른 많은 문자 범위에도 숫자가 포함됩니다.

즉, Character.isDigit()이 목록 으로 도 위임해야 합니다. 새로운 유니 코드 평면이 채워지면 Java 코드가 업데이트됩니다. JVM을 업그레이드하면 기존 코드가 새로운 숫자로 원활하게 작동 할 수 있습니다. 그것은 또한 DRY입니다 : “이 숫자”코드를 다른 곳에서 참조되는 한 곳으로 현지화함으로써, 코드 중복 (즉, 버그)의 부정적인 측면을 피할 수 있습니다. 마지막으로, 마지막 줄에 주목하십시오 :이 목록은 완전한 것이 아니며 다른 숫자가 있습니다.

개인적으로 저는 핵심 Java 라이브러리에 위임하고 “숫자 란 무엇인가를 나타내는 것”보다 생산적인 작업에 시간을 투자하고 싶습니다.


이 규칙의 유일한 예외는 실제로 다른 숫자가 아닌 리터럴 ASCII 숫자를 테스트해야하는 경우 입니다. 예를 들어 스트림을 구문 분석하고 다른 숫자와 달리 ASCII 숫자 특별한 의미를 가지면 을 사용하는 것이 적절 하지 않습니다Character.isDigit() .

이 경우 다른 방법을 작성 MyClass.isAsciiDigit()하고 논리를 거기에 넣습니다. 코드 재사용의 이점과 동일한 이점을 얻을 수 있으며 이름이 확인 대상에 대해 명확하고 논리가 정확합니다.


답변

EBCDIC를 기본 문자 세트로 사용 48하고 ASCII 문자를 처리해야하는 응용 프로그램을 C로 작성하는 경우 및를 사용하십시오 57. 그렇게하고 있습니까? 나는 그렇게 생각하지 않습니다.

사용 isDigit()방법에 따라 다릅니다. JSON 파서를 작성하고 있습니까? 만 0하기 9때문에 사용하지 않는, 숫자로 받아 들여진다 isDigit()를 확인 >= '0'하고 <= '9'. 사용자 입력을 처리하고 있습니까? 사용 isDigit()한 코드의 나머지 부분은 실제로 문자열을 처리 할 수 정확하게 숫자로 돌려있다.


답변

두 번째 예는 분명히 우수합니다. 두 번째 예제의 의미는 코드를 볼 때 즉시 분명합니다. 첫 번째 예제의 의미는 전체 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 내장 문자 라이브러리의 소스 코드를 보면 16 진수를 사용하여 문자 값과 범위를 나타냅니다. 이것이 유니 코드 표준에 지정된 방식이기 때문입니다.


답변

c를 ASCII 코드로 변환해야하기 c >= '0'때문에 항상 사용하는 것이 좋습니다 c >= 48.


답변

정규 표현식 ( RegEx )에는 숫자에 대한 특정 문자 클래스\d 가 있습니다.-문자열에서 다른 문자를 제거하는 데 사용할 수 있습니다. 결과 문자열의 길이는 원하는 값입니다.

public static int countDigits(String str) {
    str = Objects.requireNonNull(str).trim();

    return str.replaceAll("[^\\d]", "").length();
}

그러나 RegEx 는 제안 된 다른 솔루션보다 계산이 더 까다롭기 때문에 일반적으로 선호되지 않아야합니다 .