Tengo varias cadenas, algunas como "45", otras como "45px". ¿Cómo convierto ambos al número 45?
"9"
en 9
, esto es lo mejor que funcionó para mí: (Integer. "9")
.
Tengo varias cadenas, algunas como "45", otras como "45px". ¿Cómo convierto ambos al número 45?
"9"
en 9
, esto es lo mejor que funcionó para mí: (Integer. "9")
.
Respuestas:
Esto funcionará 10px
opx10
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
analizará el primer dígito continuo solo para
user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10
Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
Me gusta más la respuesta de snrobot. Usar el método Java es más simple y más robusto que usar read-string para este caso de uso simple. Hice un par de pequeños cambios. Como el autor no descartó números negativos, lo ajusté para permitir números negativos. También lo hice para que requiera que el número comience al principio de la cadena.
(defn parse-int [s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
Además, descubrí que Integer / parseInt se analiza como decimal cuando no se proporciona una raíz, incluso si hay ceros a la izquierda.
Primero, para analizar solo un número entero (ya que este es un éxito en Google y es una buena información de fondo):
Podrías usar el lector :
(read-string "9") ; => 9
Puede verificar que es un número después de leerlo:
(defn str->int [str] (if (number? (read-string str))))
No estoy seguro de si el lector clojure puede confiar en la entrada del usuario para que pueda verificar antes de leerla también:
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
Creo que prefiero la última solución.
Y ahora, a su pregunta específica. Para analizar algo que comienza con un número entero, como 29px
:
(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
if
debería ser una, when
ya que no hay más bloqueos en sus aletas.
read-string
los interpreta como octales: (read-string "08")
arroja una excepción. Integer/valueOf
los trata como decimales: (Integer/valueOf "08")
evalúa a 8.
read-string
arroja una excepción si le da una cadena vacía o algo así como "29px"
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
, en lugar del constructor Integer. La clase Integer almacena en caché valores entre -128 y 127 para minimizar la creación de objetos. El Javadoc Integer describe esto al igual que esta publicación: stackoverflow.com/a/2974852/871012
Esto funciona en respuesta para mí, mucho más directo.
(cadena de lectura "123")
=> 123
read-string
puede ejecutar código según los documentos: clojuredocs.org/clojure.core/read-string
AFAIK no hay una solución estándar para su problema. Creo que algo como lo siguiente, que utiliza clojure.contrib.str-utils2/replace
, debería ayudar:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
... y tampoco hace uso de la clojure.string/replace
función incorporada.
Esto no es perfecta, pero aquí hay algo con filter
, Character/isDigit
y Integer/parseInt
. No funcionará para números de coma flotante y falla si no hay un dígito en la entrada, por lo que probablemente debería limpiarlo. Espero que haya una mejor manera de hacer esto que no implique tanto 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)
Probablemente agregaría algunas cosas a los requisitos:
Tal vez algo como:
(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
y luego quizás puntos de bonificación por hacer de este un método múltiple que permita un valor predeterminado proporcionado por el usuario que no sea 0.
Ampliando la respuesta de snrobot:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
Esta versión devuelve nil si no hay dígitos en la entrada, en lugar de generar una excepción.
Mi pregunta es si es aceptable abreviar el nombre a "str-> int", o si cosas como esta siempre deben especificarse por completo.
El uso de la (re-seq)
función también puede extender el valor de retorno a una cadena que contiene todos los números existentes en la cadena de entrada en orden:
(defn convert-to-int [s]
(->> (re-seq #"\d" s)
(apply str)
(Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
La pregunta se refiere a analizar una cadena en un número.
(number? 0.5)
;;=> true
Entonces, a partir de los decimales anteriores, también se deben analizar.
Tal vez no responda exactamente la pregunta ahora, pero para uso general, creo que le gustaría ser estricto acerca de si es un número o no (por lo tanto, "px" no está permitido) y dejar que la persona que llama maneje no números devolviendo nil:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
Y si los flotadores son problemáticos para su dominio en lugar de Float/parseFloat
put bigdec
u otra cosa.
Para cualquier otra persona que busque analizar un literal de cadena más normal en un número, es decir, una cadena que no tenga otros caracteres no numéricos. Estos son los dos mejores enfoques:
Usando interoperabilidad 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")
Esto le permite controlar con precisión el tipo en el que desea analizar el número, cuando eso es importante para su caso de uso.
Usando el lector Clojure EDN:
(require '[clojure.edn :as edn])
(edn/read-string "333")
A diferencia del uso read-string
desde el clojure.core
cual no es seguro usarlo en una entrada no confiable, edn/read-string
es seguro ejecutarlo en una entrada no confiable, como la entrada del usuario.
Esto suele ser más conveniente que la interoperabilidad de Java si no necesita tener un control específico de los tipos. Puede analizar cualquier número literal que Clojure puede analizar como:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Lista completa aquí: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
Para casos simples, puede usar una expresión regular para extraer la primera cadena de dígitos como se mencionó anteriormente.
Si tiene una situación más complicada, puede utilizar la biblioteca 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)
lugar de hacer que el usuario lo haga (:require [tupelo.core :refer :all])
?
refer-tupelo
sigue el modelo refer-clojure
, ya que no incluye todo lo que (:require [tupelo.core :refer :all])
hace.