2013-08-29 16 views
6

mam następujące:stwierdzić, czy ciąg podwoić/pływak/int/krótkie/bajt jest poza zakresem

string outOfRange = "2147483648"; // +1 over int.MaxValue 

Oczywiście, jeśli masz coś innego niż liczba ta nie powiedzie:

var defaultValue = 0; 
int.TryParse(outOfRange, out defaultValue); 

Moje pytanie brzmi: skoro to jest liczba, a zakończy się niepowodzeniem, gdy otrzymasz numer int.TryParse(), jak możesz stwierdzić, że to się nie powiodło, ponieważ ciąg znajdował się poza zakresem kontenera, w którym jest przechowywany?

+1

jest analizowanie go do większej wartości pojemności pierwszy (powiedzmy 'long' lub' uint' jeśli wiesz, że masz tylko pozytywy) następnie sprawdzanie z 'Int32.Max/MinValue' wykonalne? –

+0

Przypuszczam, że to może być jedno rozwiązanie. Może coś bardziej ogólnego, które działałoby z dowolnym typem kontenera. – gleng

+0

Dlaczego nie zawsze używasz największej wymaganej pojemności, jeśli jest to możliwe, że twoja wartość przepełnia mniejsze pojemności? Czy możesz dodać szczegóły dotyczące kontekstu? –

Odpowiedz

2

Chciałbym spróbować przeanalizować, jeśli nie powiedzie się, następnie próbować parsować wartość o większej wydajności. Jeśli wartość wyższej pojemności przejdzie parsowanie, oznacza to, że jest poza zakresem. Jeśli to się nie uda, to jest to złe wejście.

string outOfRange = "2147483648"; // +1 over int.MaxValue 
int result; 
if (!Int32.TryParse(outOfRange, out result)) 
{ 
    long rangeChecker; 
    if (Int64.TryParse(outOfRange, out rangeChecker)) 
     //out of range 
    else 
     //bad format 
} 

Niestety, nie sądzę, że istnieje sposób, aby zrobić to ogólnie dla każdego typu; musisz napisać implementację dla wszystkich typów. A więc na przykład, co zrobić z Int64? Może używać BigInteger zamiast:

string outOfRange = "9223372036854775808"; // +1 over Int64.MaxValue 
long result; 
if (!Int64.TryParse(outOfRange, out result)) 
{ 
    BigInteger rangeChecker; 
    if (BigInteger.TryParse(outOfRange, out rangeChecker)) 
     //out of range 
    else 
     //bad format 
} 

EDIT: double zmiennoprzecinkowych może być więcej zabawy, ponieważ AFAIK, nie ma „BigDecimal” i może muszą również uwzględniać wartości, które zbliżają 0 w bardzo ekstremalnych (nie pewny tego). Być może mógłbyś zmienić wariację na czeku BigInteger, ale możesz również uwzględnić przecinki dziesiętne (najprawdopodobniej najprostsze wyrażenie byłoby tutaj najlepiej mieć tylko cyfry, opcjonalny znak ujemny i tylko najwyżej kropki dziesiętnej).Jeśli są jakieś przecinki dziesiętne, musisz je skrócić i po prostu sprawdzić całkowitą część ciągu.

EDITx2: Oto bardzo brzydki wdrożenie do sprawdzania double wartości zbyt:

// +bajillion over Double.MaxValue 
string outOfRange = "90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.1"; 
double result; 
if (!Double.TryParse(outOfRange, out result)) 
{ 
    string bigIntegerInput = outOfRange; 

    if (!Regex.IsMatch(bigIntegerInput, @"^-?[0-9]\d*(\.\d+)?$")) 
     //bad format 

    int decimalIndex = bigIntegerInput.IndexOf('.'); 
    if (decimalIndex > -1) 
     bigIntegerInput = bigIntegerInput.Substring(0, decimalIndex); 

    BigInteger rangeChecker; 
    if (BigInteger.TryParse(bigIntegerInput, out rangeChecker)) 
     //out of range 
    else 
     //bad format 
} 

Ale szczerze mówiąc, w tym momencie myślę, że właśnie wyjechał na głęboką wodę. Jeśli nie masz jakiegoś realnego wąskiego gardła wydajności lub Twoja aplikacja ma wartości spoza zakresu wprowadzane często, może być lepiej, gdy tylko złapiesz je w dziwnym momencie, jak to się dzieje, jak w this answer lub, być może, po prostu, stosując regex do wejście. W moim ostatnim przykładzie mógłbym równie dobrze zrezygnować po wykonaniu regex i tak (ale nie wiem z góry, że implementacje TryParse są łagodniejsze, co pozwala na zapis wykładniczy/naukowy. będą również musiały je pokrywać)

0

Prostym sposobem byłoby zamiast tego użyć Int32.Parse(string s) i złapać OverflowException;

