it-swarm.com.de

Beste Implementierung für eine isNumber (string) -Methode

In meiner begrenzten Erfahrung habe ich an mehreren Projekten teilgenommen, die eine Art String-Dienstprogrammklasse mit Methoden hatten, um festzustellen, ob eine bestimmte Zeichenfolge eine Zahl ist. Die Idee war immer die gleiche, jedoch war die Implementierung anders. Einige umgeben einen Analyseversuch mit try/catch 

public boolean isInteger(String str) {
    try {
        Integer.parseInt(str);
        return true;
    } catch (NumberFormatException nfe) {}
    return false;
}

und andere passen zu Regex

public boolean isInteger(String str) {
    return str.matches("^-?[0-9]+(\\.[0-9]+)?$");
}

Ist eine dieser Methoden besser als die andere? Ich persönlich ziehe es vor, den Regex-Ansatz zu verwenden, da er übersichtlich ist, aber er wird auf Augenhöhe abschneiden, wenn er über eine Liste von mehreren hunderttausend Saiten iteriert wird.

Hinweis: Da ich ein bisschen neu auf der Website bin, verstehe ich dieses Community-Wiki-Geschäft nicht vollständig. Wenn dies also dorthin gehört, lass es mich wissen, und ich ziehe es gerne weiter.

EDIT: Mit allen TryParse-Vorschlägen habe ich den Benchmark-Code von Asaph (danke für einen großartigen Beitrag!) Nach C # portiert und eine TryParse-Methode hinzugefügt. Und wie es scheint, gewinnt der TryParse zweifellos. Der Try-Catch-Ansatz hat jedoch eine verrückte Zeit in Anspruch genommen. Ich glaube, ich habe etwas falsch gemacht! Ich habe auch Regex aktualisiert, um Negative und Dezimalpunkte zu behandeln. 

Ergebnisse für den aktualisierten C # -Benchmarkcode:

00:00:51.7390000 for isIntegerParseInt
00:00:03.9110000 for isIntegerRegex
00:00:00.3500000 for isIntegerTryParse

Mit:

static bool isIntegerParseInt(string str) {
    try {
        int.Parse(str);
        return true;
    } catch (FormatException e){}
    return false;
}

static bool isIntegerRegex(string str) {
    return Regex.Match(str, "^-?[0-9]+(\\.[0-9]+)?$").Success;
}

static bool isIntegerTryParse(string str) {
    int bob;
    return Int32.TryParse(str, out bob);
}
18
thorncp

Ich habe gerade ein paar Benchmarks zur Leistung dieser beiden Methoden durchgeführt (Mac OS X Leopard Java 6). ParseInt ist schneller. Hier ist die Ausgabe:

This operation took 1562 ms.
This operation took 2251 ms.

Und hier ist mein Benchmark-Code:


public class IsIntegerPerformanceTest {

    public static boolean isIntegerParseInt(String str) {
        try {
            Integer.parseInt(str);
            return true;
        } catch (NumberFormatException nfe) {}
        return false;
    }

    public static boolean isIntegerRegex(String str) {
        return str.matches("^[0-9]+$");
    }

    public static void main(String[] args) {
        long starttime, endtime;
        int iterations = 1000000;
        starttime = System.currentTimeMillis();
        for (int i=0; i<iterations; i++) {
            isIntegerParseInt("123");
            isIntegerParseInt("not an int");
            isIntegerParseInt("-321");
        }
        endtime = System.currentTimeMillis();
        System.out.println("This operation took " + (endtime - starttime) + " ms.");
        starttime = System.currentTimeMillis();
        for (int i=0; i<iterations; i++) {
            isIntegerRegex("123");
            isIntegerRegex("not an int");
            isIntegerRegex("-321");
        }
        endtime = System.currentTimeMillis();
        System.out.println("This operation took " + (endtime - starttime) + " ms.");
    }
}

Beachten Sie auch, dass Ihr Regex negative Zahlen ablehnt und die parseInt-Methode diese akzeptiert.

13
Asaph

Hier ist unsere Art, dies zu tun:

public boolean isNumeric(String string) throws IllegalArgumentException
{
   boolean isnumeric = false;

   if (string != null && !string.equals(""))
   {
      isnumeric = true;
      char chars[] = string.toCharArray();

      for(int d = 0; d < chars.length; d++)
      {
         isnumeric &= Character.isDigit(chars[d]);

         if(!isnumeric)
         break;
      }
   }
   return isnumeric;
}
4
Jeremy Cron

Wenn absolute Leistung wichtig ist und Sie nur nach Ganzzahlen (nicht nach Gleitkommazahlen) suchen, vermute ich, dass die Iteration über jedes Zeichen in der Zeichenfolge, Falseingaben, wenn Sie auf etwas stoßen, das nicht im Bereich 0-9 liegt, der schnellste ist.

RegEx ist eine allgemeinere Lösung, die für diesen speziellen Fall wahrscheinlich nicht so schnell abschneidet. Eine Lösung, die eine Ausnahme auslöst, hat in diesem Fall zusätzlichen Aufwand. TryParse ist etwas langsamer, wenn Sie sich nicht wirklich um den Wert der Zahl kümmern, nur ob es sich um eine Zahl handelt oder nicht, da die Umwandlung in eine Zahl ebenfalls erfolgen muss.

Für alles andere als eine innere Schleife, die oft aufgerufen wird, sollten die Unterschiede zwischen all diesen Optionen unbedeutend sein.

3
Eric J.

Ich musste Code wie den Ihren umgestalten, um NumberFormatException loszuwerden. Der überarbeitete Code:

public static Integer parseInteger(final String str) {
    if (str == null || str.isEmpty()) {
        return null;
    }
    final Scanner sc = new Scanner(str);
    return Integer.valueOf(sc.nextInt());
}

Als Java 1.4-Typ wusste ich nichts über Java.util.Scanner. Ich habe diesen interessanten Artikel gefunden:

http://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Java

Ich mochte die Lösung mit dem Scanner sehr, sehr kompakt und trotzdem lesbar.

3
Heiner

Einige Sprachen wie C # haben TryParse (oder ein gleichwertiges), das für so etwas ziemlich gut funktioniert.

public boolean IsInteger(string value)
{
  int i;
  return Int32.TryParse(value, i);
}
2
Brandon

Ich persönlich würde das tun, wenn Sie es wirklich vereinfachen möchten.

public boolean isInteger(string myValue)
{
    int myIntValue;
    return int.TryParse(myValue, myIntValue)
}
2
Mitchel Sellers

Sie können eine Erweiterungsmethode für eine Zeichenfolge erstellen und den gesamten Prozess sauberer aussehen lassen ...

public static bool IsInt(this string str)
{
    int i;
    return int.TryParse(str, out i);
}

Sie könnten dann in Ihrem aktuellen Code Folgendes tun ...

if(myString.IsInt())....
2
RSolberg
public static boolean CheckString(String myString) {

char[] digits;

    digits = myString.toCharArray();
    for (char div : digits) {// for each element div of type char in the digits collection (digits is a collection containing div elements).
        try {
            Double.parseDouble(myString);
            System.out.println("All are numbers");
            return true;
        } catch (NumberFormatException e) {

            if (Character.isDigit(div)) {
                System.out.println("Not all are chars");

                return false;
            }
        }
    }

    System.out.println("All are chars");
    return true;
}
1
Naim

Ich mag Code:

public static boolean isIntegerRegex(String str) {
    return str.matches("^[0-9]+$");
}

Aber es ist besser, wenn Sie Pattern erstellen, bevor Sie es verwenden:

public static Pattern patternInteger = Pattern.compile("^[0-9]+$");
public static boolean isIntegerRegex(String str) {
  return patternInteger.matcher(str).matches();
}

Bewerben durch test wir haben ergebnis:

This operation isIntegerParseInt took 1313 ms.
This operation isIntegerRegex took 1178 ms.
This operation isIntegerRegexNew took 304 ms.

Mit:

public class IsIntegerPerformanceTest {
  private static Pattern pattern = Pattern.compile("^[0-9]+$");

    public static boolean isIntegerParseInt(String str) {
    try {
      Integer.parseInt(str);
      return true;
    } catch (NumberFormatException nfe) {
    }
    return false;
  }

  public static boolean isIntegerRegexNew(String str) {
    return pattern.matcher(str).matches();
  }

  public static boolean isIntegerRegex(String str) {
    return str.matches("^[0-9]+$");
  }

    public static void main(String[] args) {
        long starttime, endtime;
    int iterations = 1000000;
    starttime = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
      isIntegerParseInt("123");
      isIntegerParseInt("not an int");
      isIntegerParseInt("-321");
    }
    endtime = System.currentTimeMillis();
    System.out.println("This operation isIntegerParseInt took " + (endtime - starttime) + " ms.");
    starttime = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
      isIntegerRegex("123");
      isIntegerRegex("not an int");
      isIntegerRegex("-321");
    }
    endtime = System.currentTimeMillis();
    System.out.println("This operation took isIntegerRegex " + (endtime - starttime) + " ms.");
    starttime = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
      isIntegerRegexNew("123");
      isIntegerRegexNew("not an int");
      isIntegerRegexNew("-321");
    }
    endtime = System.currentTimeMillis();
    System.out.println("This operation took isIntegerRegexNew " + (endtime - starttime) + " ms.");
  }
}
1
Hong Xanh

Mit .NET können Sie Folgendes tun:

private bool isNumber(string str)
{
    return str.Any(c => !char.IsDigit(c));
}
1
Cleiton

Ich denke, es könnte schneller sein als bisherige Lösungen, wenn Sie Folgendes tun (Java):

public final static boolean isInteger(String in)
{
    char c;
    int length = in.length();
    boolean ret = length > 0;
    int i = ret && in.charAt(0) == '-' ? 1 : 0;
    for (; ret && i < length; i++)
    {
        c = in.charAt(i);
        ret = (c >= '0' && c <= '9');
    }
    return ret;
}

Ich habe den gleichen Code ausgeführt wie Asaph und das Ergebnis war:

Dieser Vorgang dauerte 28 ms.

Ein großer Unterschied (gegen 1691 ms und 2049 ms auf meinem Computer). Berücksichtigen Sie, dass diese Methode nicht überprüft wird, wenn die Zeichenfolge NULL ist. Sie sollten dies also vorher tun (einschließlich der Zeichenfolgenbeschneidung).

1
Barenca

Das ist meine Implementierung, um zu überprüfen, ob eine Zeichenfolge aus Ziffern besteht:

public static boolean isNumeric(String string)
{
    if (string == null)
    {
        throw new NullPointerException("The string must not be null!");
    }
    final int len = string.length();
    if (len == 0)
    {
        return false;
    }
    for (int i = 0; i < len; ++i)
    {
        if (!Character.isDigit(string.charAt(i)))
        {
            return false;
        }
    }
    return true;
}
1
pschichtel

Ich denke, den Leuten hier fehlt ein Punkt. Die wiederholte Verwendung desselben Musters ist sehr einfach zu optimieren. Verwenden Sie einfach ein Singleton des Patterns. In all meinen Tests hat der Try-Catch-Ansatz niemals einen besseren Benchmark als der Pattern-Ansatz. Bei einem Erfolgstest benötigt Try-Catch die doppelte Zeit, bei einem Fehlertest ist es sechsmal langsamer.

public static final Pattern INT_PATTERN= Pattern.compile("^-?[0-9]+(\\.[0-9]+)?$");

public static boolean isInt(String s){
  return INT_PATTERN.matcher(s).matches();
}
1
user1844655

Ich habe diese Klasse gerade zu meinen Utensilien hinzugefügt:

public class TryParseLong {
private boolean isParseable;

private long value;

public TryParseLong(String toParse) {
    try {
        value = Long.parseLong(toParse);
        isParseable = true;
    } catch (NumberFormatException e) {
        // Exception set to null to indicate it is deliberately
        // being ignored, since the compensating action
        // of clearing the parsable flag is being taken.
        e = null;

        isParseable = false;
    }
}

public boolean isParsable() {
    return isParseable;
}

public long getLong() {
    return value;
}
}

Um es zu benutzen:

TryParseLong valueAsLong = new TryParseLong(value);

if (valueAsLong.isParsable()) {
    ...
    // Do something with valueAsLong.getLong();
} else {
    ...
}

Dadurch wird der Wert nur einmal analysiert.

Die Ausnahmebedingung und der Steuerfluss werden immer noch durch Ausnahmen verwendet, aber zumindest kapselt sie diese Art von Code in einer Dienstprogrammklasse, und Code, der sie verwendet, kann auf normalere Weise funktionieren.

Das Problem bei Java im Vergleich zu C # ist, dass C # aus Werte besteht und als Referenz übergeben wird, sodass es effektiv zwei Informationen zurückgeben kann. Die Markierung zeigt an, dass etwas analysiert werden kann oder nicht, und der tatsächliche analysierte Wert. Wenn wir in Java einen Wert von mehr als 1 erstellen, müssen wir ein Objekt erstellen, um sie zu speichern. Daher habe ich diesen Ansatz gewählt und die Flagge und den geparsten Wert in ein Objekt eingefügt.

Bei der Escape-Analyse wird dies wahrscheinlich effizient gehandhabt und der Wert und das Flag auf dem Stapel erstellt. Dieses Objekt wird niemals auf dem Heap erstellt. Ich denke, dies hat einen minimalen Einfluss auf die Leistung.

Meines Erachtens ergibt sich daraus ein optimaler Kompromiss zwischen dem Beherrschen des Steuerungsflusses durch Ausnahme, einer guten Leistung und dem nicht langen Analysieren der Ganzzahl.

0
user2800708

Verwenden Sie für lange Zahlen Folgendes: (Java)

public static boolean isNumber(String string) {
    try {
        Long.parseLong(string);
    } catch (Exception e) {
        return false;
    }
    return true;
}
0
ssamuel68

Ich benutze das, aber ich mochte Asaphs Strenge in seinem Posten.

public static bool IsNumeric(object expression)
{
if (expression == null)
return false;

double number;
return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture),   NumberStyles.Any,
NumberFormatInfo.InvariantInfo, out number);
}
0
Daver
 public static boolean isNumber(String str){
      return str.matches("[0-9]*\\.[0-9]+");
    }

um zu prüfen, ob number (einschließlich float, integer) oder nicht

0
Wong edan

public static boolean CheckIfNumber (String-Nummer) {

    for(int i = 0; i < number.length(); i++){
        try{
            Double.parseDouble(number.substring(i));

        }catch(NumberFormatException ex){
            return false;
        }
    }
    return true;     
}

Ich hatte dieses Problem schon einmal, aber wenn ich eine Zahl und dann einen Buchstaben eingegeben hatte, würde es immer noch wahr sein. Ich denke, das ist der bessere Weg, es zu tun. Prüfen Sie einfach, ob jedes Zeichen eine Zahl ist. Etwas länger, aber es ist Vorsicht geboten, wenn der Benutzer "1abc" eingibt. Aus irgendeinem Grund, als ich versuchte zu fangen, ohne zu iterieren, dachte es immer noch, dass es eine Zahl war.

0
Daniel Kim

Eine modifizierte Version meiner vorherigen Antwort:

public static boolean isInteger(String in)
{
    if (in != null)
    {
        char c;
        int i = 0;
        int l = in.length();
        if (l > 0 && in.charAt(0) == '-')
        {
            i = 1;
        }
        if (l > i)
        {
            for (; i < l; i++)
            {
                c = in.charAt(i);
                if (c < '0' || c > '9')
                    return false;
            }
            return true;
        }
    }
    return false;
}
0
Barenca