views:

76

answers:

5

Hi! I need function that will take string and integer that indicates position of non-negative double or integer and return Number or null. If there is '+' return null.

Examples

2.1      , 0 -> 2.1
+2.1     , 0 -> null
-1       , 0 -> null
-1.2     , 1 -> 1.2
qwa56sdf , 3 -> 56

What is the most elegant way to do this? Thanks.

upd I need code like this, but better)

    Number parse(String str, int pos){
        Matcher m = Pattern.compile("^(\\d+(?:\\.\\d+)?)").matcher(str);
        m.region(pos, str.length());
        if(m.find()){
            return Double.parseDouble(m.group());
        } else {
            return null;
        }
    }
+3  A: 

You could try:

public static void main(String[] args) {
  System.out.println(parse("2.1", 0));
  System.out.println(parse("+2.1", 0));
  System.out.println(parse("-1", 0));
  System.out.println(parse("-1.2", 1));
  System.out.println(parse("qwa56sdf", 3));
}

private static Double parse(String string, int index) {
  if (string.charAt(index) == '-' || string.charAt(index) == '+') {
    return null;
  }
  try {
    return Double.parseDouble(string.substring(index).replaceAll("[^\\d.]", ""));
  }
  catch (NumberFormatException e) {
    return null;
  }
}

I had to strip the trailing non-digit characters with replace all because they caused a NumberFormatException and would return null for inputs like the one in your last example.

Edit: Your other option to work for cases like the one in the comment might be to check each character

private static Double parse(String string, int index) {
    String finalString = "";
    boolean foundSeparator = false;
    for (char c : string.substring(index).toCharArray()) {
        if (c == '.') {
            if (foundSeparator) {
                break;
            }
            else {
                foundSeparator = true;
            }
        }
        else if (!Character.isDigit(c)) {
            break;
        }
        finalString += c;
    }
    if (finalString == "") {
        return null;
    }
    return Double.parseDouble(finalString);
}
Andrei Fierbinteanu
Thanks, but there is trouble with inputs like parse("qwe5a6asf", 3)
Stas
I'm guessing that in this input you only want to match '5', that being the first valid number in that string. The updated version should work for this kind of case as well, I think.
Andrei Fierbinteanu
Yes, I need something like in second variant. But I suppose that there must be better solution with NumberFormat or something like this.
Stas
+2  A: 

You'll need to use a combination of the String.substring() method to start at the indicated position in the string and the NumberFormat class to parse the number.

Bernard
Thanks. Actually, NumberFormat has method parse with indexPosition, but there is problem with chars like '+' and '-'.
Stas
+1  A: 

If that is functionally correct, it looks elegant enough to me. You might want to make the Pattern a final class member since you only need to compile it once. And the region's probably not needed:

Matcher m = pattern.matcher(str.substring(pos));

Another option is to start with a 1-char-length substring and grow it until it doesn't parse anymore:

if ( str.charAt(pos) == '+' || str.charAt(pos) == '-' ) {
    //special cases
    return null;
}
Double val = null;
for ( int i = pos+1; i <= str.length(); i++ ) {
    try {
       val = Double.parseDouble(str.substring(pos, i)) {
    }  catch (NumberFormatException e) {
       break;
    }
}
return val;

It's a bit simpler but also naive performance-wise. A less readable but more performant solution would be to find the end of the double yourself before passing off to parse just by looking at the characters one at a time.

Mark Peters
Thanks, it's will be work, but I think there must be better solution.
Stas
+1  A: 

There is also the Scanner class. Which specifically has methods for reading in primitives, for example scanner.hasNextDouble() and scanner.nextDouble(). You'll still have to do the check for + or -, because that would still pass the check.

Russell Leggett
Thanks, but for scanner I must specified delimiter, but I can't.
Stas
Sure you can, anything thats not a digit or a dot. "[^0-9.]"
Russell Leggett
Yes, I can use delimiter, but I want parse from text like "1.2.3" result "1.2", not null.
Stas
+1  A: 
public static Double parseDouble(String input, int fromIndex) {
    Matcher matcher = Pattern.compile("\\d*\\.?\\d+")
        .matcher(input.substring(fromIndex));
    return matcher.lookingAt() ? Double.parseDouble(matcher.group()) : null;
}
Marimuthu Madasamy
Thanks. It's good solution but there are many programmers that wouldn't understand it.
Stas