Быстрый способ преобразовать строку в двойном десятичном формате, не зная, что это за формат

#java #kotlin

#java #kotlin

Вопрос:

У меня есть этот код на языке kotlin:

 val stringDecimalvalue = ... // can come as '#,###.##' or '#.###,##' and the two are valid
  

Я хочу создать Double с этим String таким образом

 var monetaryValue = Double(stringDecimalvalue)
  

Если значение находится в #,###.## Будет поднят формат a NumberFormatException , тогда мне нужно знать этот шаблон перед форматированием.

Есть способ, при котором мне не нужно прокручивать всю строку или применять регулярное выражение с совпадением? (или, может быть, легкий подход с регулярными выражениями)

Спасибо!

Комментарии:

1. Просто выполните синтаксический анализ явно с обоими форматами. Вы могли бы написать свой собственный синтаксический анализатор, чтобы сделать его немного более эффективным, но я не могу представить, что это узкое место в любом приложении.

2. Как вы хотите проанализировать число, если вы не знаете, какой символ является десятичной точкой, а какой разделителем тысяч? Есть ли способ отличить одно от другого на основе формата, в котором находится число?

3. Я ожидал, что что-то вроде этого сработает @Voo Double(DecimalFormat("0.#####").format("1,000.00")) , но возвращает `IllegalArgumentException’, в котором говорится, что это недопустимое число

4. Если сначала получить запятую, а затем точку, я знаю, что это в одном, а обратное верно для другого, но мне это не нравится, потому что мне нужно выполнить проверку других отсюда @DodgyCodeException

5. Как насчет такого числа, как 1.000 ? Это тысяча или число от 1 до трех знаков после запятой? Если вы заранее не знаете, какой формат ожидать, будут некоторые числа, которые вы не сможете разобрать однозначно.

Ответ №1:

мне кажется, что самый простой способ найти последнее появление , or . и удалить все появления . и . перед этим, тогда вы сможете использовать Double::parseString

Ответ №2:

Во-первых, я должен сказать, что не имеет смысла подход функции общего назначения propose без явного формата, потому что, как и комментарии @gidds и @DodgyCodeException, он подвержен двусмысленности.

Таким образом, я решаю проблему, используя regex разделитель десятичных знаков, сгруппированный запятыми [0-9] (,[0-9]{3})*\.[0-9]{2} , и перевернутый регистр [0-9] (\.[0-9]{3})*,[0-9]{2} (разделитель запятой, сгруппированный точками). Затем уберите запятые и замените точки, чтобы получить десятичный формат без сгруппированных точек, который я хочу проанализировать Double

Я делаю это, как extension function :

 const val NOTHING = ""
const val COMMA = ","
const val POINT = "."

val commaGrouped = "[0-9] (,[0-9]{3})*\.[0-9]{2}".toRegex()
val pointGrouped = "[0-9] (\.[0-9]{3})*,[0-9]{2}".toRegex()

fun String.commaCleaned() = replace(COMMA, NOTHING)

fun String.pointCleaned() = replace(POINT, NOTHING)

fun String.commaToPoint() = replace(COMMA, POINT)

fun String.pointToComma() = replace(POINT, COMMA)


fun String.toDouble() =
    when {
        commaGrouped matches this -> Double(commaCleaned())
        pointGrouped matches this -> Double(pointCleaned().commaToPoint())
        else -> throw IllegalArgumentException(
            "the value ${this} can't be converted to ${Double::class.java} "  
                "have match ${commaGrouped.pattern} "  
                "or ${pointGrouped.pattern}")
    }
  

поймите, что целочисленный формат или точность более двух знаков после запятой не будут соответствовать для создания Double , и это то, чего я тоже хочу.