Eu tenho várias strings, algumas como "45", outras como "45px". Como eu converto ambos para o número 45?
"9"
em 9
, esta é a melhor coisa que funcionou para mim: (Integer. "9")
.
Eu tenho várias strings, algumas como "45", outras como "45px". Como eu converto ambos para o número 45?
"9"
em 9
, esta é a melhor coisa que funcionou para mim: (Integer. "9")
.
Respostas:
Isso funcionará em 10px
oupx10
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
ele analisará o primeiro dígito contínuo apenas para
user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10
Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
Eu gosto mais da resposta do snrobot. O uso do método Java é mais simples e mais robusto do que o uso de string de leitura para este caso de uso simples. Fiz algumas pequenas alterações. Como o autor não descartou números negativos, ajustei-o para permitir números negativos. Eu também fiz isso, requer que o número comece no início da string.
(defn parse-int [s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
Além disso, descobri que Integer / parseInt analisa como decimal quando nenhuma raiz é fornecida, mesmo se houver zeros à esquerda.
Primeiro, para analisar apenas um número inteiro (já que este é um sucesso no google e é uma boa informação de plano de fundo):
Você pode usar o leitor :
(read-string "9") ; => 9
Você pode verificar se é um número depois de ler:
(defn str->int [str] (if (number? (read-string str))))
Não tenho certeza se a entrada do usuário pode ser confiável pelo leitor de clojure para que você possa verificar antes de ler também:
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
Eu acho que prefiro a última solução.
E agora, para sua pergunta específica. Para analisar algo que começa com um número inteiro, como 29px
:
(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
if
deve ser uma, when
já que não há mais nenhum bloqueio no seu DNS.
read-string
interpreta-os como octal: (read-string "08")
lança uma exceção. Integer/valueOf
trata-los como decimal: (Integer/valueOf "08")
avalia a 8.
read-string
lança uma exceção se você dar-lhe uma cadeia vazia ou algo como "29px"
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
, em vez do construtor Integer. A classe Integer armazena em cache valores entre -128 e 127 para minimizar a criação do objeto. O Javadoc inteiro descreve isso da mesma forma que esta postagem: stackoverflow.com/a/2974852/871012
Isso funciona em substituição a mim, muito mais direto.
(sequência de leitura "123")
=> 123
read-string
pode executar código por docs: clojuredocs.org/clojure.core/read-string
AFAIK não há solução padrão para o seu problema. Eu acho que algo como o seguinte, que usa clojure.contrib.str-utils2/replace
, deve ajudar:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
... e também não faz uso da clojure.string/replace
função interna.
Isso não é perfeito, mas aqui está algo com filter
, Character/isDigit
e Integer/parseInt
. Ele não funcionará para números de ponto flutuante e falhará se não houver dígito na entrada; portanto, você provavelmente deve limpá-lo. Espero que haja uma maneira melhor de fazer isso que não envolva muito 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)
Eu provavelmente adicionaria algumas coisas aos requisitos:
Talvez 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
e então talvez pontos de bônus por tornar esse um método múltiplo que permita um padrão fornecido pelo usuário diferente de 0.
Expandindo a resposta do snrobot:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
Esta versão retorna nulo se não houver dígitos na entrada, em vez de gerar uma exceção.
Minha pergunta é se é aceitável abreviar o nome para "str-> int" ou se coisas assim devem sempre ser totalmente especificadas.
Também o uso da (re-seq)
função pode estender o valor de retorno para uma sequência que contém todos os números existentes na sequência de entrada na ordem:
(defn convert-to-int [s]
(->> (re-seq #"\d" s)
(apply str)
(Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
A pergunta é feita sobre a análise de uma string em um número.
(number? 0.5)
;;=> true
Portanto, os decimais acima também devem ser analisados.
Talvez não esteja exatamente respondendo à pergunta agora, mas, para uso geral, acho que você gostaria de ser rigoroso sobre se é um número ou não (portanto, "px" não é permitido) e deixar que o chamador lide com não-números retornando zero:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
E se os flutuadores forem problemáticos para o seu domínio, em vez de Float/parseFloat
colocar bigdec
ou algo mais.
Para quem quer analisar um literal String mais normal em um número, ou seja, uma string que não possui outros caracteres não numéricos. Estas são as duas melhores abordagens:
Usando interoperabilidade 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")
Isso permite controlar com precisão o tipo em que você deseja analisar o número, quando isso for importante para o seu caso de uso.
Usando o leitor Clojure EDN:
(require '[clojure.edn :as edn])
(edn/read-string "333")
Ao contrário do uso read-string
do clojure.core
qual não é seguro usar em entradas não confiáveis, edn/read-string
é seguro executar em entradas não confiáveis, como a entrada do usuário.
Isso geralmente é mais conveniente do que a interoperabilidade Java, se você não precisar ter controle específico dos tipos. Ele pode analisar qualquer número literal que o Clojure possa analisar, como:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Lista completa aqui: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
Para casos simples, você pode usar apenas uma regex para extrair a primeira sequência de dígitos, como mencionado acima.
Se você tiver uma situação mais complicada, poderá usar a 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)
vez de fazer o usuário fazer (:require [tupelo.core :refer :all])
?
refer-tupelo
é modelado depois refer-clojure
, na medida em que não inclui tudo do jeito que (:require [tupelo.core :refer :all])
faz.