Dobry sposób na hermetyzację Integer.parseInt ()


92

Mam projekt, w którym często używamy Integer.parseInt()do konwersji ciągu znaków na int. Gdy coś pójdzie nie tak (na przykład Stringnie jest liczbą, ale literą alub czymkolwiek), ta metoda zgłosi wyjątek. Jeśli jednak muszę wszędzie obsługiwać wyjątki w moim kodzie, bardzo szybko zaczyna to wyglądać bardzo brzydko. Chciałbym to przedstawić w metodzie, jednak nie mam pojęcia, jak zwrócić czystą wartość, aby pokazać, że konwersja poszła nie tak.

W C ++ mogłem stworzyć metodę, która akceptowała wskaźnik do int i pozwalała samej metodzie zwracać wartość true lub false. Jednak o ile wiem, w Javie nie jest to możliwe. Mógłbym również utworzyć obiekt, który zawiera zmienną prawda / fałsz i przekonwertowaną wartość, ale to też nie wydaje się idealne. To samo dotyczy wartości globalnej, co może przysporzyć mi problemów z wielowątkowością.

Więc czy jest na to czysty sposób?


Znaki w ciągu muszą być wszystkie cyfry po przecinku, z wyjątkiem, że pierwszy znak ... . Zamiast obsługiwać wyjątki w każdym miejscu kodu, po prostu sprawdź format ciągu przed wywołaniem metody analizy.
Lightman,

Prawie niemożliwe jest napisanie wyrażenia regularnego, które przechwytuje wszystkie prawidłowe 32-bitowe liczby całkowite ze znakiem i żadną z nieprawidłowych. 2147483647 jest legalne, inta 2147483648 nie.
Seva Alekseyev

Odpowiedzi:


142

Możesz zwrócić Integerzamiast an int, zwracając nullw przypadku niepowodzenia analizy.

Szkoda, że ​​Java nie zapewnia sposobu zrobienia tego bez wewnętrznego zgłoszenia wyjątku - możesz ukryć wyjątek (przechwytując go i zwracając wartość null), ale nadal może to być problem z wydajnością, jeśli analizujesz setki tysięcy bitów danych dostarczonych przez użytkowników.

EDYCJA: Kod takiej metody:

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

Zwróć uwagę, że nie jestem pewien, co się stanie, jeśli textjest zerowy. Powinieneś wziąć pod uwagę, że - jeśli reprezentuje błąd (tj. Twój kod może równie dobrze przekazać nieprawidłową wartość, ale nigdy nie powinien przekazywać wartości null), to zgłoszenie wyjątku jest właściwe; jeśli nie reprezentuje błędu, prawdopodobnie powinieneś po prostu zwrócić wartość null, tak jak w przypadku każdej innej nieprawidłowej wartości.

Oryginalnie ta odpowiedź używała new Integer(String)konstruktora; teraz używa Integer.parseInti operacji bokserskiej; w ten sposób małe wartości zostaną w końcu opakowane w Integerobiekty w pamięci podręcznej , dzięki czemu będzie bardziej wydajne w takich sytuacjach.


1
Jak to pomaga? Strona wywołania będzie wymagać: <b> temp = tryParse (...); if (temp! = null) {target = temp; } else {wykonaj akcję odzyskiwania}; </b> z prawdopodobnym wyjątkiem zgłoszenia w części odzyskiwania. W pierwotnym sformułowaniu witryna wywołania wymaga <b> try target = (...). ParseInt; catch (...) {wykonaj akcję odzyskiwania} </b> z trywialnym wyjątkiem rzutowania w implementacji odzyskiwania, po prostu usuwając klauzulę catch. W jaki sposób proponowane rozwiązanie ułatwia zrozumienie tego (ma magiczną sztuczkę) lub w jakikolwiek sposób zmniejsza ilość kodu?
Ira Baxter

