This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 2.1, “Parsing a Number from a String.”
Problem
You want to convert a String
to one of Scala’s numeric types (Byte
, Double
, Int
, Float
, Long
, Short
).
Solution
Use the to*
methods that are available on a String
(courtesy of the StringLike
trait):
scala> "100".toInt
res0: Int = 100
scala> "100".toDouble
res1: Double = 100.0
scala> "100".toFloat
res2: Float = 100.0
scala> "1".toLong
res3: Long = 1
scala> "1".toShort
res4: Short = 1
scala> "1".toByte
res5: Byte = 1
Be careful because these methods can throw the usual Java NumberFormatException
:
scala> "foo".toInt
java.lang.NumberFormatException: For input string: "foo"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:449)
... more output here ...
BigInt
and BigDecimal
instances can also be created directly from strings (and can also throw a NumberFormatException
):
scala> val b = BigInt("1")
b: scala.math.BigInt = 1
scala> val b = BigDecimal("3.14159")
b: scala.math.BigDecimal = 3.14159
Handling a base and radix
If you need to perform calculations using bases other than 10, you’ll find the toInt
method in the Scala Int
class doesn’t have a method that lets you pass in a base and radix. To solve this problem, use the parseInt
method in the java.lang.Integer class, as shown in these examples:
scala> Integer.parseInt("1", 2)
res0: Int = 1
scala> Integer.parseInt("10", 2)
res1: Int = 2
scala> Integer.parseInt("100", 2)
res2: Int = 4
scala> Integer.parseInt("1", 8)
res3: Int = 1
scala> Integer.parseInt("10", 8)
res4: Int = 8
If you’re a fan of implicit conversions, you can create an implicit class and method to help solve the problem. As described in Recipe 1.11, “Add Your Own Methods to the String Class,” create the implicit conversion as follows:
implicit class StringToInt(s: String) {
def toInt(radix: Int) = Integer.parseInt(s, radix)
}
Defining this implicit class (and bringing it into scope) adds a toInt
method that takes a radix argument to the String
class, which you can now call instead of calling Integer.parseInt
:
scala> implicit class StringToInt(s: String) {
| def toInt(radix: Int) = Integer.parseInt(s, radix)
| }
defined class StringToInt
scala> "1".toInt(2)
res0: Int = 1
scala> "10".toInt(2)
res1: Int = 2
scala> "100".toInt(2)
res2: Int = 4
scala> "100".toInt(8)
res3: Int = 64
scala> "100".toInt(16)
res4: Int = 256
See Recipe 1.11 for more details on how to implement this solution outside of the REPL.
Discussion
If you’ve used Java to convert a String
to a numeric data type, the NumberFormatException
is familiar. However, Scala doesn’t have checked exceptions, so you’ll probably want to handle this situation differently.
First, you don’t have to declare that Scala methods can throw an exception, so it’s perfectly legal to declare a Scala method like this:
// not required to declare "throws NumberFormatException"
def toInt(s: String) = s.toInt
If you’re going to allow an exception to be thrown like this, callers of your method might appreciate knowing that this can happen. Consider adding a Scaladoc comment to your method in this case.
If you prefer to declare that your method can throw an exception, mark it with the @throws
annotation, as shown here:
@throws(classOf[NumberFormatException])
def toInt(s: String) = s.toInt
This approach is required if the method will be called from Java code, as described in Recipe 19.2., “Add Exception Annotations to Scala Methods to Work with Java.” However, in Scala, situations like this are often handled with the “Option/Some/None” pattern, as described in Recipe 20.6. With this approach, define the toInt
method like this:
def toInt(s: String):Option[Int] = {
try {
Some(s.toInt)
} catch {
case e: NumberFormatException => None
}
}
or this:
import scala.util.control.Exception._ def makeInt(s: String): Option[Int] = allCatch.opt(s.toInt)
Now you can call the toInt
method in several different ways, depending on your needs. The preferred approach is to use a match
expression. You can write a match
expression to print the toInt
result like this:
toInt(aString) match {
case Some(n) => println(n)
case None => println("Boom! That wasn't a number.")
}
You can also write it as follows to assign the result to a variable:
val result = toInt(aString) match {
case Some(x) => x
case None => 0 // however you want to handle this
}
If these examples haven’t yet sold you on the Option/Some/None approach, you’ll see in the Collections chapter that this pattern is incredibly helpful and convenient when working with collections.
You can also use getOrElse
:
println(toInt("1").getOrElse(0)) // 1
println(toInt("a").getOrElse(0)) // 0
// assign the result to x
val x = toInt(aString).getOrElse(0)
Alternatives to Scala’s Option
If you like the Option/Some/None concept, but need access to the exception information, there are several additional possibilities:
- Try, Success, and Failure (introduced in Scala 2.10)
- Either, Left, and Right
Here are examples of how to use allCatch
in Scala with the Option
, Try
, and Either
classes:
import scala.util.control.Exception._
import scala.util.{Try, Success, Failure}
// Option
def toInt(s: String): Option[Int] = allCatch.opt(Integer.parseInt(s))
// Try
def toInt(s: String): Try[Int] = allCatch.withTry(Integer.parseInt(s))
// Either
def toInt(s: String): Either[Throwable, Int] = allCatch.either(Integer.parseInt(s))
See Also
- Recipe 20.6, How to Use Scala’s Option/Some/None Pattern
- The Scala StringLike trait
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |