Back to Strings
Q30Question 30 of 61

Check if a String Contains Only Digits

Key Concepts
  • Loop through every character and call Character.isDigit(), returning false on the first non-digit for fail-fast behavior.
  • An empty or null string is not a valid numeric input by banking convention, so guard that case up front.
  • Character.isDigit() is Unicode-aware and accepts non-ASCII digits like Devanagari numerals, which may surprise downstream parsers.
  • For strict ASCII-only validation, compare each character against the range '0' to '9' instead of using Character.isDigit().
  • A string of all digits does not guarantee it fits in an int or long, so parse into BigDecimal for monetary amounts.
  • Regex alternative like str.matches("[0-9]+") works but is slower on hot paths due to pattern compilation overhead.

Definition

Walk every character, fail fast on the first non-digit, and only pass if the whole string is digits and non-empty.
  • Character.isDigit() checks whether a single character is a Unicode decimal digit, returning true for 0 through 9 and also for digits in other scripts.
  • Fail-fast loop iterates over every character and returns false the moment a non-digit is found, avoiding unnecessary scanning.
  • Empty-string guard rejects null and empty input up front, because an empty field is not a valid numeric entry.
  • ASCII-only variant compares each character against the range '0' to '9' to exclude non-ASCII digit characters.
  • BigDecimal follow-up is needed after validation because all-digit strings can exceed int and long ranges for large monetary amounts.

When Priya types her account number into the National Bank of India app, the system walks through every character she entered. The moment it sees a letter, a space, or any symbol, it stops and says the field is not valid. Only when every single character is a digit does it accept the input and proceed to the next step.

Understanding the Concepts

What is the digit-only check?

The digit-only check verifies that every character in a string is a decimal digit from zero through nine, with no letters, spaces, punctuation, or symbols anywhere. In a banking application, this validation runs on account numbers, PINs, OTPs, and monetary amount fields before they reach any parser or database query.

JAVA
package com.bank.validation;

public class DigitChecker {
    public static boolean containsOnlyDigits(String input) {
        if (input == null || input.isEmpty()) {
            return false;
        }
        for (int i = 0; i < input.length(); i++) {
            if (!Character.isDigit(input.charAt(i))) {
                return false;
            }
        }
        return true;
    }
}

Why does this matter in banking?

Account numbers, IFSC codes, and transaction amounts are all expected to be numeric or follow strict patterns. If a stray space or letter slips through, a database lookup fails silently or a transfer routes to the wrong account. Rajesh at the National Bank of India validates every numeric field at the entry point so downstream services never receive malformed data.

JAVA
package com.bank.service;

import com.bank.validation.DigitChecker;
import java.math.BigDecimal;

public class DepositService {
    public BigDecimal parseAmount(String raw) {
        if (!DigitChecker.containsOnlyDigits(raw)) {
            throw new IllegalArgumentException(
                "Amount must contain only digits: " + raw);
        }
        return new BigDecimal(raw);
    }
}

How does the fail-fast loop work internally?

The loop starts at index zero and advances one character at a time. The moment Character.isDigit returns false, the method returns false immediately without scanning the rest of the string. This fail-fast behavior means that for invalid input, the check stops at the first bad character, which is efficient when most rejections happen early.

JAVA
package com.bank.validation;

public class FailFastDemo {
    public static void main(String[] args) {
        String accountNumber = "2001X456";
        for (int i = 0; i < accountNumber.length(); i++) {
            char c = accountNumber.charAt(i);
            if (!Character.isDigit(c)) {
                System.out.println("Non-digit found at index " + i + ": '" + c + "'");
                break;
            }
        }
    }
}
TEXT
Non-digit found at index 4: 'X'

What are the edge cases?

The most common edge case is the empty string. A naive loop finishes immediately and returns true, but an empty field is not valid numeric input. Null input would throw a NullPointerException if not guarded. A string with leading or trailing spaces like " 123 " also fails because spaces are not digits.

