2

I was trying to write a java program where every digit of an input integer is printed in words.

For example: input 123 should produce an output "one two three".

I wrote the following program that takes an integer value, then converts it into a string. I then iterated over the characters of the string and converted them to integer values, which I later used as indices for the array.

But I'm getting ArrayIndexOutOfBoundsException.

Index 49 out of bounds for length 10

My code:

public class DigitsAsWords {
    static void Print_Digits(int N){
        String arr[] = {"zero","one", "two", "three", "four","five", "six", "seven", "eight", "nine"};
        String st = Integer.toString(N);
        System.out.println(st);
        char s;
        int a;
        for (int i=0; i<st.length(); i++){
            s = st.charAt(i);
            a = Integer.valueOf(s);
            System.out.print(arr[a]+" ");
        }
    }
    public static void main (String args[]){
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();
        Print_Digits(a);
    }
}
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • 1
    Print `a` inside of your `for` loop to look at what `Integer.valueOf(s)` returns. Is this a valid index for `arr`? For further reference, also have a look at the [ASCII Table](https://www.asciitable.com/). – maloomeister Jun 29 '22 at 08:20
  • 1
    you're getting the ASCII value... – aran Jun 29 '22 at 08:22

5 Answers5

6

This is the place your code is failing at:

a = Integer.valueOf(s);

Instead of converting '1' to 1 as you were expecting, it converts '1' into the ascii equivalent, 49.

To avoid this:

a = Character.getNumericValue(s);

This will convert '1' to 1, and so on.

Aniketh Malyala
  • 2,650
  • 1
  • 5
  • 14
  • Oh thank you. I tried it and it worked. I tried Integer.valueOf("1234") but it returned the whole value 1234 as integer instead of ASCII values so I thought it will work in the above problem too. – rat_basterd Jun 29 '22 at 08:24
  • 1
    @rat_basterd the reason that occurred is because "1234" is a String, while '1' is a character, so Integer.valueOf("1") would be 1, but Integer.valueOf('1') would be 49 – Aniketh Malyala Jun 29 '22 at 08:33
  • 1
    It’s also worth noting that `Integer.valueOf('1')` doesn’t call `valueOf(char)` as no such method exists, but rather `valueOf(int)` which is an entirely obsolete call. It effectively does the same as if you just wrote `a = s;` – Holger Jun 29 '22 at 12:39
3

Here's the problem:

There are two Integer.valueOf() methods:

  1. Integer.valueOf(int)

  2. Integer.valueOf(String)

When you call valueOf on a character such as '5', the Integer.valueOf(int) is called. The '5' is casted to int which is 53 which is the ascii code for the character '5'.

Your code:

for (int i=0; i<st.length(); i++){
  s = st.charAt(i);
  a = Integer.valueOf(s);  <=== a is the ascii code
  System.out.print(arr[a]+" ");  <==will cause index out of bound.
}

Possible fixes:

  1. compute the digit of the character (preferred). For example: '5' - '0' = 5.
  2. convert the character to a string.
for (int i=0; i<st.length(); i++){
  s = st.charAt(i);
  a = Integer.valueOf(s-'0'); 
  System.out.print(arr[a]+" ");  
}
Cheng Thao
  • 1,467
  • 1
  • 3
  • 9
  • wow i didnt even know that subtracting '0' changed it to a digit! – Aniketh Malyala Jun 29 '22 at 08:35
  • Have a look at the Javadoc and see what method `Integer.valueOf()` does, there's no need to call it: `a = s - '0'` is sufficient. And as I've said it's a bad practice to use hard-coded values. – Alexander Ivanchenko Jun 29 '22 at 14:51
  • 1
    @AlexanderIvanchenko I’d agree if the answerer wrote `s - 48`, but using the constant `'0'` does document the origin of the value. Granted, it does require the reader to know that the values `'0'`,`'1'`,`'2'`,`'3'`,`'4'`,`'5'`,`'6'`,`'7'`,`'8'`,`'9'` are consecutive in the charset, but I don’t know of any charset where this doesn’t apply, so this code would even work if Java wasn’t fixed to Unicode anyway. Or, to look at it from the other side, how would you name a constant representing `'0'` to carry more meaning than already visible when seeing `'0'`? – Holger Jun 30 '22 at 09:19
  • @Holger `s - 48` is a clear case of magic numbers, agree. Maybe I'm exaggerating, but `s - '0'` vs `Character.getNumericValue(s)` resembles `"" + something` vs `String.valueOf(someting)`. In both cases, the first part is shorter and doesn't require much mental energy to read it, but both latter parts are more expressive. – Alexander Ivanchenko Jun 30 '22 at 11:00
  • 1
    @AlexanderIvanchenko `Character.getNumericValue(s)` is preferable but the reason is not that `'0'` is a “magic number” – Holger Jun 30 '22 at 11:07
  • @Holger OK, I've got the point, removed the comment about "magic numbers". – Alexander Ivanchenko Jun 30 '22 at 11:13
1

Change:

a = Integer.valueOf(s);

To:

a = s - '0';

Subtracting '0' returns the int value of a digit character.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
1

you are getting an error here

 a = Integer.valueOf(s);

it converts char to their respective Unicode value. Instead of this code, you can use the below function.

Character.getNumericValue();

Refer this link -> Char to Int

You can also do

String c=String.valueOf(s);  
int foo = Integer.parseInt(c);

Refer -> How do I convert a String to an int in Java?

0

Firstly, lean about the primitive data types in Java.

If your background is JavaScript, which has no char type, you might have been thinking that char is a "kind of" string, but it's not.

char - is a 16-bit numeric primitive data type, which has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535) which was meant to represent Unicode-characters. But in the earlier days of Java the Unicode standard has been changed, and char type appeared to be broken, to fix this code points were introduced in the language.