15
Generalnie czystsze jest sprawdzanie w kodzie nullodwołań niż regularne obsługiwanie wyjątków.
Adam Maras

Jeszcze czystsze jest unikanie przekazywania wartości null jako wartości, ale zamiast tego w jakiś sposób wskazuje, że wystąpił błąd en; wyjątki nie powinny być używane do sterowania przepływem.
Esko

2
@Steve Kuo: Dlaczego? Gdzie jest korzyść? Oboje tworzą za każdym razem nową liczbę całkowitą? Jeśli już, kusi mnie, aby użyć Integer.parseInt i pozwolić autoboxingowi zająć się tym, aby wykorzystać pamięć podręczną dla małych wartości.
Jon Skeet

1
@Vlasec I nie tylko Optional, ale wyspecjalizowane wersje dla prymitywów, takie jak OptionalInt.
Joshua Taylor,

37

Jakiego zachowania oczekujesz, gdy nie jest to liczba?

Jeśli na przykład często masz wartość domyślną do użycia, gdy dane wejściowe nie są liczbą, wówczas przydatna może być metoda taka jak ta:

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

Podobne metody można napisać dla innego domyślnego zachowania, gdy nie można przeanalizować danych wejściowych.


29

W niektórych przypadkach powinieneś traktować błędy parsowania jako sytuacje bezawaryjne, ale w innych przypadkach, takich jak konfiguracja aplikacji, wolę obsłużyć brakujące dane wejściowe z domyślnymi wartościami za pomocą Apache Commons Lang 3 NumberUtils .

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

Przez większość czasu używasz już w swoim projekcie apache commons z innych powodów (takich jak StringUtils), które stają się przydatne.
Ratata Tata

16

Aby uniknąć obsługi wyjątków, użyj wyrażenia regularnego, aby upewnić się, że najpierw masz wszystkie cyfry:

//Checking for Regular expression that matches digits
if(value.matches("\\d+")) {
     Integer.parseInt(value);
}

Dziękuję za Twoją odpowiedź. Przeczytałem większość odpowiedzi na tej stronie, osobiście napisałem rozwiązanie try / catch. jednak tutaj jest mój problem, choć mały, z tym rozwiązaniem. większość IDE dławi się analizowaniem przepływu twojego kodu, gdy masz próbę / catch wewnątrz pętli. dlatego potrzebowałem rozwiązania bez try / catch.
zwycięzca n.

4
Bądź ostrożny. Wyrażenie regularne z dopasowuje liczbę całkowitą zaczynającą się od 0, która następnie zgłosi wyjątek NumberFormatException. Spróbuj tego ^ (?: [1-9] \ d * | 0) $ ze stackoverflow.com/questions/12018479/…
Goose

5
To konkretne wyrażenie regularne nie obsługuje liczb ujemnych.
Brad Cupit

7
również to nie obejmuje zakresów liczb wykraczających poza granice liczb całkowitych
Mohammad Yahia

10

Jest Ints.tryParse()w guawie . Nie zgłasza wyjątku na łańcuchu nieliczbowym, jednak rzuca wyjątek na łańcuch o wartości null.


4

Po przeczytaniu odpowiedzi na pytanie uważam, że hermetyzowanie lub zawijanie metody parseInt nie jest konieczne, może nawet nie jest to dobry pomysł.

Możesz zwrócić „null”, jak zasugerował Jon, ale to mniej więcej zastąpienie konstrukcji try / catch przez sprawdzenie wartości null. Jest tylko niewielka różnica w zachowaniu, jeśli „zapomnisz” obsługi błędów: jeśli nie złapiesz wyjątku, nie ma przypisania, a zmienna po lewej stronie zachowuje starą wartość. Jeśli nie wykonasz testu pod kątem wartości null, prawdopodobnie zostaniesz uderzony przez JVM (NPE).

