封装Integer.parseInt()的好方法

问题描述 投票:80回答:23

我有一个项目,我们经常使用Integer.parseInt()将String转换为int。当出现问题时(例如,String不是数字,而是字母a,或其他什么),此方法将抛出异常。但是,如果我必须在我的代码中处理各种异常,那么这很快就会变得非常丑陋。我想把它放在一个方法中,但是,我不知道如何返回一个干净的值来表明转换出错了。

在C ++中,我可以创建一个接受指向int的指针的方法,让方法本身返回true或false。但是,据我所知,这在Java中是不可能的。我还可以创建一个包含true / false变量和转换值的对象,但这似乎也不理想。对于全局值也是如此,这可能会给我带来一些多线程的麻烦。

那么有一个干净的方法吗?

java exception-handling encapsulation
23个回答
128
投票

你可以返回一个Integer而不是int,在解析失败时返回null

遗憾的是,如果没有内部抛出的异常,Java没有提供这样做的方法 - 您可以隐藏异常(通过捕获并返回null),但如果您解析数百个,它仍然可能是性能问题数千位用户提供的数据。

编辑:这种方法的代码:

public static Integer tryParse(String text) {
  try {
    return Integer.parseInt(text);
  } catch (NumberFormatException e) {
    return null;
  }
}

请注意,如果text为null,我不确定这会做什么。您应该考虑 - 如果它代表一个错误(即您的代码可能传递无效值,但永远不应传递null),那么抛出异常是合适的;如果它不代表一个bug,那么你应该像对任何其他无效值一样返回null。

最初这个答案使用了new Integer(String)构造函数;它现在使用Integer.parseInt和拳击手术;以这种方式,小值将最终被装箱到缓存的Integer对象,使其在这些情况下更有效。


1
投票

Jon Skeet给出的答案很好,但我不喜欢回馈一个null整数对象。我发现这令人困惑。从Java 8开始,有一个更好的选择(在我看来),使用Integer

null

这使得您必须处理没有可用值的情况。我希望将来可以将这种函数添加到java库中,但我不知道是否会发生这种情况。


1
投票

请问parseInt方法怎么样?

这很简单,只需将内容复制粘贴到一个返回OptionalIntpublic static OptionalInt tryParse(String value) { try { return OptionalInt.of(Integer.parseInt(value)); } catch (NumberFormatException e) { return OptionalInt.empty(); } } 的新实用程序中,并用返回值替换throws。似乎底层代码中没有例外,但更好的检查。

通过跳过整个异常处理的东西,您可以节省一些无效输入的时间。从JDK 1.0开始,这个方法就在那里,所以你不可能做很多工作来保持它的最新状态。


1
投票

如果您使用的是Java 8或更高版本,则可以使用我刚刚发布的库:Integer。它支持int,long和boolean,它不依赖于捕获异常。与Guava的Ints.tryParse不同,它返回OptionalInt / OptionalLong / Optional,就像在Optional<Integer>中一样,但效率更高。


0
投票

我建议你考虑像这样的方法

https://github.com/robtimus/try-parse

然后你可以按照自己的意愿实施。如果你想要结果 - 也许是因为你使用Integer.parseInt() - 你可以使用数组技巧。

https://stackoverflow.com/a/38451745/1180351

将result [0]设置为进程中找到的整数值。


0
投票

这有点类似于Nikolay的解决方案:

 IntegerUtilities.isValidInteger(String s)

主要方法演示代码。实现Parser接口的另一种方法显然是从构造中设置“\ D +”,并且方法不执行任何操作。


0
投票

你可以自己动手,但它也很容易使用commons lang的 IntegerUtilities.isValidInteger(String s, int[] result) private static class Box<T> { T me; public Box() {} public T get() { return me; } public void set(T fromParse) { me = fromParse; } } private interface Parser<T> { public void setExclusion(String regex); public boolean isExcluded(String s); public T parse(String s); } public static <T> boolean parser(Box<T> ref, Parser<T> p, String toParse) { if (!p.isExcluded(toParse)) { ref.set(p.parse(toParse)); return true; } else return false; } public static void main(String args[]) { Box<Integer> a = new Box<Integer>(); Parser<Integer> intParser = new Parser<Integer>() { String myExclusion; public void setExclusion(String regex) { myExclusion = regex; } public boolean isExcluded(String s) { return s.matches(myExclusion); } public Integer parse(String s) { return new Integer(s); } }; intParser.setExclusion("\\D+"); if (parser(a,intParser,"123")) System.out.println(a.get()); if (!parser(a,intParser,"abc")) System.out.println("didn't parse "+a.get()); } 。它使用StringUtils.isNumeric()迭代String中的每个字符。


0
投票

他们以递归的方式处理这个问题。例如,从控制台读取数据时:

