2012-03-03 3 views
39

Załóżmy, że mam ciąg znaków w scala i chcę spróbować przetworzyć podwójne z niego.Jak ustalić, czy ciąg Scala można parsować jako podwójne, czy nie?

Wiem, że mogę po prostu zadzwonić pod numer toDouble, a następnie złapać wyjątek formatu liczbowego w java, jeśli to się nie powiedzie, ale czy istnieje lepszy sposób na zrobienie tego? Na przykład, jeśli istnieje funkcja parseDouble, która zwróciła Option[Double], to kwalifikuje się.

Nie chcę umieszczać tego w moim własnym kodzie, jeśli już istnieje w standardowej bibliotece i po prostu szukam go w niewłaściwym miejscu.

Dziękujemy za pomoc.

Odpowiedz

42

Albo po prostu

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

Fancy wersja:

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

Powinieneś naprawdę złapać 'NonFatal (_)', a nie tylko "_". – rightfold

+1

Powinieneś raczej użyć funkcji 'Try'. Zobacz odpowiedź @Jeff Schwab. – Moebius

6

Nic podobnego nie tylko w Scali, ale nawet w podstawowej Javie.

Oto kod kawałek, który robi to bez wyjątków, choć:

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 
} 

Zastosowanie:

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

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

Możesz spróbować użyć opcji util.control.Exception.catching, która zwraca typ Either.

Więc przy użyciu następujących zwraca Lewy owijania NumberFormatException lub Prawy owijania Double

import util.control.Exception._ 

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

Scalaz zapewnia metodę rozszerzenia parseDouble na String s, co daje wartość typu 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") 

Możesz go przekonwertować na Option, jeśli jest to wymagane.

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

Jak wydajność tego porównania w porównaniu do domyślnej implementacji biblioteki zapakowane w 'Try()'? – NightWolf

+0

@NightWolf, 'Try' nie istniał w chwili, gdy pisałem tę odpowiedź. :-) W tym kontekście oba podejścia działają w ten sam sposób, a wydajność powinna być taka sama. – missingfaktor

+0

@NightWolf, myślę, że Scalaz od tamtej pory zniszczył 'Validation' i wprowadził inny typ z podobnymi, ale więcej właściwościami dźwięku. Mogę się mylić. Nie używałem go od jakiegoś czasu. – missingfaktor

5

Niestety tego nie ma w standardowej bibliotece. Oto, czego używam:

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

bym zwykle iść z „zamiast” Spróbuj:

uwagi, że można zrobić dalsze obliczenia w za i każdy wyjątek projekt w błąd (ex). AFAIK to idiomatyczny sposób obsługi sekwencji niewiarygodnych operacji.

+0

Opps, dziękuję za wskazanie, poprawiono =) – Maverick

+1

Mogę się mylić, ponieważ jestem nowy w scala, ale wierzę, że aby to skompilować, trzeba: a) importować scala.util. {Spróbuj, sukces, niepowodzenie} i dodaj słowo "case" przed słowami "Success" i "Failure" –

Powiązane problemy