Sugestia ziewania wygląda dla mnie bardziej elegancko, ponieważ nie lubię zwracać wartości zerowej, aby zasygnalizować jakieś błędy lub wyjątkowe stany. Teraz musisz sprawdzić równość referencyjną z predefiniowanym obiektem, który wskazuje na problem. Ale, jak twierdzą inni, jeśli znowu „zapomnisz” o sprawdzeniu, a String jest niemożliwy do przeanalizowania, program będzie kontynuował z opakowanym int wewnątrz obiektu „ERROR” lub „NULL”.

Rozwiązanie Nikolaya jest jeszcze bardziej zorientowane obiektowo i będzie działać z metodami parseXXX z innych klas opakowujących. Ostatecznie jednak zastąpił on wyjątek NumberFormatException przez wyjątek OperationNotSupported - ponownie potrzebujesz metody try / catch, aby obsłużyć dane wejściowe, których nie można przeanalizować.

Tak więc, to mój wniosek, aby nie hermetyzować zwykłej metody parseInt. Hermetyzowałbym tylko wtedy, gdybym mógł dodać również obsługę błędów (zależną od aplikacji).


4

Może możesz użyć czegoś takiego:

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

}

Podoba mi się twoje rozwiązanie wykorzystujące wzór może. Very haskelly;)
rodrigoelp

1
To rozwiązanie jest teraz przestarzałe, ponieważ istnieje java.util.Optional od wersji Java 8 :)
Vlasec

2

Możesz również w bardzo prosty sposób powielić zachowanie C ++, które chcesz

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

Użyłbyś takiej metody:

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

Po tym zmienna resultjest prawdą, jeśli wszystko poszło dobrze i byRef[0]zawiera przeanalizowaną wartość.

Osobiście trzymałbym się złapania wyjątku.


2

Odpowiedź udzielona przez Jona Skeeta jest w porządku, ale nie lubię oddawać nullobiektu typu Integer. Uważam to za mylące w użyciu. Od Java 8 istnieje lepsza opcja (moim zdaniem), wykorzystująca OptionalInt:

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

To wyraźnie pokazuje, że musisz obsłużyć przypadek, w którym żadna wartość nie jest dostępna. Wolałbym, aby tego rodzaju funkcja została w przyszłości dodana do biblioteki java, ale nie wiem, czy to kiedykolwiek się stanie.



1

Moja Java jest trochę zardzewiała, ale zobaczę, czy mogę wskazać ci właściwy kierunek:

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

}

Jeśli zwracana wartość to null, masz złą wartość. W przeciwnym razie masz ważny plik Integer.


OP chce wyniku konwersji (jako odniesienia) oraz wskazania, że ​​konwersja powiodła się (lub nie).
ziewać

1
@yawn: I odwołanie zerowe daje dokładnie to wskazanie.
Jon Skeet

@John Skeet: poprawne, ale inaczej odczytałem jego zamiary. Napisał coś w rodzaju użycia obiektu pośredniego do odróżnienia sukcesu / porażki + wartość. Wychodząc z C ++ w tle, doszedłem do wniosku, że gdyby chciał użyć wartości null (zamiast obiektu), nie zadałby tego pytania.
ziewać

Istnieje zasadnicza różnica między „wartością” a „przedmiotem”. Odwołanie o wartości null to czysta wartość, ale nie obiekt.
Jon Skeet

1. W Integer.tryParsestandardowej Integerklasie Java nie ma . 2. Wykonywanie działań new Integerjest niepotrzebne (i zalecane), ponieważ Java automatycznie pakuje i rozpakowuje. Twoja Java jest nie tylko trochę zardzewiała, jest bardzo zardzewiała.
ADTC

1

A co z rozwidleniem metody parseInt ?

To proste, po prostu skopiuj i wklej zawartość do nowego narzędzia, które zwraca Integerlub Optional<Integer>i zamienia rzuty na zwroty. Wygląda na to, że w kodzie podstawowym nie ma wyjątków, ale lepiej sprawdź .