method

0
投票

为避免异常,您可以使用Java的Character.isDigit()方法。下面的代码基本上是Apache Common的Java.util.Scanner keyboard = new Java.util.Scanner(System.in); public int GetMyInt(){ int ret; System.out.print("Give me an Int: "); try{ ret = Integer.parseInt(keyboard.NextLine()); } catch(Exception e){ System.out.println("\nThere was an error try again.\n"); ret = GetMyInt(); } return ret; } 类的简化版本。

Format.parseObject

您可以根据自己的喜好使用IntegerValidatorpublic static boolean tryParse(String s, int[] result) { NumberFormat format = NumberFormat.getIntegerInstance(); ParsePosition position = new ParsePosition(0); Object parsedValue = format.parseObject(s, position); if (position.getErrorIndex() > -1) { return false; } if (position.getIndex() < s.length()) { return false; } result[0] = ((Long) parsedValue).intValue(); return true; } 阵列技巧。

这是我使用它的测试 -

AtomicInteger

0
投票

我也遇到了同样的问题。这是我写的一个方法,要求用户输入并且不接受输入,除非它是一个整数。请注意我是初学者,所以如果代码没有按预期工作,请归咎于我的经验不足!

int[]

0
投票

这是对问题8391979的回答,“java是否有一个int.tryparse,它不会为坏数据抛出异常?[duplicate]”这个被关闭并链接到这个问题。

编辑2016 08 17:添加了ltrimZeroes方法并在tryParse()中调用它们。在numberString中没有前导零可能会给出错误的结果(请参阅代码中的注释)。现在还有公共静态字符串ltrimZeroes(String numberString)方法,适用于正数和负数“数字”(END编辑)