JAVA
package com.bank.validation;

public class EdgeCaseDemo {
    public static void main(String[] args) {
        System.out.println(DigitChecker.containsOnlyDigits(null));     // false
        System.out.println(DigitChecker.containsOnlyDigits(""));       // false
        System.out.println(DigitChecker.containsOnlyDigits(" 123 ")); // false
        System.out.println(DigitChecker.containsOnlyDigits("00000")); // true
        System.out.println(DigitChecker.containsOnlyDigits("9"));     // true
    }
}
TEXT
false
false
false
true
true

What is the Unicode trap with Character.isDigit()?

Character.isDigit() is Unicode-aware. It returns true for digit characters in many scripts, not just ASCII zero through nine. For example, Devanagari digits (used in Hindi) and Arabic-Indic digits all pass the check. This means a field validated with Character.isDigit() might accept characters that Integer.parseInt() or BigDecimal cannot parse, causing a downstream NumberFormatException.

JAVA
package com.bank.validation;

public class UnicodeDigitDemo {
    public static void main(String[] args) {
        char devanagariOne = '\u0967'; // Devanagari digit 1
        System.out.println("isDigit: " + Character.isDigit(devanagariOne)); // true

        // But parseInt cannot handle it
        try {
            Integer.parseInt(String.valueOf(devanagariOne));
        } catch (NumberFormatException e) {
            System.out.println("parseInt failed: " + e.getMessage());
        }
    }
}
TEXT
isDigit: true
parseInt failed: For input string: "\u0967"

How do you enforce strict ASCII-only digits?

For banking fields that must contain only ASCII digits, compare each character against the range from '0' to '9' instead of calling Character.isDigit(). This rejects all non-ASCII digit characters while keeping the same fail-fast loop structure.

JAVA
package com.bank.validation;

public class AsciiDigitChecker {
    public static boolean isAsciiDigitsOnly(String input) {
        if (input == null || input.isEmpty()) {
            return false;
        }
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (c < '0' || c > '9') {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        System.out.println(isAsciiDigitsOnly("200145"));  // true
        System.out.println(isAsciiDigitsOnly("\u0967")); // false
    }
}
TEXT
true
false

How does the regex alternative compare?

A regular expression like str.matches("[0-9]+") checks the same condition in one line. However, String.matches() compiles the pattern on every call, which adds overhead on hot paths. For infrequent validation, regex is fine. For high-volume batch processing, pre-compile the pattern or use the explicit loop.

JAVA
package com.bank.validation;

import java.util.regex.Pattern;

public class RegexDigitChecker {
    private static final Pattern DIGITS_ONLY = Pattern.compile("[0-9]+");

    public static boolean check(String input) {
        if (input == null || input.isEmpty()) {
            return false;
        }
        return DIGITS_ONLY.matcher(input).matches();
    }

    public static void main(String[] args) {
        System.out.println(check("200145"));  // true
        System.out.println(check("20 01"));   // false
    }
}
TEXT
true
false

What are common mistakes?

The most common mistake is forgetting the empty-string guard. A second mistake is using Character.isDigit() when strict ASCII is required, which silently accepts non-ASCII digits. A third mistake is assuming that all-digit means parseable into an int or long, when the value might be a 20-digit account number that overflows.

JAVA
package com.bank.validation;

public class CommonMistakesDemo {
    // WRONG: no null/empty guard, uses isDigit for ASCII field
    public static boolean wrongCheck(String s) {
        for (int i = 0; i < s.length(); i++) {
            if (!Character.isDigit(s.charAt(i))) return false;
        }
        return true; // empty string returns true incorrectly
    }