OverflowException
y oznacza liczbę mniejszą niż lub większa niż MINVALUE maxvalue.

+1

Wyjątki nie są dobre dla przepływu sterowania. –

+1

@ It'sNotalie. Generalnie uniknęłbym ich podczas tego rodzaju operacji, ale w tym przypadku nie jest proste napisanie zamiennika. Jeśli zaangażujesz 'IFormatProvider', jeszcze bardziej. –

1

Użyj zwykłego Parse zamiast TryParse. A następnie użyj go wewnątrz try/catch, ponieważ da ci odpowiedni wyjątek. Zobacz szczegóły: http://msdn.microsoft.com/en-us/library/b3h1hf19.aspx. Wyjątek, którego szukasz, to OverflowException.

+0

Dlaczego używać wyjątków do sterowania przepływem, gdy można po prostu użyć zwracanej wartości 'TryParse'? –

+1

'TryParse' zwraca' bool' i to nie jest porównywalne do otrzymywania wielu różnych wyjątków z 'Parse'. – meilke

2
string outOfRange = "2147483648"; // +1 over int.MaxValue 
int value; 
if(! int.TryParse(outOfRange, out value)) { 
    try { 
     int.Parse(defaultValue); 
    } catch(OverflowException e) { 
     // was overflow 
    } catch(Exception e) { 
     // was other reason 
    } 
} 

Zakładając, że istnieje kilka przypadków, w których liczba jest zbyt duża, narzut wyjątku rzucanie i łapanie mogą być tolerowane, jak normalne przypadki są traktowane z szybszym TryParse metody bez udziału wyjątki.

To działa podobnie do innych typów danych liczbowych, jak pływa ...

+0

Nie używaj wyjątków do sterowania przepływem. –

+0

Zamiast tego możesz zamiast tego użyć 'Int64.TryParse'; jeśli to minie, wiesz, że jest poza zasięgiem. Jeśli się nie powiedzie, to wiesz, że to złe wejście. –

+0

@ChrisSinclair Ale pierwotne pytanie dotyczyło również rozwiązania dla długich, podwójnych, itd. – FrankPl

1

chciałbym przyjrzeć przy użyciu System.Convert.ToInt32(String) jako mechanizm do przekształcania rzeczy; mianowicie, ponieważ wyjątek OverflowException został już zaimplementowany.

Jest to wygodne, ponieważ można zrobić coś prostego jak

try 
{ 
     result = Convert.ToInt32(value); 
     Console.WriteLine("Converted the {0} value '{1}' to the {2} value {3}.", 
        value.GetType().Name, value, result.GetType().Name, result); 
} 
catch (OverflowException) 
{ 
     Console.WriteLine("{0} is outside the range of the Int32 type.", value); 
} 
catch (FormatException) 
{ 
     Console.WriteLine("The {0} value '{1}' is not in a recognizable format.", 
        value.GetType().Name, value); 
} 

i logika jest już częścią standardowej biblioteki systemowej.

+0

To nie odnosi się do podwójnego/float. – hatchet

+0

Convert.ToDouble (String) i Convert.ToFloat (String) z pewnością do. I są już wdrożone przez MSDN. –

+0

Interpretowałem twój "mechanizm przekształcania rzeczy" w ten sposób, że powyższy kod może być użyty do konwersji również podwójnych i pływających. Jeśli twój kod jest przykładem, który częściowo rozwiązuje problem, z innym kodem potrzebnym w przypadku takich przypadków jak double/float, powyższy komentarz nie ma zastosowania. – hatchet

2

Możesz spróbować parsować z BigInteger.

BigInteger bigInt; 
bool isAnOutOfRangeInt = BigInteger.TryParse(input, out bigInt) 
         && (bigInt > int.MaxValue || bigInt < int.MinValue); 
// if you care to have the value as an int: 
if (!isAnOutOfRangeInt) 
{ 
    int intValue = (int)bigInt; 
} 
+0

A jak użyłbyś tego dla 'double's zamiast' int's, zgodnie z żądaniem oryginalnego postu? – FrankPl

+0

"Dziesiętny" zamiast? – Matthew

4

Wybrałbym rozwiązanie Try/Catch dla tego scenariusza.

 string outOfRange = "2147483648"; 
     try 
     { 
      int.Parse(outOfRange); 
     } 
     catch (OverflowException oex) 
     { 

     } 
     catch (Exception ex) 
     { } 

wiem, że większość ludzi tutaj polecam unikanie tego, ale czasami po prostu musimy z niego korzystać (lub nie mamy do ale to właśnie nam zaoszczędzić dużo czasu).
here's mały post o skuteczności Try/Catch.

3

można analizować na dziesiętny, a następnie sprawdzić zasięg, unika try/catch

string s = "2147483648"; 
decimal.Parse(s) > int.MaxValue;