У меня есть разные строки, некоторые вроде «45», некоторые вроде «45 пикселей». Как мне преобразовать их в число 45?
"9"
в 9
это самое лучшее , что работал для меня (Integer. "9")
.
У меня есть разные строки, некоторые вроде «45», некоторые вроде «45 пикселей». Как мне преобразовать их в число 45?
"9"
в 9
это самое лучшее , что работал для меня (Integer. "9")
.
Ответы:
Это будет работать на 10px
илиpx10
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
он будет анализировать первую непрерывную цифру только так
user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10
Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
Мне больше нравится ответ снроботов. В этом простом случае использование метода Java проще и надежнее, чем использование строки чтения. Я сделал пару небольших изменений. Поскольку автор не исключил отрицательные числа, я скорректировал его, чтобы разрешить отрицательные числа. Я также сделал так, чтобы число начиналось с начала строки.
(defn parse-int [s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
Кроме того, я обнаружил, что Integer / parseInt анализирует как десятичное, когда не задано основание, даже если есть ведущие нули.
Во-первых, чтобы проанализировать только целое число (поскольку это хит в Google и хорошая справочная информация):
Вы можете использовать ридер :
(read-string "9") ; => 9
Вы можете проверить, что это номер после прочтения:
(defn str->int [str] (if (number? (read-string str))))
Я не уверен, можно ли доверять пользовательскому вводу читателю clojure, чтобы вы могли проверить его перед чтением:
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
Думаю, я предпочитаю последнее решение.
А теперь к вашему конкретному вопросу. Чтобы проанализировать что-то, что начинается с целого числа, например 29px
:
(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
if
должны быть a, when
поскольку в ваших fns нет блока else.
read-string
интерпретирует их как восьмеричные: (read-string "08")
выдает исключение. Integer/valueOf
обрабатывает их как десятичные: (Integer/valueOf "08")
оценивает до 8.
read-string
выдает исключение, если вы указываете ему пустую строку или что-то вроде «29px»
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
конструктор Integer, а не Integer. Класс Integer кэширует значения от -128 до 127, чтобы минимизировать создание объекта. Документ Integer Javadoc описывает это так же, как и этот пост: stackoverflow.com/a/2974852/871012
Для меня это работает в ответе, намного проще.
(строка чтения "123")
=> 123
read-string
может выполнять код в соответствии с документами: clojuredocs.org/clojure.core/read-string
AFAIK нет стандартного решения вашей проблемы. Я думаю, что что-то вроде следующего, который использует clojure.contrib.str-utils2/replace
, должно помочь:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
... и он также не использует встроенную clojure.string/replace
функцию.
Это не идеально, но вот что - то с filter
, Character/isDigit
и Integer/parseInt
. Он не будет работать для чисел с плавающей запятой и не работает, если во входных данных нет цифры, поэтому вам, вероятно, следует очистить его. Я надеюсь, что есть более приятный способ сделать это, не использующий столько Java.
user=> (defn strToInt [x] (Integer/parseInt (apply str (filter #(Character/isDigit %) x))))
#'user/strToInt
user=> (strToInt "45px")
45
user=> (strToInt "45")
45
user=> (strToInt "a")
java.lang.NumberFormatException: For input string: "" (NO_SOURCE_FILE:0)
Я бы, наверное, добавил к требованиям несколько вещей:
Может быть что-то вроде:
(defn parse-int [v]
(try
(Integer/parseInt (re-find #"^\d+" (.toString v)))
(catch NumberFormatException e 0)))
(parse-int "lkjhasd")
; => 0
(parse-int (java.awt.Color. 4 5 6))
; => 0
(parse-int "a5v")
; => 0
(parse-int "50px")
; => 50
а затем, возможно, бонусные баллы за то, чтобы сделать это мульти-методом, который допускает пользовательское значение по умолчанию, отличное от 0.
Расширяя ответ snrobot:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
Эта версия возвращает nil, если во входных данных нет цифр, а не вызывает исключение.
У меня вопрос, допустимо ли сокращать имя до «str-> int» или такие вещи всегда должны быть полностью указаны.
Для всех, кто хочет преобразовать более обычный строковый литерал в число, то есть строку, в которой нет других нечисловых символов. Это два лучших подхода:
Использование взаимодействия с Java:
(Long/parseLong "333")
(Float/parseFloat "333.33")
(Double/parseDouble "333.3333333333332")
(Integer/parseInt "-333")
(Integer/parseUnsignedInt "333")
(BigInteger. "3333333333333333333333333332")
(BigDecimal. "3.3333333333333333333333333332")
(Short/parseShort "400")
(Byte/parseByte "120")
Это позволяет вам точно контролировать тип, в котором вы хотите проанализировать число, когда это важно для вашего варианта использования.
Используя программу чтения Clojure EDN:
(require '[clojure.edn :as edn])
(edn/read-string "333")
В отличие от использования, read-string
из clojure.core
которого небезопасно использовать для ненадежного ввода, edn/read-string
безопасно запускать для ненадежного ввода, такого как пользовательский ввод.
Это часто более удобно, чем взаимодействие с Java, если вам не нужно иметь особый контроль над типами. Он может анализировать любой числовой литерал, который может анализировать Clojure, например:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Полный список здесь: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
Также с помощью (re-seq)
функции можно расширить возвращаемое значение до строки, содержащей все числа, существующие во входной строке, в следующем порядке:
(defn convert-to-int [s]
(->> (re-seq #"\d" s)
(apply str)
(Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
Вопрос касается разбора строки на число.
(number? 0.5)
;;=> true
Таким образом, из приведенных выше десятичных знаков также следует анализировать.
Возможно, сейчас не совсем отвечаю на вопрос, но для общего использования, я думаю, вы хотели бы быть строгими в отношении того, является ли это число или нет (поэтому «px» не разрешено), и позволить вызывающей стороне обрабатывать не числа, возвращая nil:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
И если для вашего домена проблематичны поплавки, а не Float/parseFloat
поставить bigdec
или еще что-то.
В простых случаях вы можете просто использовать регулярное выражение, чтобы вытащить первую строку цифр, как указано выше.
Если у вас более сложная ситуация, вы можете использовать библиотеку InstaParse:
(ns tst.parse.demo
(:use tupelo.test)
(:require
[clojure.string :as str]
[instaparse.core :as insta]
[tupelo.core :as t] ))
(t/refer-tupelo)
(dotest
(let [abnf-src "
size-val = int / int-px
int = digits ; ex '123'
int-px = digits <'px'> ; ex '123px'
<digits> = 1*digit ; 1 or more digits
<digit> = %x30-39 ; 0-9
"
tx-map {:int (fn fn-int [& args]
[:int (Integer/parseInt (str/join args))])
:int-px (fn fn-int-px [& args]
[:int-px (Integer/parseInt (str/join args))])
:size-val identity
}
parser (insta/parser abnf-src :input-format :abnf)
instaparse-failure? (fn [arg] (= (class arg) instaparse.gll.Failure))
parse-and-transform (fn [text]
(let [result (insta/transform tx-map
(parser text))]
(if (instaparse-failure? result)
(throw (IllegalArgumentException. (str result)))
result))) ]
(is= [:int 123] (parse-and-transform "123"))
(is= [:int-px 123] (parse-and-transform "123px"))
(throws? (parse-and-transform "123xyz"))))
(t/refer-tupelo)
а не заставляете пользователя делать (:require [tupelo.core :refer :all])
?
refer-tupelo
смоделирован после того refer-clojure
, как он не включает в себя все, что (:require [tupelo.core :refer :all])
делает способ .