2012-03-03 7 views
39

Supongamos que tengo una cadena en scala y quiero intentar analizar un doble fuera de ella.¿Cómo encontrar si una cadena Scala es analizable como un doble o no?

Lo sé, solo puedo llamar al toDouble y luego tomar la excepción java num format si esto falla, pero ¿hay una manera más clara de hacerlo? Por ejemplo, si hubiera una función parseDouble que devolviera Option[Double], esto calificaría.

No quiero poner esto en mi propio código si ya existe en la biblioteca estándar y solo lo estoy buscando en el lugar equivocado.

Gracias por cualquier ayuda que pueda proporcionar.

Respuesta

42

O simplemente

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

de lujo:

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

Realmente debería capturar 'NonFatal (_)', no solo '_'. – rightfold

+1

Debería usar la función 'Try'. Ver la respuesta de @Jeff Schwab. – Moebius

6

No hay nada como esto, no solo en Scala, sino incluso en Java básico.

Aquí hay un código de pieza que lo hace sin excepciones, sin embargo:

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 
} 

Uso:

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

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

Puede intentar usar util.control.Exception.catching que devuelve un tipo Either.

Así, utilizando la siguiente devuelve una izquierda envolviendo una NumberFormatException o un derecho envolver una Double

import util.control.Exception._ 

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

Scalaz proporciona un método de extensión parseDouble en String s, lo que da un valor de 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") 

Puede convertirlo a Option si es necesario.

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

¿Cómo se compara el rendimiento de esto con la implementación de la biblioteca por defecto en un 'Try()'? – NightWolf

+0

@NightWolf, 'Try' no existía en el momento en que escribí esta respuesta. :-) En este contexto, ambos enfoques funcionan de la misma manera, y el rendimiento debería ser el mismo. – missingfaktor

+0

@NightWolf, creo que Scalaz ya eliminó 'Validation' e introdujo otro tipo con propiedades similares pero con más sonido. Sin embargo, podría estar equivocado. No lo he usado por un tiempo. – missingfaktor

5

Lamentablemente, esto no está en la biblioteca estándar. Esto es lo que utilizar:

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

lo general iría con un intento "en su lugar":

Nota, que puede hacer el cálculo adicional en el para y cualquier excepción sería proyecto en una falla (ex). AFAIK esta es la forma idiomática de manejar una secuencia de operaciones poco confiables.

+0

Opps, gracias por señalar, corregido =) – Maverick

+1

Puedo estar equivocado ya que soy nuevo en scala, pero creo que para hacer esta compilación uno tiene que a) importar scala.util. {Try, Success, Failure} y agregue la palabra 'caso' antes de las palabras 'éxito' y 'fracaso' –

Cuestiones relacionadas