it-swarm.com.de

Gute Möglichkeit, Integer.parseInt () einzukapseln

Ich habe ein Projekt, in dem wir häufig Integer.parseInt() verwenden, um einen String in ein int zu konvertieren. Wenn etwas schief geht (zum Beispiel ist String keine Zahl, sondern der Buchstabe a oder was auch immer), löst diese Methode eine Ausnahme aus. Wenn ich jedoch überall in meinem Code mit Ausnahmen umgehen muss, sieht das sehr schnell aus. Ich möchte dies in eine Methode einfügen, ich habe jedoch keine Ahnung, wie ich einen sauberen Wert zurückgeben sollte, um zu zeigen, dass die Konvertierung fehlgeschlagen ist. 

In C++ hätte ich eine Methode erstellen können, die einen Zeiger auf ein int akzeptiert und die Methode selbst true oder false zurückgeben lässt. Dies ist meines Wissens jedoch in Java nicht möglich. Ich könnte auch ein Objekt erstellen, das eine true/false-Variable und den konvertierten Wert enthält. Dies scheint jedoch nicht ideal zu sein. Dasselbe gilt für einen globalen Wert, und dies kann mir beim Multithreading Probleme bereiten. 

Gibt es einen sauberen Weg, dies zu tun? 

79
Bad Horse

Sie könnten anstelle von Integer eine int zurückgeben und bei einem Parser-Fehler null zurückgeben.

Es ist eine Schande, dass Java keine Möglichkeit bietet, dies zu tun, ohne dass intern eine Ausnahme ausgelöst wird - Sie können die Ausnahme ausblenden (indem Sie sie abfangen und null zurückgeben), aber es könnte dennoch ein Leistungsproblem sein, wenn Sie Hunderte analysieren von Tausenden von Bits vom Benutzer bereitgestellten Daten.

EDIT: Code für eine solche Methode:

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

Beachten Sie, dass ich nicht ganz sicher bin, was dies tun wird, wenn text null ist. Sie sollten berücksichtigen, dass - wenn es sich um einen Fehler handelt (d. H., Ihr Code kann zwar einen ungültigen Wert übergeben, aber niemals einen Nullwert übergeben), ist das Auslösen einer Ausnahme angemessen. Wenn es sich nicht um einen Fehler handelt, sollten Sie wahrscheinlich nur null zurückgeben, wie Sie es für jeden anderen ungültigen Wert tun würden.

Ursprünglich verwendete diese Antwort den Konstruktor new Integer(String); es verwendet jetzt Integer.parseInt und einen Boxvorgang; Auf diese Weise werden kleine Werte zu zwischengespeicherten Integer-Objekten zusammengefasst, wodurch sie in solchen Situationen effizienter werden.

126
Jon Skeet

Welches Verhalten erwarten Sie, wenn es keine Zahl ist?

Wenn Sie beispielsweise häufig einen Standardwert verwenden, wenn die Eingabe keine Zahl ist, könnte eine Methode wie diese nützlich sein:

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

Ähnliche Methoden können für ein anderes Standardverhalten geschrieben werden, wenn die Eingabe nicht analysiert werden kann.

32
Joachim Sauer

In einigen Fällen sollten Sie Fehler beim Parsing als ausfallsichere Situationen behandeln. In anderen Fällen, z. B. bei der Anwendungskonfiguration, ziehe ich es vor, fehlende Eingaben mit Standardwerten mithilfe von Apache Commons Lang 3 NumberUtils zu behandeln.

int port = NumberUtils.toInt(properties.getProperty("port"), 8080);
22
Bryan W. Wagner

Um die Behandlung von Ausnahmen zu vermeiden, verwenden Sie einen regulären Ausdruck, um sicherzustellen, dass Sie zuerst alle Ziffern haben:

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

Es gibt Ints.tryParse() in Guave . Es wird keine Ausnahme für eine nicht numerische Zeichenfolge ausgelöst, es wird jedoch eine Ausnahme für eine Nullzeichenfolge ausgelöst. 

10
husayt

Nach dem Lesen der Antworten auf die Frage, die ich denke, ist das Einkapseln oder Einwickeln der parseInt-Methode nicht notwendig, vielleicht sogar keine gute Idee.

