1. 소개
종종 String s에서 작업하는 동안 String이 유효한 숫자인지 여부 를 파악해야 합니다 .
이 사용방법(예제)에서는 먼저 일반 Java를 사용한 다음 정규식을 사용하고 마지막으로 외부 라이브러리를 사용하여 주어진 문자열이 숫자인지 감지하는 여러 가지 방법을 탐색합니다 .
다양한 구현에 대한 논의가 끝나면 벤치마크를 사용하여 어떤 방법이 최적인지 파악합니다.
2. 전제 조건
주요 콘텐츠로 이동하기 전에 몇 가지 전제 조건부터 시작하겠습니다.
이 문서의 후반부에서는 Apache Commons 외부 라이브러리를 사용하여 pom.xml 에 의존성을 추가합니다 .
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
이 라이브러리의 최신 버전은 Maven Central 에서 찾을 수 있습니다 .
3. 일반 자바 사용
문자열이 숫자인지 여부 를 확인하는 가장 쉽고 신뢰할 수 있는 방법은 Java의 기본 제공 메서드를 사용하여 문자열을 구문 분석하는 것입니다.
- Integer.parseInt(문자열)
- Float.parseFloat(문자열)
- Double.parseDouble(문자열)
- Long.parseLong(문자열)
- 새로운 BigInteger(문자열)
이러한 메서드가 NumberFormatException 을 발생시키지 않으면 구문 분석이 성공했고 문자열 이 숫자라는 의미입니다 .
public static boolean isNumeric(String strNum) {
if (strNum == null) {
return false;
}
try {
double d = Double.parseDouble(strNum);
} catch (NumberFormatException nfe) {
return false;
}
return true;
}
이 방법이 실제로 작동하는 것을 봅시다:
assertThat(isNumeric("22")).isTrue();
assertThat(isNumeric("5.05")).isTrue();
assertThat(isNumeric("-200")).isTrue();
assertThat(isNumeric("10.0d")).isTrue();
assertThat(isNumeric(" 22 ")).isTrue();
assertThat(isNumeric(null)).isFalse();
assertThat(isNumeric("")).isFalse();
assertThat(isNumeric("abc")).isFalse();
isNumeric() 메서드 에서 Double 유형의 값만 확인합니다 . 그러나 이전에 등록한 구문 분석 방법을 사용하여 Integer , Float , Long 및 큰 숫자를 확인하도록 이 방법을 수정할 수도 있습니다 .
이러한 메서드는 Java 문자열 변환 문서 에서도 설명합니다 .
4. 정규 표현식 사용
이제 정규식 -?\d+(\.\d+)? 양수 또는 음수 정수와 실수로 구성된 숫자 문자열을 일치시킵니다 .
광범위한 규칙을 식별하고 처리하기 위해 이 정규식을 확실히 수정할 수 있다는 것은 말할 필요도 없습니다. 여기서는 간단하게 유지하겠습니다.
이 정규식을 분석하고 작동 방식을 살펴보겠습니다.
- -? – 이 부분은 주어진 숫자가 음수인지 여부를 식별하고 대시 " – "는 문자 그대로 대시를 검색하고 물음표 " ? ” 옵션으로 그 존재를 표시합니다.
- \d+ – 하나 이상의 숫자를 검색합니다.
- (\.\d+)? – 정규식의 이 부분은 float 숫자를 식별하는 것입니다. 여기서 우리는 하나 이상의 숫자와 마침표를 검색합니다. 결국 물음표는 이 완전한 그룹이 선택 사항임을 나타냅니다.
정규식은 매우 광범위한 주제입니다. 간략한 개요를 보려면 Java 정규 표현식 API 에 대한 사용방법(예제)를 확인하십시오 .
지금은 위의 정규식을 사용하여 메서드를 만들어 보겠습니다.
private Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?");
public boolean isNumeric(String strNum) {
if (strNum == null) {
return false;
}
return pattern.matcher(strNum).matches();
}
이제 위의 메서드에 대한 몇 가지 주장을 살펴보겠습니다.
assertThat(isNumeric("22")).isTrue();
assertThat(isNumeric("5.05")).isTrue();
assertThat(isNumeric("-200")).isTrue();
assertThat(isNumeric(null)).isFalse();
assertThat(isNumeric("abc")).isFalse();
5. 아파치 커먼즈 사용
이 섹션에서는 Apache Commons 라이브러리에서 사용할 수 있는 다양한 방법에 대해 설명합니다.
5.1. NumberUtils.isCreatable(문자열)
Apache Commons의 NumberUtils는 정적 메서드 NumberUtils.isCreatable(String) 을 제공 하여 문자열이 유효한 Java 번호인지여부를 확인합니다
이 방법은 다음을 허용합니다.
- 0x 또는 0X로 시작하는 16진수
- 선행 0으로 시작하는 8진수
- 과학 표기법(예: 1.05e-10)
- 유형 한정자로 표시된 숫자(예: 1L 또는 2.2d)
제공된 문자열이 null 또는 empty/blank 이면 숫자로 간주되지 않으며 메서드는 false 를 반환합니다 .
이 방법을 사용하여 몇 가지 테스트를 실행해 보겠습니다.
assertThat(NumberUtils.isCreatable("22")).isTrue();
assertThat(NumberUtils.isCreatable("5.05")).isTrue();
assertThat(NumberUtils.isCreatable("-200")).isTrue();
assertThat(NumberUtils.isCreatable("10.0d")).isTrue();
assertThat(NumberUtils.isCreatable("1000L")).isTrue();
assertThat(NumberUtils.isCreatable("0xFF")).isTrue();
assertThat(NumberUtils.isCreatable("07")).isTrue();
assertThat(NumberUtils.isCreatable("2.99e+8")).isTrue();
assertThat(NumberUtils.isCreatable(null)).isFalse();
assertThat(NumberUtils.isCreatable("")).isFalse();
assertThat(NumberUtils.isCreatable("abc")).isFalse();
assertThat(NumberUtils.isCreatable(" 22 ")).isFalse();
assertThat(NumberUtils.isCreatable("09")).isFalse();
6, 7, 8행에서 각각 16진수, 8진수 및 과학 표기법에 대한 진정한 어설션을 얻고 있음에 유의하십시오 .
또한 14행에서 문자열 "09"는 앞의 "0"이 이것이 8진수임을 나타내고 "09" 는 유효한 8진수가 아니기 때문에 false를 반환합니다 .
이 메서드로 true를 반환하는 모든 입력에 대해 유효한 숫자를 제공하는 NumberUtils.createNumber(String) 를 사용할 수 있습니다 .
5.2. NumberUtils.isParsable(문자열)
NumberUtils.isParsable (String) 메서드는 주어진 문자열이 구문 분석 가능한지 여부를 확인합니다.
구문 분석 가능한 숫자는 Integer.parseInt(String) , Long.parseLong(String) , Float.parseFloat(String) 또는 Double.parseDouble(String) 과 같은 구문 분석 방법으로 성공적으로 구문 분석된 숫자입니다 .
NumberUtils.isCreatable() 과 달리 이 메서드는 16진수, 과학적 표기법 또는 'f', 'F', 'd' ,'D' ,'l' 또는 'L ' 과 같은 한정자로 끝나는 문자열을 허용하지 않습니다. ' .
몇 가지 확언을 살펴보겠습니다.
assertThat(NumberUtils.isParsable("22")).isTrue();
assertThat(NumberUtils.isParsable("-23")).isTrue();
assertThat(NumberUtils.isParsable("2.2")).isTrue();
assertThat(NumberUtils.isParsable("09")).isTrue();
assertThat(NumberUtils.isParsable(null)).isFalse();
assertThat(NumberUtils.isParsable("")).isFalse();
assertThat(NumberUtils.isParsable("6.2f")).isFalse();
assertThat(NumberUtils.isParsable("9.8d")).isFalse();
assertThat(NumberUtils.isParsable("22L")).isFalse();
assertThat(NumberUtils.isParsable("0xFF")).isFalse();
assertThat(NumberUtils.isParsable("2.99e+8")).isFalse();
4행에서 NumberUtils.isCreatable() 과 달리 문자열 "0" 으로 시작하는 숫자는 8진수가 아닌 일반 10진수로 간주되므로 true를 반환합니다.
이 방법을 섹션 3에서 숫자를 구문 분석하고 오류를 확인하는 작업을 대신하여 사용할 수 있습니다.
5.3. StringUtils.isNumeric(CharSequence )
StringUtils.isNumeric(CharSequence) 메서드는 유니코드 숫자를 엄격하게 검사합니다. 이는 다음을 의미합니다.
- 유니코드 숫자인 모든 언어의 모든 숫자가 허용됩니다.
- 소수점은 유니코드 숫자로 간주되지 않으므로 유효하지 않습니다.
- 선행 기호(양수 또는 음수)도 허용되지 않습니다.
이제 이 방법이 실제로 작동하는 것을 봅시다:
assertThat(StringUtils.isNumeric("123")).isTrue();
assertThat(StringUtils.isNumeric("١٢٣")).isTrue();
assertThat(StringUtils.isNumeric("१२३")).isTrue();
assertThat(StringUtils.isNumeric(null)).isFalse();
assertThat(StringUtils.isNumeric("")).isFalse();
assertThat(StringUtils.isNumeric(" ")).isFalse();
assertThat(StringUtils.isNumeric("12 3")).isFalse();
assertThat(StringUtils.isNumeric("ab2c")).isFalse();
assertThat(StringUtils.isNumeric("12.3")).isFalse();
assertThat(StringUtils.isNumeric("-123")).isFalse();
2행과 3행의 입력 매개변수는 각각 아랍어와 데바나가리어로 숫자 123 을 나타냅니다 . 유효한 유니코드 숫자이므로 이 메서드는 true를 반환합니다 .
5.4. StringUtils.isNumericSpace(CharSequence)
StringUtils.isNumericSpace (CharSequence)는 유니코드 숫자 및/또는 공백을 엄격하게 검사합니다. 이는 StringUtils.isNumeric() 과 동일 하지만 선행 및 후행 공백뿐만 아니라 숫자 사이에 공백도 허용한다는 점이 다릅니다.
assertThat(StringUtils.isNumericSpace("123")).isTrue();
assertThat(StringUtils.isNumericSpace("١٢٣")).isTrue();
assertThat(StringUtils.isNumericSpace("")).isTrue();
assertThat(StringUtils.isNumericSpace(" ")).isTrue();
assertThat(StringUtils.isNumericSpace("12 3")).isTrue();
assertThat(StringUtils.isNumericSpace(null)).isFalse();
assertThat(StringUtils.isNumericSpace("ab2c")).isFalse();
assertThat(StringUtils.isNumericSpace("12.3")).isFalse();
assertThat(StringUtils.isNumericSpace("-123")).isFalse();
6. 벤치마크
이 기사를 마치기 전에 위에서 언급한 방법 중 우리의 사용 사례에 가장 적합한 방법을 분석하는 데 도움이 되는 몇 가지 벤치마크 결과를 살펴보겠습니다.
6.1. 간단한 벤치마크
먼저 간단한 접근 방식을 취합니다. 하나의 문자열 값을 선택합니다. 테스트를 위해 Integer.MAX_VALUE 를 사용합니다 . 그 값은 모든 구현에 대해 테스트됩니다.
Benchmark Mode Cnt Score Error Units
Benchmarking.usingCoreJava avgt 20 57.241 ± 0.792 ns/op
Benchmarking.usingNumberUtils_isCreatable avgt 20 26.711 ± 1.110 ns/op
Benchmarking.usingNumberUtils_isParsable avgt 20 46.577 ± 1.973 ns/op
Benchmarking.usingRegularExpressions avgt 20 101.580 ± 4.244 ns/op
Benchmarking.usingStringUtils_isNumeric avgt 20 35.885 ± 1.691 ns/op
Benchmarking.usingStringUtils_isNumericSpace avgt 20 31.979 ± 1.393 ns/op
보시다시피 가장 비용이 많이 드는 작업은 정규 표현식입니다. 그 다음은 핵심 Java 기반 솔루션입니다.
또한 Apache Commons 라이브러리를 사용하는 작업은 대체로 동일합니다.
6.2. 향상된 벤치마크
보다 대표적인 벤치마크를 위해 보다 다양한 테스트 세트를 사용해 보겠습니다.
- 95개의 값은 숫자입니다(0-94 및 Integer.MAX_VALUE ).
- 3은 숫자를 포함하지만 여전히 잘못된 형식입니다 — ' x0 ', ' 0. .005′ 및 ' –11 '
- 1은 텍스트만 포함
- 1은 널
동일한 테스트를 실행하면 결과가 표시됩니다.
Benchmark Mode Cnt Score Error Units
Benchmarking.usingCoreJava avgt 20 10162.872 ± 798.387 ns/op
Benchmarking.usingNumberUtils_isCreatable avgt 20 1703.243 ± 108.244 ns/op
Benchmarking.usingNumberUtils_isParsable avgt 20 1589.915 ± 203.052 ns/op
Benchmarking.usingRegularExpressions avgt 20 7168.761 ± 344.597 ns/op
Benchmarking.usingStringUtils_isNumeric avgt 20 1071.753 ± 8.657 ns/op
Benchmarking.usingStringUtils_isNumericSpace avgt 20 1157.722 ± 24.139 ns/op
가장 중요한 차이점은 테스트 중 두 가지인 정규식 솔루션과 핵심 Java 기반 솔루션이 자리를 바꿨다는 것입니다.
이 결과로부터 우리는 5%의 경우에만 발생하는 NumberFormatException 의 throw 및 처리가 전체 성능에 상대적으로 큰 영향을 미친다는 것을 알 수 있습니다. 따라서 최적의 솔루션은 예상되는 입력에 따라 다르다는 결론을 내릴 수 있습니다.
또한 최적의 성능을 위해 Commons 라이브러리의 메서드 또는 유사하게 구현된 메서드를 사용해야 한다고 안전하게 결론을 내릴 수 있습니다.
7. 결론
이 기사에서는 문자열이 숫자인지 여부를 찾는 다양한 방법을 살펴보았습니다 . 우리는 내장 방법과 외부 라이브러리의 두 가지 솔루션을 모두 살펴보았습니다.
항상 그렇듯이 벤치마크를 수행하는 데 사용되는 코드를 포함하여 위에 제공된 모든 예제 및 코드 스니펫의 구현은 GitHub 에서 찾을 수 있습니다 .