In this statement a = Integer.valueOf(s); character s is being promoted into int type and method valueOf() creates an unnecessary instance of Integer which is being immediately thrown away, and you end up with a having a value in the range from 48 (0) to 57 (9), which is greater than array's length. Therefore, you're getting a justifiable ArrayIndexOutOfBoundsException.

Also, there are few more issues.

Compiler will not complain that you're creating a new array for each method call but this approach is wrong, the string array should be static, i.e. it should reside on a class level:

public static final String[] DIGITS = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};

Another important issue which might seem unimportant is that the names you're using both uninformative (like arr) and they violate the Java naming convention (like Print_Digits).

Also, C-style of array declarationString arr[] is not considered to be a good practice in Java because it mixes the data type and the variable name.

And here is a couple of solutions for this problem.

The first one is somewhat close to code provided by OP:

public static void printDigits1(int num) {
    String numericString = String.valueOf(num);
    
    for (int i = 0; i < numericString.length(); i++) {
        String digit = DIGITS[Character.getNumericValue(numericString.charAt(i))];
        System.out.print(digit + " ");
    }
}

We can also address this problem without using strings at all:

public static void printDigits2(int num) {
    if (num == 0) {
        System.out.println(DIGITS[0]);
        return;
    }
    
    int len = (int) Math.ceil(Math.log10(num));
    String[] result = new String[len];

    for (int cur = num, i = len - 1; cur > 0; cur /= 10, i--) {
        result[i] = DIGITS[cur % 10];
    }

    System.out.println(String.join(" ", result));
}

And here's a concise solution using Stream API:

public static void printDigits3(int num) {
    
    String result = String.valueOf(num).chars()
        .map(Character::getNumericValue)
        .mapToObj(i -> DIGITS[i])
        .collect(Collectors.joining(" "));

    System.out.println(result);
}

main()

public static void main(String[] args) {
    printDigits1(59);
    System.out.println();
    printDigits2(128);
    printDigits3(789);
}

Output:

five nine 
one two eight
seven eight nine

A link to Online Demo

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46