下面你找到了一个基本的Wrapper(boxing)类,它有一个高速优化的tryParse()方法(类似于C#),它解析字符串本身,比Java中的Integer.parseInt(String s)快一点:

int[] i = new int[1];
Assert.assertTrue(IntUtils.tryParse("123", i));
Assert.assertEquals(123, i[0]);

IntBoxSimple类的测试/示例程序:

private int numberValue(String value, boolean val) throws IOException {
    //prints the value passed by the code implementer
    System.out.println(value);
    //returns 0 is val is passed as false
    Object num = 0;
    while (val) {
        num = br.readLine();
        try {
            Integer numVal = Integer.parseInt((String) num);
            if (numVal instanceof Integer) {
                val = false;
                num = numVal;
            }
        } catch (Exception e) {
            System.out.println("Error. Please input a valid number :-");
        }
    }
    return ((Integer) num).intValue();
}

34
投票

当它不是数字时,你期望什么行为?

例如,如果您在输入不是数字时经常使用默认值,那么这样的方法可能很有用:

public static int parseWithDefault(String number, int defaultVal) {
  try {
    return Integer.parseInt(number);
  } catch (NumberFormatException e) {
    return defaultVal;
  }
}

当无法解析输入时,可以为不同的默认行为编写类似的方法。


0
投票

尝试使用正则表达式和默认参数参数

public class IntBoxSimple {
    // IntBoxSimple - Rudimentary class to implement a C#-like tryParse() method for int
    // A full blown IntBox class implementation can be found in my Github project
    // Copyright (c) 2016, Peter Sulzer, Fürth
    // Program is published under the GNU General Public License (GPL) Version 1 or newer

    protected int _n; // this "boxes" the int value

    // BEGIN The following statements are only executed at the
    // first instantiation of an IntBox (i. e. only once) or
    // already compiled into the code at compile time:
    public static final int MAX_INT_LEN =
            String.valueOf(Integer.MAX_VALUE).length();
    public static final int MIN_INT_LEN =
            String.valueOf(Integer.MIN_VALUE).length();
    public static final int MAX_INT_LASTDEC =
            Integer.parseInt(String.valueOf(Integer.MAX_VALUE).substring(1));
    public static final int MAX_INT_FIRSTDIGIT =
            Integer.parseInt(String.valueOf(Integer.MAX_VALUE).substring(0, 1));
    public static final int MIN_INT_LASTDEC =
            -Integer.parseInt(String.valueOf(Integer.MIN_VALUE).substring(2));
    public static final int MIN_INT_FIRSTDIGIT =
            Integer.parseInt(String.valueOf(Integer.MIN_VALUE).substring(1,2));
    // END The following statements...

    // ltrimZeroes() methods added 2016 08 16 (are required by tryParse() methods)
    public static String ltrimZeroes(String s) {
        if (s.charAt(0) == '-')
            return ltrimZeroesNegative(s);
        else
            return ltrimZeroesPositive(s);
    }
    protected static String ltrimZeroesNegative(String s) {
        int i=1;
        for ( ; s.charAt(i) == '0'; i++);
        return ("-"+s.substring(i));
    }
    protected static String ltrimZeroesPositive(String s) {
        int i=0;
        for ( ; s.charAt(i) == '0'; i++);
        return (s.substring(i));
    }

    public static boolean tryParse(String s,IntBoxSimple intBox) {
        if (intBox == null)
            // intBoxSimple=new IntBoxSimple(); // This doesn't work, as
            // intBoxSimple itself is passed by value and cannot changed
            // for the caller. I. e. "out"-arguments of C# cannot be simulated in Java.
            return false; // so we simply return false
        s=s.trim(); // leading and trailing whitespace is allowed for String s
        int len=s.length();
        int rslt=0, d, dfirst=0, i, j;
        char c=s.charAt(0);
        if (c == '-') {
            if (len > MIN_INT_LEN) { // corrected (added) 2016 08 17
                s = ltrimZeroesNegative(s);
                len = s.length();
            }
            if (len >= MIN_INT_LEN) {
                c = s.charAt(1);
                if (!Character.isDigit(c))
                    return false;
                dfirst = c-'0';
                if (len > MIN_INT_LEN || dfirst > MIN_INT_FIRSTDIGIT)
                    return false;
            }
            for (i = len - 1, j = 1; i >= 2; --i, j *= 10) {
                c = s.charAt(i);
                if (!Character.isDigit(c))
                    return false;
                rslt -= (c-'0')*j;
            }
            if (len < MIN_INT_LEN) {
                c = s.charAt(i);
                if (!Character.isDigit(c))
                    return false;
                rslt -= (c-'0')*j;
            } else {
                if (dfirst >= MIN_INT_FIRSTDIGIT && rslt < MIN_INT_LASTDEC)
                    return false;
                rslt -= dfirst * j;
            }
        } else {
            if (len > MAX_INT_LEN) { // corrected (added) 2016 08 16
                s = ltrimZeroesPositive(s);
                len=s.length();
            }
            if (len >= MAX_INT_LEN) {
                c = s.charAt(0);
                if (!Character.isDigit(c))
                    return false;
                dfirst = c-'0';
                if (len > MAX_INT_LEN || dfirst > MAX_INT_FIRSTDIGIT)
                    return false;
            }
            for (i = len - 1, j = 1; i >= 1; --i, j *= 10) {
                c = s.charAt(i);
                if (!Character.isDigit(c))
                    return false;
                rslt += (c-'0')*j;
            }
            if (len < MAX_INT_LEN) {
                c = s.charAt(i);
                if (!Character.isDigit(c))
                    return false;
                rslt += (c-'0')*j;
            }
            if (dfirst >= MAX_INT_FIRSTDIGIT && rslt > MAX_INT_LASTDEC)
                return false;
            rslt += dfirst*j;
        }
        intBox._n=rslt;
        return true;
    }

    // Get the value stored in an IntBoxSimple:
    public int get_n() {
        return _n;
    }
    public int v() { // alternative shorter version, v for "value"
        return _n;
    }
    // Make objects of IntBoxSimple (needed as constructors are not public):
    public static IntBoxSimple makeIntBoxSimple() {
        return new IntBoxSimple();
    }
    public static IntBoxSimple makeIntBoxSimple(int integerNumber) {
        return new IntBoxSimple(integerNumber);
    }

    // constructors are not public(!=:
    protected IntBoxSimple() {} {
        _n=0; // default value an IntBoxSimple holds
    }
    protected IntBoxSimple(int integerNumber) {
        _n=integerNumber;
    }
}

如果您正在使用apache.commons.lang3然后使用import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class IntBoxSimpleTest { public static void main (String args[]) { IntBoxSimple ibs = IntBoxSimple.makeIntBoxSimple(); String in = null; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); do { System.out.printf( "Enter an integer number in the range %d to %d:%n", Integer.MIN_VALUE, Integer.MAX_VALUE); try { in = br.readLine(); } catch (IOException ex) {} } while(! IntBoxSimple.tryParse(in, ibs)); System.out.printf("The number you have entered was: %d%n", ibs.v()); } }

public static int parseIntWithDefault(String str, int defaultInt) {
    return str.matches("-?\\d+") ? Integer.parseInt(str) : defaultInt;
}


int testId = parseIntWithDefault("1001", 0);
System.out.print(testId); // 1001

int testId = parseIntWithDefault("test1001", 0);
System.out.print(testId); // 1001

int testId = parseIntWithDefault("-1001", 0);
System.out.print(testId); // -1001

int testId = parseIntWithDefault("test", 0);
System.out.print(testId); // 0

0
投票

如果有人特意请求整数,我想提出另一个有效的建议:只需使用long并使用Long.MIN_VALUE来处理错误情况。这类似于Reader中用于chars的方法,其中Reader.read()返回char范围内的整数,如果读取器为空则返回-1。

对于Float和Double,NaN可以以类似的方式使用。

NumberUtils

0
投票

您不应使用例外来验证您的值。

对于单个字符,有一个简单的解决方案:

int testId = NumberUtils.toInt("test", 0);
System.out.print(testId); // 0

对于更长的值,最好使用一些工具。 Apache提供的NumberUtils在这里可以很好地工作:

public static long parseInteger(String s) {
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException e) {
        return Long.MIN_VALUE;
    }
}


// ...
long l = parseInteger("ABC");
if (l == Long.MIN_VALUE) {
    // ... error
} else {
    int i = (int) l;
}

请检查Character.isDigit()


-1
投票

您可以像这样使用Null-Object:

NumberUtils.isNumber()

此示例中main的结果是:

https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/math/NumberUtils.html

这样,您始终可以测试失败的转换,但仍然可以将结果用作Integer实例。您可能还想调整NULL表示的数字(≠0)。


24
投票

在某些情况下,您应该将解析错误视为快速失败的情况,但在其他情况下,例如应用程序配置,我更喜欢使用Apache Commons Lang 3 NumberUtils处理缺省值的缺失输入。

int port = NumberUtils.toInt(properties.getProperty("port"), 8080);

15
投票

为避免处理异常,请使用正则表达式以确保首先包含所有数字:

if(value.matches("\\d+") {
    Integer.parseInt(value);
}

10
投票

Ints.tryParse()Guava。它不会在非数字字符串上抛出异常,但它会在空字符串上抛出异常。


4
投票

在阅读了问题的答案之后,我认为封装或包装parseInt方法是没有必要的,甚至可能不是一个好主意。

您可以像Jon建议的那样返回'null',但这或多或少通过null-check替换try / catch构造。如果你'忘记'错误处理,那么行为只会略有不同:如果你没有捕获异常,那么就没有赋值,左侧变量保持旧值。如果你不测试null,你可能会受到JVM(NPE)的攻击。

哈欠的建议对我来说看起来更优雅,因为我不喜欢返回null来表示一些错误或异常状态。现在,您必须检查与预定义对象的引用相等性,这表示存在问题。但是,正如其他人所说,如果再次忘记'检查并且String是不可解析的,程序将继续使用'ERROR'或'NULL'对象中的包装int。

Nikolay的解决方案更加面向对象,并且可以与其他包装类的parseXXX方法一起使用。但最后,他只是将NumberFormatException替换为OperationNotSupported异常 - 再次需要try / catch来处理不可解析的输入。

所以,我的结论是没有封装普通的parseInt方法。如果我还可以添加一些(依赖于应用程序)错误处理,我只会封装。


4
投票

也许你可以使用这样的东西:

public class Test {
public interface Option<T> {
    T get();

    T getOrElse(T def);

    boolean hasValue();
}

final static class Some<T> implements Option<T> {

    private final T value;

    public Some(T value) {
        this.value = value;
    }

    @Override
    public T get() {
        return value;
    }

    @Override
    public T getOrElse(T def) {
        return value;
    }

    @Override
    public boolean hasValue() {
        return true;
    }
}

final static class None<T> implements Option<T> {

    @Override
    public T get() {
        throw new UnsupportedOperationException();
    }

    @Override
    public T getOrElse(T def) {
        return def;
    }

    @Override
    public boolean hasValue() {
        return false;
    }

}

public static Option<Integer> parseInt(String s) {
    Option<Integer> result = new None<Integer>();
    try {
        Integer value = Integer.parseInt(s);
        result = new Some<Integer>(value);
    } catch (NumberFormatException e) {
    }
    return result;
}

}

2
投票

您还可以非常简单地复制您想要的C ++行为

public static boolean parseInt(String str, int[] byRef) {
    if(byRef==null) return false;
    try {
       byRef[0] = Integer.parseInt(prop);
       return true;
    } catch (NumberFormatException ex) {
       return false;
    }
}

你会使用这样的方法:

int[] byRef = new int[1];
boolean result = parseInt("123",byRef);

之后变量qazxsw poi如果一切顺利且qazxsw poi包含已解析的值,则为真。

就个人而言,我会坚持抓住异常。


1
投票

我的Java有点生疏,但让我看看我是否可以指出你正确的方向:

result

如果您的返回值是byRef[0],那么您的值很差。否则,你有一个有效的public class Converter { public static Integer parseInt(String str) { Integer n = null; try { n = new Integer(Integer.tryParse(str)); } catch (NumberFormatException ex) { // leave n null, the string is invalid } return n; } }

© www.soinside.com 2019 - 2024. All rights reserved.