tutorial online example clojure

online - En Clojure, ¿cómo puedo convertir una Cadena en un número?



clojure vs scala (11)

Tengo varias cadenas, algunas como "45", algunas como "45px". ¿Cómo puedo convertir ambos en el número 45?


Nueva respuesta

Me gusta más la respuesta de snrobot. Usar el método de Java es más simple y más robusto que utilizar la cadena de lectura para este caso de uso simple. Hice un par de pequeños cambios. Dado que el autor no descartaba los números negativos, lo ajusté para permitir números negativos. También lo hice así que requiere el número para comenzar al principio de la secuencia.

(defn parse-int [s] (Integer/parseInt (re-find #"/A-?/d+" s)))

Además encontré que Integer / parseInt analiza como decimal cuando no se da radix, incluso si hay ceros a la izquierda.

Respuesta anterior

En primer lugar, para analizar solo un número entero (ya que este es un hit en google y es una buena información de fondo):

Puedes usar el reader :

(read-string "9") ; => 9

Puede verificar que sea un número después de haber sido leído:

(defn str->int [str] (if (number? (read-string str))))

No estoy seguro de si el lector clojure puede confiar en la entrada del usuario, por lo que podría consultar 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 tu pregunta específica. Para analizar algo que comienza con un entero, como 29px :

(read-string (second (re-matches (re-pattern "(//d+).*") "29px"))) ; => 29


AFAIK no hay una solución estándar para su problema. Creo que algo como lo siguiente, que usa clojure.contrib.str-utils2/replace , debería ayudar:

(defn str2int [txt] (Integer/parseInt (replace txt #"[a-zA-Z]" "")))


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 estar completamente especificadas.


Esto funciona en repl para mí, mucho más directo.

(cadena de lectura "123")

=> 123


Esto no es perfecto, pero aquí hay algo con filter , Character/isDigit y Integer/parseInt . No funcionará para números flotantes y falla si no hay un dígito en la entrada, por lo que probablemente debería limpiarlo. Espero que haya una manera más agradable de hacer esto que no involucre 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)


La pregunta pregunta sobre analizar una cadena en un número.

(number? 0.5) ;;=> true

Por lo tanto, los decimales anteriores también deben analizarse.

Tal vez no responda exactamente la pregunta ahora, pero para uso general, creo que le gustaría ser estricto sobre si es un número o no (así que "px" no está permitido) y dejar que la persona que llama maneje números que no sean nulos al devolver nil:

(defn str->number [x] (when-let [num (re-matches #"-?/d+/.?/d*" x)] (try (Float/parseFloat num) (catch Exception _ nil))))

Y si los Floats son problemáticos para su dominio en lugar de Float/parseFloat ponga bigdec u otra cosa.


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 usar 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"))))


Probablemente agregaría algunas cosas a los requisitos:

  • Tiene que comenzar con un dígito
  • Tiene que tolerar entradas vacías
  • Tolera que se pase cualquier objeto (toString es estándar)

Tal vez algo así 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 quizás también puntos de bonificación por hacer de este un método múltiple que permite un valor por defecto proporcionado por el usuario distinto de 0.


También el uso de la función (re-seq) puede extender el valor de retorno a una cadena que contenga 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


Esto funcionará en 10px o px10

(defn parse-int [s] (Integer. (re-find #"/d+" s )))

se analizará el primer dígito continuo solo para

user=> (parse-int "10not123") 10 user=> (parse-int "abc10def11") 10


(defn parse-int [s] (Integer. (re-find #"[0-9]*" s))) user> (parse-int "10px") 10 user> (parse-int "10") 10