String-to-number parsing
Turning text into numbers safely: integer parsing, negative numbers, base conversion, overflow checks and tokenization. Real parsers must handle signs, bad characters and values outside the int range.
Learning goals
- Parse integers manually with correct sign handling
- Detect overflow while parsing
- Convert numbers between bases
- Tokenize input into numbers
Prerequisites
Integer parsing
Parsing an integer reads digits left to right, updating value = value * 10 + digit, where the digit is c - '0'. Reject empty input, a sign with no digits and any non-digit character. Integer.parseInt throws NumberFormatException for all of these, and for leading or trailing spaces.
Parse your own text in the animation and watch the value build up.
1int a = Integer.parseInt("1204"); // 12042int b = Integer.parseInt("+45"); // 45: a plus sign is accepted3int c = Integer.parseInt("007"); // 7: leading zeros are fine4// Integer.parseInt(" 7") and Integer.parseInt("") throw NumberFormatException
Parsing an int by hand (sign, digits, overflow)
Interactive · edit the inputchar[] s · length 5
Read an optional sign, then digits left to right: value = value * 10 + digit. Accumulate in a long so overflow can be detected.
1static int parseInt(String s) {2int i = 0, sign = 1;3if (s.charAt(0) == '-' || s.charAt(0) == '+') { if (s.charAt(0) == '-') sign = -1; i = 1; }4if (i == s.length()) throw new NumberFormatException(s); // a sign alone5long value = 0;6for (; i < s.length(); i++) {7int d = s.charAt(i) - '0';8if (d < 0 || d > 9) throw new NumberFormatException(s); // not a digit9value = value * 10 + d;10if (sign * value > Integer.MAX_VALUE || sign * value < Integer.MIN_VALUE) throw new NumberFormatException(s); // overflow11}12return (int) (sign * value);13}
Live state
i
0
sign
1
value
0
Execution trace (dry run table)Show 7 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | i=0 sign=1 value=0 | Read an optional sign, then digits left to right: value = value * 10 + digit. Accumulate in a long so overflow can be detected. |
| 2 | L3 | i=1 sign=-1 | '-' is a sign: sign = -1. Digits start at index 1. |
| 3 | L10 | i=1 digit=1 value=1 sign * value=-1 | value = previous * 10 + 1 = 1. |
| 4 | L10 | i=2 digit=2 value=12 sign * value=-12 | value = previous * 10 + 2 = 12. |
| 5 | L10 | i=3 digit=0 value=120 sign * value=-120 | value = previous * 10 + 0 = 120. |
| 6 | L10 | i=4 digit=4 value=1204 sign * value=-1204 | value = previous * 10 + 4 = 1204. |
| 7 | L12 | return=-1204 | Parsed -1204. Note the smallest int works because the magnitude 2147483648 is compared after applying the sign. |
Negative numbers
Handle an optional leading '-' (or '+') before the digits and apply the sign at the end: the sign is a separate variable, not part of the digit loop. The most negative int, -2147483648, has no positive counterpart, so a parser that parses the magnitude into an int fails on it; accumulate in a long or in negative form.
Try the smallest-int preset: the magnitude 2147483648 does not fit, but the signed value does.
1int min = Integer.parseInt("-2147483648"); // fine: Integer.MIN_VALUE2int abs = Math.abs(Integer.MIN_VALUE); // still -2147483648: no positive counterpart
Parsing an int by hand (sign, digits, overflow)
Interactive · edit the inputchar[] s · length 5
Read an optional sign, then digits left to right: value = value * 10 + digit. Accumulate in a long so overflow can be detected.
1static int parseInt(String s) {2int i = 0, sign = 1;3if (s.charAt(0) == '-' || s.charAt(0) == '+') { if (s.charAt(0) == '-') sign = -1; i = 1; }4if (i == s.length()) throw new NumberFormatException(s); // a sign alone5long value = 0;6for (; i < s.length(); i++) {7int d = s.charAt(i) - '0';8if (d < 0 || d > 9) throw new NumberFormatException(s); // not a digit9value = value * 10 + d;10if (sign * value > Integer.MAX_VALUE || sign * value < Integer.MIN_VALUE) throw new NumberFormatException(s); // overflow11}12return (int) (sign * value);13}
Live state
i
0
sign
1
value
0
Execution trace (dry run table)Show 7 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | i=0 sign=1 value=0 | Read an optional sign, then digits left to right: value = value * 10 + digit. Accumulate in a long so overflow can be detected. |
| 2 | L3 | i=1 sign=-1 | '-' is a sign: sign = -1. Digits start at index 1. |
| 3 | L10 | i=1 digit=1 value=1 sign * value=-1 | value = previous * 10 + 1 = 1. |
| 4 | L10 | i=2 digit=2 value=12 sign * value=-12 | value = previous * 10 + 2 = 12. |
| 5 | L10 | i=3 digit=0 value=120 sign * value=-120 | value = previous * 10 + 0 = 120. |
| 6 | L10 | i=4 digit=4 value=1204 sign * value=-1204 | value = previous * 10 + 4 = 1204. |
| 7 | L12 | return=-1204 | Parsed -1204. Note the smallest int works because the magnitude 2147483648 is compared after applying the sign. |
Base conversion
To write n in base b, repeatedly take the remainder n % b as the next digit (least significant first) and divide by b until n is 0, then reverse the digits. To read a string in base b use value = value * b + digit. Java provides Integer.toString(n, radix), Integer.toBinaryString, toHexString and Integer.parseInt(s, radix).
Convert a number in the animation; the digits come out in reverse order.
1static String toBase(int n, int base) {2if (n == 0) return "0";3StringBuilder sb = new StringBuilder();4while (n > 0) { sb.append(Character.forDigit(n % base, base)); n /= base; }5return sb.reverse().toString();6}7String bin = Integer.toBinaryString(156); // "10011100"8int hex = Integer.parseInt("ff", 16); // 255
Converting a number to another base
Interactive · edit the inputdigits so far (least significant first)
Repeatedly divide by 2: the remainders are the digits, starting from the least significant.
1static String toBase(int n, int base) { // n >= 02if (n == 0) return "0";3StringBuilder sb = new StringBuilder();4while (n > 0) {5int digit = n % base;6sb.append(Character.forDigit(digit, base));7n /= base;8}9return sb.reverse().toString(); // digits come out least significant first10}
Live state
n
156
base
2
Execution trace (dry run table)Show 10 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | n=156 base=2 | Repeatedly divide by 2: the remainders are the digits, starting from the least significant. |
| 2 | L6 | n=156 n % base=0 digit=0 | 156 % 2 = 0; 156 / 2 = 78. |
| 3 | L6 | n=78 n % base=0 digit=0 | 78 % 2 = 0; 78 / 2 = 39. |
| 4 | L6 | n=39 n % base=1 digit=1 | 39 % 2 = 1; 39 / 2 = 19. |
| 5 | L6 | n=19 n % base=1 digit=1 | 19 % 2 = 1; 19 / 2 = 9. |
| 6 | L6 | n=9 n % base=1 digit=1 | 9 % 2 = 1; 9 / 2 = 4. |
| 7 | L6 | n=4 n % base=0 digit=0 | 4 % 2 = 0; 4 / 2 = 2. |
| 8 | L6 | n=2 n % base=0 digit=0 | 2 % 2 = 0; 2 / 2 = 1. |
| 9 | L6 | n=1 n % base=1 digit=1 | 1 % 2 = 1; 1 / 2 = 0. |
| 10 | L9 | return="10011100" | Reverse the collected digits: 156 in base 2 is "10011100". Integer.toString(156, 2) does the same. |
Overflow checks
Multiplying by 10 can overflow an int silently. Check before or after each step: accumulate in a long and compare with Integer.MAX_VALUE and MIN_VALUE, or test value > (Integer.MAX_VALUE - digit) / 10 before updating. Math.multiplyExact and Math.addExact throw ArithmeticException on overflow.
Parse 2147483648 in the animation: the check fires on the last digit.
1static int parsePositive(String s) {2int value = 0;3for (char c : s.toCharArray()) {4int d = c - '0';5if (value > (Integer.MAX_VALUE - d) / 10) throw new ArithmeticException("overflow");6value = value * 10 + d;7}8return value;9}
Parsing an int by hand (sign, digits, overflow)
Interactive · edit the inputchar[] s · length 5
Read an optional sign, then digits left to right: value = value * 10 + digit. Accumulate in a long so overflow can be detected.
1static int parseInt(String s) {2int i = 0, sign = 1;3if (s.charAt(0) == '-' || s.charAt(0) == '+') { if (s.charAt(0) == '-') sign = -1; i = 1; }4if (i == s.length()) throw new NumberFormatException(s); // a sign alone5long value = 0;6for (; i < s.length(); i++) {7int d = s.charAt(i) - '0';8if (d < 0 || d > 9) throw new NumberFormatException(s); // not a digit9value = value * 10 + d;10if (sign * value > Integer.MAX_VALUE || sign * value < Integer.MIN_VALUE) throw new NumberFormatException(s); // overflow11}12return (int) (sign * value);13}
Live state
i
0
sign
1
value
0
Execution trace (dry run table)Show 7 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | i=0 sign=1 value=0 | Read an optional sign, then digits left to right: value = value * 10 + digit. Accumulate in a long so overflow can be detected. |
| 2 | L3 | i=1 sign=-1 | '-' is a sign: sign = -1. Digits start at index 1. |
| 3 | L10 | i=1 digit=1 value=1 sign * value=-1 | value = previous * 10 + 1 = 1. |
| 4 | L10 | i=2 digit=2 value=12 sign * value=-12 | value = previous * 10 + 2 = 12. |
| 5 | L10 | i=3 digit=0 value=120 sign * value=-120 | value = previous * 10 + 0 = 120. |
| 6 | L10 | i=4 digit=4 value=1204 sign * value=-1204 | value = previous * 10 + 4 = 1204. |
| 7 | L12 | return=-1204 | Parsed -1204. Note the smallest int works because the magnitude 2147483648 is compared after applying the sign. |
Tokenization
Tokenization splits input into meaningful pieces. For numbers separated by whitespace use split("\\s+") (after trim()), StringTokenizer, or Scanner. Handle empty tokens: a leading delimiter yields an empty first token, and consecutive delimiters give empty tokens unless the regex absorbs runs.
Try the whitespace-run preset.
1String line = " 12 -7 30 ";2String[] parts = line.trim().split("\\s+"); // [12, -7, 30]3int[] nums = Arrays.stream(parts).mapToInt(Integer::parseInt).toArray();4long sum = Arrays.stream(nums).asLongStream().sum(); // 35
split: how a string becomes tokens
Interactive · edit the inputchar[] s · length 8
tokens
Every comma ends a token. Two commas in a row make an empty token.
1static List<String> split(String s, char delim) {2List<String> out = new ArrayList<>();3StringBuilder cur = new StringBuilder();4for (char c : s.toCharArray()) {5if (c == delim) { out.add(cur.toString()); cur.setLength(0); } // delimiter: close the token6else cur.append(c);7}8out.add(cur.toString()); // the last token, possibly empty9return out; // String.split(regex) then drops trailing empty tokens10}
Live state
Execution trace (dry run table)Show 11 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | Every comma ends a token. Two commas in a row make an empty token. | |
| 2 | L6 | i=0 current="a" | Append 'a' to the current token. |
| 3 | L5 | i=1 token="a" | Delimiter: close the token "a". |
| 4 | L6 | i=2 current="b" | Append 'b' to the current token. |
| 5 | L5 | i=3 token="b" | Delimiter: close the token "b". |
| 6 | L5 | i=4 token="" | Delimiter with nothing before it: an empty token is added. |
| 7 | L6 | i=5 current="c" | Append 'c' to the current token. |
| 8 | L5 | i=6 token="c" | Delimiter: close the token "c". |
| 9 | L5 | i=7 token="" | Delimiter with nothing before it: an empty token is added. |
| 10 | L8 | token="" | End of input: add the last token "" (empty). |
| 11 | L9 | return=["a", "b", "", "c"] length=4 | Java's split removes trailing empty tokens (but keeps leading and inner ones): 4 tokens. |
Practice ladder
Now try it yourself
Easy
- Parse "-1204" by hand with value = value * 10 + digit
- Convert 255 to hexadecimal and binary
Medium
- Why does parsing the magnitude of -2147483648 into an int fail?
- Write parsePositive that rejects overflow
Advanced
- Implement atoi with whitespace, sign and clamping on overflow
Key takeaways
- Digits are c - '0'; accumulate value * 10 + digit and apply the sign separately.
- Accumulate in long or check before multiplying to catch overflow.
- Base conversion uses repeated remainders; split with a regex handles whitespace runs.