Pomijając całą obsługę wyjątków, możesz zaoszczędzić trochę czasu na nieprawidłowych danych wejściowych. Ta metoda istnieje od wersji JDK 1.0, więc jest mało prawdopodobne, że będziesz musiał robić wiele, aby ją aktualizować.


0

Sugerowałbym rozważenie takiej metody

 IntegerUtilities.isValidInteger(String s)

które następnie wdrażasz według własnego uznania. Jeśli chcesz, aby wynik został przeniesiony z powrotem - być może dlatego, że i tak używasz Integer.parseInt () - możesz użyć sztuczki tablicowej.

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

gdzie ustawiasz wynik [0] na wartość całkowitą znalezioną w procesie.


0

Jest to trochę podobne do rozwiązania Nikolaya:

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

Główna metoda demonstruje kod. Innym sposobem implementacji interfejsu Parser byłoby oczywiście po prostu ustawienie "\ D +" z konstrukcji i pozostawienie metodom nic nie robiąc.


0

Państwo mogłoby toczyć własną rękę, ale to tak samo łatwe w użyciu commons Langa StringUtils.isNumeric() metoda . Używa Character.isDigit () do iteracji po każdym znaku w łańcuchu.


Wtedy nie zadziała, jeśli cyfra zawiera zbyt dużą liczbę. Integer.parseInt zgłasza wyjątek dla liczb większych niż Integer.MAX_VALUE (to samo oczywiście dla strony ujemnej).
Searles

0

Sposób, w jaki radzę sobie z tym problemem, jest rekurencyjny. Na przykład podczas odczytu danych z konsoli:

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

Aby uniknąć wyjątku, możesz użyć Format.parseObjectmetody Java . Poniższy kod jest w zasadzie uproszczoną wersją klasy IntegerValidator Apache Common .

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

Możesz użyć sztuczki tablicowej AtomicIntegerlub, w int[]zależności od preferencji.

Oto mój test, który go wykorzystuje -

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

0

Ja też miałem ten sam problem. Jest to metoda, którą napisałem, aby poprosić użytkownika o dane wejściowe i nie akceptować danych wejściowych, chyba że jest to liczba całkowita. Pamiętaj, że jestem początkującym, więc jeśli kod nie działa zgodnie z oczekiwaniami, obwiniaj mój brak doświadczenia!

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

1
Nie używaj System.out.println (jest to zła praktyka). Problem z jego używaniem polega na tym, że twój program będzie czekał do zakończenia drukowania. Lepszym podejściem jest użycie struktury rejestrowania.
Omar Hrynkiewicz

0

To jest odpowiedź na pytanie 8391979, „Czy java ma int.tryparse, która nie zgłasza wyjątku dla złych danych? [Duplikat]”, która jest zamknięta i połączona z tym pytaniem.

Edytuj 2016 08 17: Dodano metody ltrimZeroes i wywołałem je w tryParse (). Bez wiodących zer w numberString może dać fałszywe wyniki (patrz komentarze w kodzie). Istnieje teraz również publiczna statyczna metoda String ltrimZeroes (String numberString), która działa dla dodatnich i ujemnych "liczb" (END Edit)

