Проверьте, является ли строка типом int, long, float, boolean или double - PullRequest
0 голосов
/ 03 мая 2020

Я хочу проверить, является ли строка типом int, long, float, double или boolean.

Например, «1» должно быть int, «1.22» должно быть float и «1.5555555» должно быть double.

Это лучшее, что мне удалось:

case item: String =>

        if (item.toInt.toString.equals(item)) {
          arrayType = Int.getClass
          ByteBuffer.allocate(4 * array.length)
        }
        else if (item.toLong.toString.equals(item)) {
          arrayType = Long.getClass
          ByteBuffer.allocate(8 * array.length)
        }
        else if (item.toFloat.toString.equals(item)) {
          arrayType = Float.getClass
          ByteBuffer.allocate(4 * array.length)
        }
        else if (item.toDouble.toString.equals(item)) {
          arrayType = Double.getClass
          ByteBuffer.allocate(8 * array.length)
        }
        else if (item.toBoolean.toString.equals(item)) {
          arrayType = Boolean.getClass
          ByteBuffer.allocate(array.length)
        }

        else throw new UnsupportedOperationException("Type not supported: " + item.getClass)

Ответы [ 2 ]

4 голосов
/ 03 мая 2020

Я не понимаю, как может работать опубликованный код. Если item равно "true", то .toInt сгенерирует и никогда не доберется до теста .toBoolean.

У меня возникнет соблазн использовать RegEx для выполнения начальной сегрегации и позволить BigDecimal выполнить разбор номера.

val isBool = "(?i)(?:true|false)".r
val isNum = raw"\d*\.?\d+".r

item match {
  case isBool() => ...
  case isNum() =>
    val bd = BigDecimal(item)
    if      (bd.isValidInt)      ...
    else if (bd.isValidLong)     ...
    else if (bd.isDecimalFloat)  ...
    else if (bd.isDecimalDouble) ...
    else //too big to fit?
  case _ => //report bad item
}
1 голос
/ 04 мая 2020

Предыдущий ответ будет работать, хотя он не охватывает все случаи. Например, цифры с суффиксом f или d могут анализироваться в Double или Float, как и некоторые строки, такие как "Infinity" или "-Infinity". Например, "12.1234567893901f".toFloat -> 12.123457 и "NaN".toFloat -> NaN. Scientifi c или "E" нотация также разбирается в Float / Doubles (становится ±Infinity при необходимости).

  • item.getClass всегда будет String в строка выше, поэтому сообщение об ошибке «Тип не поддерживается» может вводить в заблуждение, и вы можете предпочесть IllegalArgumentException с сообщением, которое показывает item вместо item.getClass.
  • Проверка s.toXXX.toString.equals(s) выиграно ' t работает для значений s, которые могут успешно анализироваться, но "не упрощены". Один случай - длинная цепочка цифр: "61234817390131412313458".toDouble.toString = "6.123481739013142E22". То же самое относится и к другим «не упрощенным» значениям, например, "+0".toFloat.toString = "0.0"
  • . Как упоминалось в комментариях и предыдущих ответах, каждый метод toXXX может выдавать ошибку, поэтому, чтобы попробовать все из них, их можно обернуть в Try. Метод find для List остановится и вернет первый элемент, который создает isSuccess, равный true.
  • Если s.toFloat не выдаст ошибку, то s.toDouble не выдаст ошибку, и наоборот. Оба должны преуспеть или потерпеть неудачу вместе. Следовательно, необходимо выполнить дополнительные проверки, чтобы определить, какой из них более подходит (но, как уже упоминалось, toString.equals слишком специфично c).
    • Определенные (очень положительные или отрицательные) значения становятся ±Infinity при разборе на число с плавающей точкой, но не в два раза. Если ввод вводится в Infinity для числа с плавающей запятой, но не в Double, тогда вы можете предпочесть Double. Если входной сигнал анализируется на Infinity для обоих типов, выберите любой из них.
    • Слишком малые входы будут обнулены раньше для Float, чем для Double.

Вот краткий обзор возможных определений функций:

object t {
  type Result = (Int, Class[_])
  type ListElt = (Result, String => Any)
  def useFloat(s:String): Boolean = {
    // determine if choosing Float is "desirable"
    val floatVal:Float = s.toFloat
    val doubleVal:Double = s.toDouble
    // if very little precision is lost, or if the maximum information stored isn't lost completely
    val MAX_LOST:Double = 1E-5.min(doubleVal)
    val preservedPrecision:Boolean = (floatVal - doubleVal).abs <= MAX_LOST
    // Remove this variable if `Double` is preferred when bothInfinite
    val bothInfinite:Boolean = floatVal.isInfinite && doubleVal.isInfinite
    preservedPrecision || bothInfinite
  }
  def getSizeAndType(s: String): Option[Result] = {
    val floatResult:Result = (4, Float.getClass)
    val doubleResult:Result = (8, Double.getClass)
    val conversions: List[ListElt] = List(
      ((4, Int.getClass), ((x: String) => x.toInt)),
      ((8, Long.getClass), ((x: String) => x.toLong)),
      (floatResult, ((x: String) => x.toFloat)),
      (doubleResult, ((x: String) => x.toDouble)),
      ((1, Boolean.getClass), ((x: String) => x.toBoolean))
    )
    val firstSuccess: Option[ListElt] = conversions.find((elt: ListElt) => scala.util.Try(elt._2(s)).isSuccess)
    val result = firstSuccess.map(_._1)
    // check if choosing Float is "desirable"
    result match {
      case Some(`floatResult`) =>
        if (useFloat(s)){
          Some(floatResult)
        } else {
          Some(doubleResult)
        }
      case other => other
    }
  }
  def handle(s:String) = {
    val (bytes, arrayType) = getSizeAndType(s).getOrElse(0, "None")
    if (bytes > 0) {
      // perform allocation
      //ByteBuffer.allocate(4 * array.length)
      println(s"${bytes}, ${arrayType}")
    } else {
      // throw exception, etc.
      println("Not parsable")
    }
  }
}

println(t.handle("9")) // 4, class scala.Int$
println(t.handle("1.9")) // 4, class scala.Float$
println(t.handle("2147483648")) // 8, class scala.Long$
println(t.handle("2.5769803776E9")) // 8, class scala.Double$ (small enough for finite float but loses enough precision)
println(t.handle("3.4028235E38")) // 8, class scala.Double$ (ditto)
println(t.handle("6.805647E38")) // 8, class scala.Double$ (too big for finite float)
println(t.handle("12.123456789")) // 4, class scala.Float$
println(t.handle("Infinity")) // 4, class scala.Float$
println(t.handle("false")) // 1, class scala.Boolean$
println(t.handle("xyz")) // Not parsable
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...