2014-05-08 10 views
6

Was wäre ein schneller ein sicherer Weg, um einen String in einen numerischen Typ zu konvertieren, während ein Standardwert bereitgestellt wird, wenn die Konvertierung fehlschlägt?Schnelle und sichere Umwandlung von String in numerische Typen

ich die in der Regel empfohlen, versucht mit, das heißt mit Ausnahmen:

implicit class StringConversion(val s: String) { 

    private def toTypeOrElse[T](convert: String=>T, defaultVal: T) = try { 
    convert(s) 
    } catch { 
    case _: NumberFormatException => defaultVal 
    } 

    def toShortOrElse(defaultVal: Short = 0) = toTypeOrElse[Short](_.toShort, defaultVal) 
    def toByteOrElse(defaultVal: Byte = 0) = toTypeOrElse[Byte](_.toByte, defaultVal) 
    def toIntOrElse(defaultVal: Int = 0) = toTypeOrElse[Int](_.toInt, defaultVal) 
    def toDoubleOrElse(defaultVal: Double = 0D) = toTypeOrElse[Double](_.toDouble, defaultVal) 
    def toLongOrElse(defaultVal: Long = 0L) = toTypeOrElse[Long](_.toLong, defaultVal) 
    def toFloatOrElse(defaultVal: Float = 0F) = toTypeOrElse[Float](_.toFloat, defaultVal) 
} 

dieses Utility-Klasse verwenden, kann ich jetzt einfach eine beliebige Zeichenfolge in einer bestimmten numerischen Typ konvertieren, und einen Standardwert für den Fall bieten die String korrekt den numerischen Typ ist nicht darstellt:

scala> "123".toIntOrElse() 
res1: Int = 123 
scala> "abc".toIntOrElse(-1) 
res2: Int = -1 
scala> "abc".toIntOrElse() 
res3: Int = 0 
scala> "3.14159".toDoubleOrElse() 
res4: Double = 3.14159 
... 

Während es funktioniert wunderbar, dieser Ansatz scheint nicht gut zu skalieren, wahrscheinlich wegen der Ausnahmen Mechanismus:

scala> for (i<-1 to 10000000) "1234".toIntOrElse() 

dauert etwa 1 Sekunde auszuführen, während

scala> for (i<-1 to 10000000) "abcd".toIntOrElse() 

grob 1 Minute dauert!

Ich denke, ein anderer Ansatz wäre es, zu vermeiden, dass Ausnahmen von den Methoden toInt, toDouble, ... ausgelöst werden.

Konnte dies erreicht werden, indem geprüft wird, ob ein String "vom gegebenen Typ" ist? Man könnte natürlich die String-Zeichen durchlaufen und prüfen, ob es sich um Ziffern handelt (siehe z. B. this example), aber was ist dann mit den anderen numerischen Formaten (double, float, hex, octal, ...)?

+1

Regex ist wahrscheinlich der beste Weg, um hier zu gehen, wenn Sie die Overhea vollständig vermeiden wollen d der Versuch/Fang-Semantik. Sie müssen nur für jeden der möglichen numerischen Typen, von denen Sie konvertieren möchten, Regexes erstellen. Aber ehrlich gesagt, das ist wahrscheinlich eine vorzeitige Optimierung. Wie schnell muss dieser Code sein? Wie oft wird es getroffen? Wie oft wird es ungültige Zahlen bekommen, die den Catch-Block treffen? Diese Fragen müssen Sie sich vor der Optimierung stellen, da der Code ein wenig komplexer wird. – cmbaxter

+0

@cmbaxter Ich stimme dir zu, aber ich benutze dies in einem Big Data-Kontext, in dem ich riesige CSV-Dateien analysiere (Milliarden von Zeilen), also ist es wichtig. – borck

+0

Fair genug. Dann würde ich mit Regex gehen, um die Saite zuerst zu untersuchen. Wird viel schneller sein. – cmbaxter

Antwort

1

Als erster Ansatz, filtern diese Eingabezeichenfolgen aus, die keine Ziffer enthalten

private def toTypeOrElse[T](convert: String=>T, defaultVal: T) = try { 
    if (s.contains("[0-9]")) convert(s) { 
    else defaultVal 
    } catch { 
    case _: NumberFormatException => defaultVal 
    } 
} 

aktualisieren

Angereichert Satz von Zeichen, die von in einem numerischen Wert, noch keine Reihenfolge auftreten können Vorkommen oder Grenzen in der Wiederholung berücksichtigt,

private def toTypeOrElse[T](convert: String=>T, defaultVal: T) = try { 
    if (s matches "[\\+\\-0-9.e]+") convert(s) 
    else defaultVal 
    } catch { 
    case _: NumberFormatException => defaultVal 
    } 
} 
+0

Warum nicht diejenigen ausfiltern, die nicht nur Ziffern oder ein '-' enthalten? – Kigyo

Verwandte Themen