У мене є різні рядки, деякі на кшталт "45", деякі - "45px". Як я конвертую обидва ці числа на число 45?
"9"
в 9
це найкраще , що працював для мене (Integer. "9")
.
У мене є різні рядки, деякі на кшталт "45", деякі - "45px". Як я конвертую обидва ці числа на число 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.,
Мені подобається відповідь snrobot краще. Використання методу 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
повинні бути такою, when
оскільки у ваших FNS немає іншого блоку.
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 кешує значення між -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)))
Ця версія повертає нуль, якщо у введенні немає цифр, а не збільшення винятку.
Моє запитання - чи прийнятно скорочувати назву до "str-> int", чи такі речі завжди повинні бути повністю уточнені.
Також за допомогою (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" не дозволено), і дозволити абоненту обробляти не цифри, повертаючи нуль:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
І якщо Floats є проблематичними для вашого домену замість Float/parseFloat
put bigdec
або чогось іншого.
Для всіх, хто хоче розібрати більш звичайний лінійний рядок на число, тобто рядок, який не має інших символів, що не є числовими. Це два найкращі підходи:
Використання Java interop:
(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
Для простих випадків ви можете просто скористатися регулярним виразом, щоб витягнути перший рядок цифр, як згадувалося вище.
Якщо у вас складніша ситуація, ви можете скористатися бібліотекою 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])
робить.