    // CORRECT: guards null/empty, uses ASCII range
    public static boolean correctCheck(String s) {
        if (s == null || s.isEmpty()) return false;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c < '0' || c > '9') return false;
        }
        return true;
    }

    public static void main(String[] args) {
        System.out.println("Wrong on empty: " + wrongCheck(""));       // true (bug)
        System.out.println("Correct on empty: " + correctCheck("")); // false
    }
}
TEXT
Wrong on empty: true
Correct on empty: false

What is the time and space complexity?

The loop runs in O(n) time where n is the length of the string. It uses O(1) extra space because no additional data structures are created. The regex approach also runs in O(n) time but has higher constant overhead from pattern matching. For batch validation of millions of account numbers, the loop approach is measurably faster.

JAVA
package com.bank.validation;

public class PerformanceDemo {
    public static void main(String[] args) {
        String largeNumber = "1".repeat(1_000_000);
        long start = System.nanoTime();
        boolean result = AsciiDigitChecker.isAsciiDigitsOnly(largeNumber);
        long elapsed = System.nanoTime() - start;
        System.out.println("Result: " + result + ", Time: " + elapsed / 1_000 + " microseconds");
    }
}

What are common interview follow-up questions?

  • How would you validate a decimal number like "123.45" that contains a dot?
  • How would you validate a negative number with a leading minus sign?
  • What is the difference between Character.isDigit() and Character.isLetterOrDigit()?
  • How would you validate that the string represents a number within a specific range?

Visual Overview

DIAGRAM
  Input: "200145"
       │
       ▼
  ┌──────────────────┐
  │  Null or empty?   │──── yes ──→ return false
  └───────┬──────────┘
          │ no
          ▼
  ┌──────────────────┐
  │  i = 0           │
  │  char = '2'      │──── is digit? yes ──→ next
  │  char = '0'      │──── is digit? yes ──→ next
  │  char = '0'      │──── is digit? yes ──→ next
  │  char = '1'      │──── is digit? yes ──→ next
  │  char = '4'      │──── is digit? yes ──→ next
  │  char = '5'      │──── is digit? yes ──→ done
  └───────┬──────────┘
          │
          ▼
  ┌──────────────────┐
  │  return true      │
  └──────────────────┘

  Input: "20X1"
       │
       ▼
  char = '2' ── digit ── next
  char = '0' ── digit ── next
  char = 'X' ── NOT digit ── return false (fail fast)

Key Takeaway

Checking whether a string contains only digits is one of the most common validations in any banking application. Rajesh at the National Bank of India uses this on account numbers, PINs, OTPs, and deposit amounts to ensure no stray letters or spaces slip into a numeric field. The approach is a simple loop that walks every character and calls a digit check, returning false the instant it finds a non-digit character.

The edge case that trips up beginners is the empty string. A bare loop returns true for an empty string because there is nothing to reject, but an empty field is not valid numeric input. Guarding null and empty up front prevents this. The senior nuance is that Character.isDigit() is Unicode-aware and accepts digit characters from Devanagari, Arabic-Indic, and other scripts, not just ASCII zero through nine. For a banking field that feeds into Integer.parseInt() or BigDecimal, those non-ASCII digits cause a NumberFormatException downstream.

When strict ASCII validation is required, compare each character against the range from '0' to '9' instead of calling Character.isDigit(). This rejects all non-ASCII numerals while keeping the same fail-fast structure. For high-volume batch processing of account numbers, the explicit loop with the ASCII range check is measurably faster than a regex alternative, though for infrequent checks a pre-compiled regex pattern works fine.

One more point that separates a thoughtful answer from a naive one is recognizing that all-digit does not mean parseable into a number. A 20-digit account number overflows both int and long, so after validating digits you should parse into BigDecimal for monetary amounts. Priya's deposit of ten million rupees has more digits than a long can handle gracefully, and BigDecimal is the safe choice.

Remember one sentence: loop every character, fail fast on the first non-digit, guard null and empty, and use the ASCII range check when Unicode digits could surprise downstream parsers.

---

Share
Use ← → arrow keys to navigate