Sie könnten 'null' zurückgeben, wie Jon vorgeschlagen hat, aber dies ersetzt mehr oder weniger ein try/catch-Konstrukt durch eine Nullprüfung. Es gibt nur einen kleinen Unterschied im Verhalten, wenn Sie die Fehlerbehandlung "vergessen": Wenn Sie die Ausnahme nicht abfangen, gibt es keine Zuweisung und die linke Variable behält den alten Wert. Wenn Sie nicht auf Null testen, werden Sie wahrscheinlich von der JVM (NPE) getroffen.

der Vorschlag von yawn sieht für mich eleganter aus, weil ich nicht gerne Null zurückließe, um einige Fehler oder außergewöhnliche Zustände anzuzeigen. Jetzt müssen Sie die referenzielle Gleichheit mit einem vordefinierten Objekt überprüfen, das auf ein Problem hinweist. Wie andere jedoch behaupten, wenn Sie erneut "vergessen", zu prüfen, und ein String nicht analysierbar ist, läuft das Programm kontinuierlich mit dem eingebundenen int in Ihrem 'ERROR' oder 'NULL' Objekt.

Nikolays Lösung ist noch objektorientierter und wird auch mit parseXXX-Methoden aus anderen Wrapper-Klassen arbeiten. Am Ende hat er die NumberFormatException jedoch durch eine OperationNotSupported-Ausnahme ersetzt. Wiederum benötigen Sie einen Try/Catch-Befehl, um mit unparsierbaren Eingaben umzugehen.

Daher ist es meine Schlussfolgerung, die plain parseInt-Methode nicht einzukapseln. Ich würde nur kapseln, wenn ich einige (anwendungsabhängige) Fehlerbehandlung hinzufügen könnte.

4
Andreas_D

Vielleicht kannst du so etwas verwenden:

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;
}

}
4
Nikolay Ivanov

Sie können das gewünschte C++ - Verhalten auch sehr einfach replizieren

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;
    }
}

Sie würden die Methode wie folgt verwenden:

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

Danach ist die Variable result wahr, wenn alles richtig geht und byRef[0] den geparsten Wert enthält.

Persönlich würde ich mich an die Ausnahme halten.

2
Wilian Z.

Mein Java ist ein bisschen rostig, aber lassen Sie mich sehen, ob ich Sie in die richtige Richtung weisen kann:

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;
    }

}

Wenn Ihr Rückgabewert null ist, haben Sie einen ungültigen Wert. Ansonsten haben Sie eine gültige Integer.

1
Adam Maras

Wenn Sie Java 8 oder höher verwenden, können Sie eine Bibliothek verwenden, die ich gerade veröffentlicht habe: https://github.com/robtimus/try-parse . Es unterstützt int, long und boolean, die nicht darauf angewiesen sind, Ausnahmen abzufangen. Im Gegensatz zu Guavas Ints.tryParse gibt es OptionalInt/OptionalLong/Optional zurück, ähnlich wie in https://stackoverflow.com/a/38451745/1180351 , aber effizienter.

1
Rob Spoor

Was ist mit die parseInt - Methode aufzuteilen?

Es ist einfach: Kopieren Sie den Inhalt einfach in ein neues Dienstprogramm, das Integer oder Optional<Integer> zurückgibt, und ersetzen Sie Würfe durch Rückgaben. Es scheint, dass es im zugrundeliegenden Code keine Ausnahmen gibt, aber besser überprüfen.

Wenn Sie die gesamte Ausnahmebehandlung überspringen, können Sie bei ungültigen Eingaben etwas Zeit sparen. Und die Methode gibt es seit JDK 1.0. Es ist also unwahrscheinlich, dass Sie viel tun müssen, um auf dem neuesten Stand zu bleiben.

1
Vlasec

Die Art und Weise, wie ich mit diesem Problem umgehe, ist rekursiv. Zum Beispiel beim Lesen von Daten von der Konsole:

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;
}
0
Boboman

Um eine Ausnahme zu vermeiden, können Sie die Format.parseObject-Methode von Java verwenden. Der folgende Code ist im Wesentlichen eine vereinfachte Version von Apache Commons IntegerValidator - Klasse.

public 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;
}

Sie können je nach Wunsch entweder AtomicInteger oder den int[]-Array-Trick verwenden.

Hier ist mein Test, der es benutzt -

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

Die Antwort von Jon Skeet ist in Ordnung, aber ich mag es nicht, ein null Integer-Objekt zurückzugeben. Ich finde das verwirrend zu benutzen. Seit Java 8 gibt es (meiner Meinung nach) eine bessere Option, die OptionalInt zu verwenden:

public static OptionalInt tryParse(String value) {
 try {
     return OptionalInt.of(Integer.parseInt(value));
  } catch (NumberFormatException e) {
     return OptionalInt.empty();
  }
}

Dies macht deutlich, dass Sie den Fall behandeln müssen, in dem kein Wert verfügbar ist. Ich würde es vorziehen, wenn diese Art von Funktionen zukünftig zur Java-Bibliothek hinzugefügt würde, aber ich weiß nicht, ob dies jemals passieren wird.

0
Marc

Ich hatte auch das gleiche Problem. Dies ist eine Methode, die ich geschrieben habe, um den Benutzer nach einer Eingabe zu fragen und die Eingabe nicht zu akzeptieren, es sei denn, es handelt sich um eine Ganzzahl. Bitte beachten Sie, dass ich Anfänger bin. Wenn der Code nicht wie erwartet funktioniert, dann ist meine Unerfahrenheit die Schuld!

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();
}
0
Abhinav Mathur

Ich würde vorschlagen, dass Sie eine Methode wie betrachten

 IntegerUtilities.isValidInteger(String s)

die Sie dann umsetzen, wie Sie es für richtig halten. Wenn Sie möchten, dass das Ergebnis zurückgegeben wird - vielleicht, weil Sie sowieso Integer.parseInt () verwenden - können Sie den Array-Trick verwenden.

 IntegerUtilities.isValidInteger(String s, int[] result)

wo Sie result [0] auf den ganzzahligen Wert setzen, der im Prozess gefunden wurde.

Dies ist eine Antwort auf Frage 8391979: "Hat Java eine int.tryparse, die keine Ausnahme für fehlerhafte Daten auslöst? [Duplikat]", die geschlossen und mit dieser Frage verknüpft ist.

Edit 2016 08 17: LtrimZeroes-Methoden hinzugefügt und in tryParse () aufgerufen. Ohne führende Nullen in numberString kann es zu falschen Ergebnissen kommen (siehe Kommentare im Code). Es gibt jetzt auch eine öffentliche statische String ltrimZeroes (String numberString) -Methode, die für positive und negative "Zahlen" funktioniert (END Edit).

Nachfolgend finden Sie eine rudimentäre Wrapper (boxing) -Klasse für int mit einer hochgeschwindigkeitsoptimierten tryParse () - Methode (ähnlich wie in C #), die den String selbst parst und etwas schneller ist als Integer.parseInt (String s) von Java:

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;
    }
}

Test-/Beispielprogramm für die Klasse IntBoxSimple:

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());
    }
}
0
Peter Sulzer

Versuchen Sie es mit regulären Ausdrücken und Standardparametern

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

wenn Sie Apache.commons.lang3 verwenden, verwenden Sie NumberUtils :

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

Sie könnten Ihre eigenen Rollen machen, aber es ist genauso einfach, die StringUtils.isNumeric()-Methode von commons lang zu verwenden. Es verwendet Character.isDigit () , um jedes Zeichen im String zu durchlaufen.

0
James Bassett

Ich würde gerne einen anderen Vorschlag einreichen, der funktioniert, wenn man gezielt ganze Zahlen anfordert: Einfach long verwenden und für Fehlerfälle Long.MIN_VALUE verwenden. Dies ähnelt dem Ansatz, der für Zeichen in Reader verwendet wird, wenn Reader.read () eine Ganzzahl im Bereich eines Zeichens zurückgibt oder -1, wenn der Reader leer ist.

Für Float und Double kann NaN auf ähnliche Weise verwendet werden.

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;
}
0
Searles

Sie sollten keine Ausnahmen verwenden, um Ihre Werte zu überprüfen.

Für Einzelzeichen gibt es eine einfache Lösung:

Character.isDigit()

Für längere Werte sollten einige Utensilien verwendet werden. NumberUtils, die von Apache bereitgestellt werden, funktionieren hier perfekt:

NumberUtils.isNumber()

Bitte überprüfen Sie https://commons.Apache.org/proper/commons-lang/javadocs/api-2.6/org/Apache/commons/lang/math/NumberUtils.html

0

Dies ist in etwa der Lösung von Nikolay ähnlich:

 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());
 }

Die Hauptmethode demonstriert den Code. Eine andere Möglichkeit, die Parser-Schnittstelle zu implementieren, wäre offensichtlich, einfach "\ D +" von der Konstruktion aus zu setzen und die Methoden nichts tun zu lassen.

0
Carl