Poniżej znajdziesz podstawową klasę Wrapper (boxing) dla int ze zoptymalizowaną pod kątem szybkości metodą tryParse () (podobnie jak w C #), która analizuje sam ciąg znaków i jest nieco szybsza niż Integer.parseInt (String s) z Javy:

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 / przykładowy program dla klasy 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

Spróbuj z wyrażeniem regularnym i argumentem parametrów domyślnych

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

jeśli używasz apache.commons.lang3, to używając NumberUtils :

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

0

Chciałbym dorzucić inną propozycję, która działa, gdy żąda się konkretnie liczb całkowitych: po prostu użyj długości i użyj Long.MIN_VALUE dla przypadków błędów. Jest to podobne do podejścia stosowanego w przypadku znaków w programie Reader, gdzie funkcja Reader.read () zwraca liczbę całkowitą z zakresu typu char lub wartość -1, jeśli czytnik jest pusty.

W przypadku Float i Double NaN można użyć w podobny sposób.

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

Biorąc pod uwagę istniejące odpowiedzi, skopiowałem i ulepszyłem kod źródłowy programu, Integer.parseIntaby wykonać zadanie, i moje rozwiązanie

  • nie używa potencjalnie powolnego try-catch (w przeciwieństwie do Lang 3 NumberUtils ),
  • nie używa wyrażeń regularnych, które nie mogą wychwycić zbyt dużych liczb,
  • unika boksu (w przeciwieństwie do guawy Ints.tryParse()),
  • nie wymaga żadnych przydziałów (w przeciwieństwie do int[], Box, OptionalInt),
  • akceptuje CharSequenceczęść lub część zamiast całości String,
  • może używać dowolnej podstawy, która Integer.parseIntmoże, tj. [2,36],
  • nie zależy od żadnych bibliotek.

Jedynym minusem jest to, że nie ma różnicy między toIntOfDefault("-1", -1)i toIntOrDefault("oops", -1).

public static int toIntOrDefault(CharSequence s, int def) {
    return toIntOrDefault0(s, 0, s.length(), 10, def);
}
public static int toIntOrDefault(CharSequence s, int def, int radix) {
    radixCheck(radix);
    return toIntOrDefault0(s, 0, s.length(), radix, def);
}
public static int toIntOrDefault(CharSequence s, int start, int endExclusive, int def) {
    boundsCheck(start, endExclusive, s.length());
    return toIntOrDefault0(s, start, endExclusive, 10, def);
}
public static int toIntOrDefault(CharSequence s, int start, int endExclusive, int radix, int def) {
    radixCheck(radix);
    boundsCheck(start, endExclusive, s.length());
    return toIntOrDefault0(s, start, endExclusive, radix, def);
}
private static int toIntOrDefault0(CharSequence s, int start, int endExclusive, int radix, int def) {
    if (start == endExclusive) return def; // empty

    boolean negative = false;
    int limit = -Integer.MAX_VALUE;

    char firstChar = s.charAt(start);
    if (firstChar < '0') { // Possible leading "+" or "-"
        if (firstChar == '-') {
            negative = true;
            limit = Integer.MIN_VALUE;
        } else if (firstChar != '+') {
            return def;
        }

        start++;
        // Cannot have lone "+" or "-"
        if (start == endExclusive) return def;
    }
    int multmin = limit / radix;
    int result = 0;
    while (start < endExclusive) {
        // Accumulating negatively avoids surprises near MAX_VALUE
        int digit = Character.digit(s.charAt(start++), radix);
        if (digit < 0 || result < multmin) return def;
        result *= radix;
        if (result < limit + digit) return def;
        result -= digit;
    }
    return negative ? result : -result;
}
private static void radixCheck(int radix) {
    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
        throw new NumberFormatException(
                "radix=" + radix + " ∉ [" +  Character.MIN_RADIX + "," + Character.MAX_RADIX + "]");
}
private static void boundsCheck(int start, int endExclusive, int len) {
    if (start < 0 || start > len || start > endExclusive)
        throw new IndexOutOfBoundsException("start=" + start + " ∉ [0, min(" + len + ", " + endExclusive + ")]");
    if (endExclusive > len)
        throw new IndexOutOfBoundsException("endExclusive=" + endExclusive + " > s.length=" + len);
}

0

Może ktoś szuka bardziej ogólnego podejścia, skoro w Javie 8 jest Pakiet, java.util.functionktóry pozwala zdefiniować Funkcje Dostawcy. Możesz mieć funkcję, która przyjmuje dostawcę i wartość domyślną w następujący sposób:

public static <T> T tryGetOrDefault(Supplier<T> supplier, T defaultValue) {
    try {
        return supplier.get();
    } catch (Exception e) {
        return defaultValue;
    }
}

Za pomocą tej funkcji można wykonać dowolną metodę analizy lub nawet inne metody, które mogłyby zgłosić wyjątek, zapewniając jednocześnie, że żaden wyjątek nie zostanie nigdy zgłoszony:

Integer i = tryGetOrDefault(() -> Integer.parseInt(stringValue), 0);
Long l = tryGetOrDefault(() -> Long.parseLong(stringValue), 0l);
Double d = tryGetOrDefault(() -> Double.parseDouble(stringValue), 0d);

-1

Możesz użyć Null-Object w następujący sposób:

public class Convert {

    @SuppressWarnings({"UnnecessaryBoxing"})
    public static final Integer NULL = new Integer(0);

    public static Integer convert(String integer) {

        try {
            return Integer.valueOf(integer);
        } catch (NumberFormatException e) {
            return NULL;
        }

    }

    public static void main(String[] args) {

        Integer a = convert("123");
        System.out.println("a.equals(123) = " + a.equals(123));
        System.out.println("a == NULL " + (a == NULL));

        Integer b = convert("onetwothree");
        System.out.println("b.equals(123) = " + b.equals(123));
        System.out.println("b == NULL " + (b == NULL));

        Integer c = convert("0");
        System.out.println("equals(0) = " + c.equals(0));
        System.out.println("c == NULL " + (c == NULL));

    }

}

Wynikiem main w tym przykładzie jest:

a.equals(123) = true
a == NULL false
b.equals(123) = false
b == NULL true
c.equals(0) = true
c == NULL false

W ten sposób zawsze możesz sprawdzić, czy konwersja nie powiodła się, ale nadal pracować z wynikami jako instancjami typu Integer. Możesz także zmienić liczbę reprezentowaną przez NULL (≠ 0).


Co się stanie, jeśli „String integer” to literał String „0”? Nigdy nie dowiesz się, czy podano nieprawidłowe dane wejściowe.
Bart Kiers

Myślę, że zależy to od tego, czy operator == dla dwóch liczb całkowitych porównuje wartości lub odniesienia. Jeśli porównuje wartości, problem istnieje. Jeśli porówna odniesienia, zadziałaby w sposób równoważny z moją odpowiedzią.
Adam Maras

Dlaczego głos przeciw? Moja odpowiedź jest poprawna i ma tę przewagę (nad zerową), że zawsze masz do czynienia z prawidłową instancją liczby całkowitej (zamiast zera), zwalniając Cię z konieczności radzenia sobie z NPE.
ziewać

Jednak odróżnienie wartości null od rzeczywistej liczby całkowitej jest przydatne . Państwo powinno być testowanie wynik dla nieważności, aby wiedzieć, czy parse udało czy nie. Ukrywanie tego za i użytecznego w inny sposób obiektu to recepta na problemy, IMO.
Jon Skeet

Więcej głosów przeciw - interesujące! Odkąd / ja jestem nowy w SO i wszyscy mogliby mi wytłumaczyć dlaczego?
ziewać

-1

Nie należy używać wyjątków do sprawdzania poprawności wartości .

W przypadku pojedynczego znaku istnieje proste rozwiązanie:

Character.isDigit()

W przypadku dłuższych wartości lepiej jest użyć niektórych narzędzi. NumberUtils dostarczone przez Apache działałyby tutaj idealnie:

NumberUtils.isNumber()

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


«Sprawdza, czy ciąg znaków jest prawidłowym numerem Java. Poprawne liczby obejmują liczby szesnastkowe oznaczone kwalifikatorem 0x, notację naukową oraz liczby oznaczone kwalifikatorem typu (np. 123L). » To nie jest to, co można przeanalizować Integer.parseInt.
Miha_x64
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.