diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberParser.java
index 3c8779b23..e8b929e34 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberParser.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberParser.java
@@ -12,6 +12,8 @@
package org.dromara.hutool.core.math;
+import org.dromara.hutool.core.array.ArrayUtil;
+import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil;
@@ -123,6 +125,73 @@ public class NumberParser {
}
}
+ /**
+ * 转换char数组为一个int值,此方法拷贝自{@link Integer#parseInt(String, int)}
+ * 拷贝的原因是直接转换char[]避免创建String对象造成的多余拷贝
+ * 此方法自动跳过首尾空白符
+ *
+ * @param chars char数组
+ * @param radix 进制数
+ * @return int值
+ * @see Integer#parseInt(String, int)
+ */
+ public int parseInt(final char[] chars, final int radix) {
+ if (ArrayUtil.isEmpty(chars)) {
+ throw new IllegalArgumentException("Empty chars!");
+ }
+
+ int result = 0;
+ boolean negative = false;
+ int i = 0;
+ int limit = -Integer.MAX_VALUE;
+ int digit;
+
+ // 跳过空白符
+ while (CharUtil.isBlankChar(chars[i])) {
+ i++;
+ }
+
+ final char firstChar = chars[i];
+ if (firstChar < '0') { // Possible leading "+" or "-"
+ if (firstChar == '-') {
+ negative = true;
+ limit = Integer.MIN_VALUE;
+ } else if (firstChar != '+') {
+ throw new NumberFormatException("Invalid first char: " + firstChar);
+ }
+
+ if (chars.length == 1) {
+ // Cannot have lone "+" or "-"
+ throw new NumberFormatException("Invalid chars has lone: " + firstChar);
+ }
+ i++;
+ }
+
+ final int multmin = limit / radix;
+ while (i < chars.length) {
+ // 跳过空白符
+ if (CharUtil.isBlankChar(chars[i])) {
+ i++;
+ continue;
+ }
+
+ // Accumulating negatively avoids surprises near MAX_VALUE
+ digit = Character.digit(chars[i++], radix);
+ if (digit < 0) {
+ throw new NumberFormatException(StrUtil.format("Invalid chars: {} at {}", chars, i - 1));
+ }
+ if (result < multmin) {
+ throw new NumberFormatException(StrUtil.format("Invalid chars: {}", new Object[]{chars}));
+ }
+ result *= radix;
+ if (result < limit + digit) {
+ throw new NumberFormatException(StrUtil.format("Invalid chars: {}", new Object[]{chars}));
+ }
+ result -= digit;
+ }
+ return negative ? result : -result;
+ }
+
/**
* 解析转换数字字符串为long型数字,规则如下:
*
@@ -293,7 +362,7 @@ public class NumberParser {
if (null == locale) {
locale = Locale.getDefault(Locale.Category.FORMAT);
}
- if(StrUtil.startWith(numberStr, CharUtil.PLUS)){
+ if (StrUtil.startWith(numberStr, CharUtil.PLUS)) {
// issue#I79VS7
numberStr = StrUtil.subSuf(numberStr, 1);
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java
index d3cc57c00..1ee01e01b 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java
@@ -1230,6 +1230,22 @@ public class NumberUtil extends NumberValidator {
return NumberParser.INSTANCE.parseInt(numberStr);
}
+ /**
+ * 转换char数组为一个int值,此方法拷贝自{@link Integer#parseInt(String, int)}
+ * 拷贝的原因是直接转换char[]避免创建String对象造成的多余拷贝。
+ * 此方法自动跳过首尾空白符
+ *
+ * @param chars char数组
+ * @param radix 进制数
+ * @return int值
+ * @throws NumberFormatException 数字格式异常
+ * @see Integer#parseInt(String, int)
+ * @since 6.0.0
+ */
+ public static int parseInt(final char[] chars, final int radix) throws NumberFormatException {
+ return NumberParser.INSTANCE.parseInt(chars, radix);
+ }
+
/**
* 解析转换数字字符串为 {@link java.lang.Long } 规则如下:
*
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/math/NumberParserTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/math/NumberParserTest.java
index d8b611011..4486f89e5 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/math/NumberParserTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/math/NumberParserTest.java
@@ -15,10 +15,51 @@ package org.dromara.hutool.core.math;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
public class NumberParserTest {
@Test
void parseLongTest() {
final long value = NumberParser.INSTANCE.parseLong("0.a");
Assertions.assertEquals(0L, value);
}
+
+ @Test
+ void testParseIntWithValidInputs() {
+ // Test with various valid inputs
+ assertEquals(123, NumberParser.INSTANCE.parseInt("123".toCharArray(), 10));
+ assertEquals(-123, NumberParser.INSTANCE.parseInt("-123".toCharArray(), 10));
+ assertEquals(123, NumberParser.INSTANCE.parseInt("+123".toCharArray(), 10));
+ assertEquals(0, NumberParser.INSTANCE.parseInt("0".toCharArray(), 10));
+ assertEquals(255, NumberParser.INSTANCE.parseInt("ff".toCharArray(), 16));
+ assertEquals(-255, NumberParser.INSTANCE.parseInt("-ff".toCharArray(), 16));
+ }
+
+ @Test
+ void testParseIntWithInvalidInputs() {
+ // Test with various invalid inputs
+ assertThrows(IllegalArgumentException.class, () -> NumberParser.INSTANCE.parseInt("".toCharArray(), 10));
+ assertThrows(NumberFormatException.class, () -> NumberParser.INSTANCE.parseInt("1234".toCharArray(), 2));
+ assertThrows(NumberFormatException.class, () -> NumberParser.INSTANCE.parseInt("abc".toCharArray(), 10));
+ assertThrows(NumberFormatException.class, () -> NumberParser.INSTANCE.parseInt("--123".toCharArray(), 10));
+ assertThrows(NumberFormatException.class, () -> NumberParser.INSTANCE.parseInt("++123".toCharArray(), 10));
+ assertThrows(NumberFormatException.class, () -> NumberParser.INSTANCE.parseInt("123".toCharArray(), 1));
+ }
+
+ @Test
+ void testParseIntWithLeadingAndTrailingWhitespace() {
+ // Test with leading and trailing whitespace
+ assertEquals(42, NumberParser.INSTANCE.parseInt(" 42 ".toCharArray(), 10));
+ }
+
+ @Test
+ void testParseIntWithMaxAndMinInt() {
+ // Test with values at the edge of int range
+ final char[] maxIntStr = Integer.toString(Integer.MAX_VALUE).toCharArray();
+ final char[] minIntStr = Integer.toString(Integer.MIN_VALUE).toCharArray();
+
+ assertEquals(Integer.MAX_VALUE, NumberParser.INSTANCE.parseInt(maxIntStr, 10));
+ assertEquals(Integer.MIN_VALUE, NumberParser.INSTANCE.parseInt(minIntStr, 10));
+ }
}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/math/NumberUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/math/NumberUtilTest.java
index 7c30a8b8d..62afa6b09 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/math/NumberUtilTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/math/NumberUtilTest.java
@@ -26,6 +26,7 @@ import java.text.NumberFormat;
import java.text.ParseException;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* {@link NumberUtil} 单元测试类
@@ -320,7 +321,7 @@ public class NumberUtilTest {
@Test
public void decimalFormatNaNTest() {
- Assertions.assertThrows(IllegalArgumentException.class, ()->{
+ assertThrows(IllegalArgumentException.class, ()->{
final Double a = 0D;
final Double b = 0D;
@@ -331,7 +332,7 @@ public class NumberUtilTest {
@Test
public void decimalFormatNaNTest2() {
- Assertions.assertThrows(IllegalArgumentException.class, ()->{
+ assertThrows(IllegalArgumentException.class, ()->{
final Double a = 0D;
final Double b = 0D;
@@ -383,7 +384,7 @@ public class NumberUtilTest {
@Test
void emptyToBigDecimalTest(){
- Assertions.assertThrows(IllegalArgumentException.class,()->{
+ assertThrows(IllegalArgumentException.class,()->{
NumberUtil.toBigDecimal("");
});
}
@@ -445,7 +446,7 @@ public class NumberUtilTest {
@Test
public void parseIntTest3() {
- Assertions.assertThrows(NumberFormatException.class, ()->{
+ assertThrows(NumberFormatException.class, ()->{
final int v1 = NumberUtil.parseInt("d");
assertEquals(0, v1);
});
@@ -453,7 +454,7 @@ public class NumberUtilTest {
@Test
public void parseIntTest4() {
- Assertions.assertThrows(NumberFormatException.class, ()->{
+ assertThrows(NumberFormatException.class, ()->{
// issue#I5M55F
// 科学计数法忽略支持,科学计数法一般用于表示非常小和非常大的数字,这类数字转换为int后精度丢失,没有意义。
final String numberStr = "429900013E20220812163344551";
@@ -692,7 +693,7 @@ public class NumberUtilTest {
@Test
public void rangeMinTest() {
- Assertions.assertThrows(NegativeArraySizeException.class, ()->{
+ assertThrows(NegativeArraySizeException.class, ()->{
NumberUtil.range(0, Integer.MIN_VALUE);
});
}
@@ -757,7 +758,7 @@ public class NumberUtilTest {
@EnabledForJreRange(max = JRE.JAVA_8)
void numberFormatTest() {
// JDK8下,NaN解析报错,JDK9+中返回0
- Assertions.assertThrows(ParseException.class, ()->{
+ assertThrows(ParseException.class, ()->{
NumberFormat.getInstance().parse("NaN");
});
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java
index bceb0b955..a2692366c 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java
@@ -15,6 +15,7 @@ package org.dromara.hutool.json;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.ReaderWrapper;
import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.math.NumberUtil;
import java.io.IOException;
import java.io.InputStream;
@@ -171,27 +172,21 @@ public class JSONTokener extends ReaderWrapper {
}
/**
- * Get the last character read from the input or '\0' if nothing has been read yet.
+ * 获取上一个读取的字符,如果没有读取过则返回'\0'
*
- * @return the last character read from the input.
+ * @return 上一个读取的字符
*/
protected char getPrevious() {
return this.previous;
}
/**
- * 读取下一个字符,并比对是否和指定字符匹配
- *
- * @param c 被匹配的字符
- * @return The character 匹配到的字符
- * @throws JSONException 如果不匹配抛出此异常
+ * 获取16进制unicode转义符对应的字符值,如:
+ *
{@code '4f60' -> '你'}+ * @return 字符 */ - public char next(final char c) throws JSONException { - final char n = this.next(); - if (n != c) { - throw this.syntaxError("Expected '" + c + "' and instead saw '" + n + "'"); - } - return n; + public char nextUnicode(){ + return (char) NumberUtil.parseInt(next(4), 16); } /** @@ -201,12 +196,9 @@ public class JSONTokener extends ReaderWrapper { * @return 获得的n个字符组成的字符串 * @throws JSONException 如果源中余下的字符数不足以提供所需的字符数,抛出此异常 */ - public String next(final int n) throws JSONException { - if (n == 0) { - return ""; - } - + public char[] next(final int n) throws JSONException { final char[] chars = new char[n]; + int pos = 0; while (pos < n) { chars[pos] = this.next(); @@ -215,7 +207,7 @@ public class JSONTokener extends ReaderWrapper { } pos += 1; } - return new String(chars); + return chars; } /** @@ -276,7 +268,7 @@ public class JSONTokener extends ReaderWrapper { sb.append('\r'); break; case 'u':// Unicode符 - sb.append((char) Integer.parseInt(this.next(4), 16)); + sb.append(nextUnicode()); break; case '"': case '\'':