2012-03-03 13 views
39

Supponiamo di avere una stringa in scala e voglio provare ad analizzarne una doppia.Come scoprire se una stringa di Scala è analizzabile come doppia o no?

Lo so, posso solo chiamare toDouble e quindi rilevare l'eccezione del formato numero java se questo non riesce, ma c'è un modo più pulito per fare questo? Ad esempio, se esiste una funzione parseDouble che restituisce Option[Double], ciò si qualificherebbe.

Non voglio inserirlo nel mio codice se esiste già nella libreria standard e lo sto cercando nel posto sbagliato.

Grazie per l'aiuto che puoi fornire.

risposta

42

O semplicemente

versione
def parseDouble(s: String) = try { Some(s.toDouble) } catch { case _ => None } 

Fancy:

case class ParseOp[T](op: String => T) 
implicit val popDouble = ParseOp[Double](_.toDouble) 
implicit val popInt = ParseOp[Int](_.toInt) 
// etc. 
def parse[T: ParseOp](s: String) = try { Some(implicitly[ParseOp[T]].op(s)) } 
            catch {case _ => None} 

scala> parse[Double]("1.23") 
res13: Option[Double] = Some(1.23) 

scala> parse[Int]("1.23") 
res14: Option[Int] = None 

scala> parse[Int]("1") 
res15: Option[Int] = Some(1) 
+3

Si dovrebbe prendere veramente 'NonFatal (_)', non solo '_'. – rightfold

+1

Dovresti piuttosto usare la funzione 'Prova'. Vedi la risposta di @Jeff Schwab. – Moebius

6

Non c'è niente di simile non solo in Scala, ma anche in Java di base.

Ecco un codice pezzo che lo fa senza eccezioni, però:

def parseDouble(s: String)(implicit nf: NumberFormat) = { 
    val pp = new ParsePosition(0) 
    val d = nf.parse(s, pp) 
    if (pp.getErrorIndex == -1) Some(d.doubleValue) else None 
} 

Usage:

implicit val formatter = NumberFormat.getInstance(Locale.ENGLISH) 

Console println parseDouble("184.33") 
Console println parseDouble("hello, world") 
10

Si potrebbe provare a utilizzare util.control.Exception.catching che restituisce un tipo Either.

Quindi, utilizzando il seguente restituisce avvolgendo una sinistra un NumberFormatException o un diritto avvolgendo un Double

import util.control.Exception._ 

catching(classOf[NumberFormatException]) either "12.W3".toDouble 
28

Scalaz fornisce un metodo di estensione parseDouble su String s, che dà un valore di tipo Validation[NumberFormatException, Double].

scala> "34.5".parseDouble 
res34: scalaz.Validation[NumberFormatException,Double] = Success(34.5) 

scala> "34.bad".parseDouble 
res35: scalaz.Validation[NumberFormatException,Double] = Failure(java.lang.NumberFormatException: For input string: "34.bad") 

È possibile convertirlo in Option se necessario.

scala> "34.bad".parseDouble.toOption 
res36: Option[Double] = None 
+0

Come si confronta la performance di questo con l'implementazione della libreria predefinita avvolta in un 'Try()'? – NightWolf

+0

@NightWolf, 'Try' non esisteva al momento in cui ho scritto questa risposta. :-) In questo contesto, entrambi gli approcci funzionano allo stesso modo e le prestazioni dovrebbero essere le stesse. – missingfaktor

+0

@NightWolf, penso che Scalaz abbia eliminato "Validation" e introdotto un altro tipo con proprietà simili ma più valide. Potrei sbagliarmi però. Non l'ho usato da un po '. – missingfaktor

5

Purtroppo, questo non è nella libreria standard. Ecco quello che ho usare:

class SafeParsePrimitive(s: String) { 
    private def nfe[T](t: => T) = { 
    try { Some(t) } 
    catch { case nfe: NumberFormatException => None } 
    } 
    def booleanOption = s.toLowerCase match { 
    case "yes" | "true" => Some(true) 
    case "no" | "false" => Some(false) 
    case _ => None 
    } 
    def byteOption = nfe(s.toByte) 
    def doubleOption = nfe(s.toDouble) 
    def floatOption = nfe(s.toFloat) 
    def hexOption = nfe(java.lang.Integer.valueOf(s,16)) 
    def hexLongOption = nfe(java.lang.Long.valueOf(s,16)) 
    def intOption = nfe(s.toInt) 
    def longOption = nfe(s.toLong) 
    def shortOption = nfe(s.toShort) 
} 
implicit def string_parses_safely(s: String) = new SafeParsePrimitive(s) 
17
scala> import scala.util.Try 
import scala.util.Try 

scala> def parseDouble(s: String): Option[Double] = Try { s.toDouble }.toOption 
parseDouble: (s: String)Option[Double] 

scala> parseDouble("3.14") 
res0: Option[Double] = Some(3.14) 

scala> parseDouble("hello") 
res1: Option[Double] = None 
1

mi piacerebbe di solito andare con un "a posto" Prova:

nota, che si può fare ulteriori calcoli nella e qualsiasi eccezione sarebbe progetto in un fallimento (ex). AFAIK questo è il modo idiomatico di gestire una sequenza di operazioni inaffidabili.

+0

Opps, grazie per avermelo segnalato, corretto =) – Maverick

+1

Potrei sbagliarmi dato che sono v nuovo di scala ma credo che per fare questo compili si abbia a) import scala.util. {Try, Success, Failure} e aggiungi la parola 'caso' prima delle parole 'Success' e 'Failure' –

Problemi correlati