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


46

동료들과 토론을 한 후 모범 사례에 따라 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 우수 사례를 준수합니까?


141
실제로 '0'과 '9'를 의미 할 때 왜 48과 57을 쓸까요? 당신이 의미하는 것을 쓰십시오.
Brandin

9
Java가 VK_사용해야 할 상수가 있으며, 두 번째로 char 코드를 사용하는 것이 char보다 낫습니다. Java는 교차 유형 검사를 수행하지 않아야하는 안전한 유형의 언어입니다. @Brandin 코딩 관행이라고합니다
Martin Barker

12
이 질문이 좋은 사람은 6 명 이상을 판단하지 않아도됩니다. 문자를 숫자로 사용하고 있습니까? 그렇다면 숫자를 사용하십시오. 문자로 사용하고 있습니까? 그렇다면 글자를 사용하십시오.
Alec Teal

17
@MartinBarker VK_*상수는 문자가 아닌 키에 해당합니다 .
코드 InChaos

2
귀하의 질문과 관련 하여이 코드의 기능을 결정하는 데 몇 분이 걸렸습니다. 이미 (1)에서 이것이 ISO-Latin 1의 숫자 범위라는 것을 알고 있다고 가정하기 때문에 명확하지 않습니다. 따라서 유지 관리 관점에서 문제가됩니다.
CyberSkull

답변:


124

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

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


33
비 거절 비 ASCII 숫자가 잘못되었다고 가정하는 이유는 무엇입니까? 상황에 따라 다릅니다.
코드 InChaos

21
@CodesInChaos 실제로 숫자 를 찾으려면 0123456789 스캔이 잘못되었습니다. 실제로이 10 개의 문자 만 검색하려는 경우 ASCII / ISO-Latin 만 알고있는 사람들에게 우연히 친숙하게 보이는 토큰은 의미가 없습니다. 아무 문제가 없습니다. 예를 들어, 실제로 10 자만 허용하는 레거시 소프트웨어와 상호 작용하기 위해서는 종종 정확하게해야합니다. 그러나 matches("[0-9]+")역사적으로 동기가 부여 된 범위 트릭을 악용하기보다는와 같은 것을 사용하여 의도를 명확하게해야합니다 .
Kilian Foth

15
ASCII 숫자와 같은 전체 너비 숫자 가 있으며 일반적으로 ASCII 숫자 대신이를 받아들이려면 많은 소프트웨어가 필요합니다. "많은"의 정의에 따라 많은 소프트웨어가 망가 졌을 수 있습니다. 한 국가의 소프트웨어 공급 업체가 다른 국가의 요구 사항을 준수하지 않기 때문에 다른 국가에 판매 할 수 없기 때문에 쉽게 알 수 있습니다. )
rwong

37
IhhveaaJapanese IME
BlueRaja-대니 Pflughoeft

14
"둘 다 무섭다",하지만 당신은 적합한 솔루션을 ;-) 말을 잊었
Kromster 지원 모니카 말한다

163

둘 다. 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()하고 논리를 거기에 넣습니다. 코드 재사용의 이점과 동일한 이점을 얻을 수 있으며 이름이 확인 대상에 대해 명확하고 논리가 정확합니다.


4
실제로 트릭을 수행하는 깨끗한 코드를 제공하는 훌륭한 답변입니다.
Pierre Arlaud

27

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

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


3
실제로 EBCDIC를 가져오고 리턴하는 애플리케이션을 Java로 작성할 수 있습니다. 재미 없어
Thorbjørn Ravn Andersen

EBCDIC 문자를 크로스 플랫폼 환경으로 변환 할 때 EBCDIC 문자의 10 진수 값을 사용하여 작성된 코드를 살펴보면 비슷한 재미가 없습니다.
Gwyn Evans

1
Java로 EBCDIC 데이터를 처리하는 경우 문자로 처리하기 전에 Java 원시 UTF-16 문자 세트로 변환해야합니다. 그러나 나는 그것이 실제로 응용 프로그램에 달려 있다고 생각합니다. 프로그램이 EBCDIC를 다루어야한다면, 무엇을해야하는지 이해할 것입니다.
Michael Burr

1
요점은 Java에서 EBCDIC를 처리 할 때 '0'과 48이 모두 숫자 0을 감지하는 데 잘못되었다는 것 입니다. 더 최근의 C, C ++ 등에서 '\ n'및 '\ r'은 구현이 정의되어 있으므로 비 Windows 컴파일러를 사용하여 파일에서 Windows CR / LF 쌍을 감지하려면 대신 10 진수 값을 확인하십시오 '\ n'및 '\ r'확인
gnasher729

12

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


1
또한 '\x2603'임의의 숫자가 아닌 16 진수 인코딩으로 문자 값을 테스트하고 있음을 명시 적으로 나타 내기 위해 16 진수로 문자 리터럴을 작성하는 것이 좋습니다 .
wefwefa3

-4

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


3
이 답변은 일주일 전의 이전 답변에서 아직 언급되지 않은 내용은 무엇입니까?

-5

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

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

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

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


확인하는 아주 우아한 방법!
Kevin Robatel

정규식은 이와 같은 작업을 위해 과잉입니다
Pharap

2
@StefanoBragaglia 귀하의 답변을 다시 읽은 후에는 실제로 질문에 대한 답변이 아니라고 생각합니다.
Pharap

2
귀하의 답변은 "문자열의 숫자를 어떻게 계산합니까?"의 문제를 해결하는 다른 방법을 제공합니다. 코드 샘플 및 상수 표현의 기본 문제 (숫자 또는 문자)에는 대답하지 않습니다.

2
이것은 실제로 숫자를 세지 않습니다 (여기서도 아니고 모든 숫자를 제거한 후 문자열의 길이를 알려줍니다). 그러나 실제로 질문에 대답하지는 않는다는 데 동의합니다. 예를 들어, 아무도 문자열에서 문자를 제거하는 것에 대해 묻지 않았습니다. 질문은 문자의 숫자 여부를 확인하는 가장 좋은 방법을 묻는 것입니다.
doppelgreener
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.