Ich habe verschiedene Zeichenfolgen, einige wie "45", einige wie "45px". Wie konvertiere ich beide in die Zahl 45?
"9"
möchten 9
, ist dies das Beste, was für mich funktioniert hat : (Integer. "9")
.
Ich habe verschiedene Zeichenfolgen, einige wie "45", einige wie "45px". Wie konvertiere ich beide in die Zahl 45?
"9"
möchten 9
, ist dies das Beste, was für mich funktioniert hat : (Integer. "9")
.
Antworten:
Dies wird auf 10px
oder funktionierenpx10
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
es wird nur die erste fortlaufende Ziffer so analysiert
user=> (parse-int "10not123")
10
user=> (parse-int "abc10def11")
10
Exception in thread "main" java.lang.ClassNotFoundException: Integer.,
Ich mag die Antwort von Snrobot besser. Die Verwendung der Java-Methode ist einfacher und robuster als die Verwendung von Lesezeichenfolgen für diesen einfachen Anwendungsfall. Ich habe ein paar kleine Änderungen vorgenommen. Da der Autor negative Zahlen nicht ausschloss, habe ich sie angepasst, um negative Zahlen zuzulassen. Ich habe es auch so gemacht, dass die Nummer am Anfang der Zeichenfolge beginnen muss.
(defn parse-int [s]
(Integer/parseInt (re-find #"\A-?\d+" s)))
Außerdem habe ich festgestellt, dass Integer / parseInt als Dezimalzahl analysiert wird, wenn kein Radix angegeben wird, selbst wenn führende Nullen vorhanden sind.
Erstens, um nur eine Ganzzahl zu analysieren (da dies ein Hit bei Google ist und es sich um gute Hintergrundinformationen handelt):
Sie könnten den Leser benutzen :
(read-string "9") ; => 9
Sie können überprüfen, ob es sich um eine Zahl handelt, nachdem sie gelesen wurde:
(defn str->int [str] (if (number? (read-string str))))
Ich bin mir nicht sicher, ob Benutzereingaben vom Clojure-Reader als vertrauenswürdig eingestuft werden können, sodass Sie sie auch vor dem Lesen überprüfen können:
(defn str->int [str] (if (re-matches (re-pattern "\\d+") str) (read-string str)))
Ich glaube, ich bevorzuge die letzte Lösung.
Und nun zu Ihrer spezifischen Frage. So analysieren Sie etwas, das mit einer Ganzzahl beginnt, wie 29px
:
(read-string (second (re-matches (re-pattern "(\\d+).*") "29px"))) ; => 29
if
sollten Sie eine sein, when
da es keinen anderen Block in Ihrer FNS gibt.
read-string
interpretiert sie als oktal: (read-string "08")
löst eine Ausnahme aus. Integer/valueOf
behandelt sie als Dezimalzahl: (Integer/valueOf "08")
read-string
eine Ausnahme ausgelöst wird, wenn Sie eine leere Zeichenfolge oder etwas wie "29px"
(defn parse-int [s]
(Integer. (re-find #"[0-9]*" s)))
user> (parse-int "10px")
10
user> (parse-int "10")
10
Integer/valueOf
, anstelle des Integer-Konstruktors zu verwenden. Die Integer-Klasse speichert Werte zwischen -128 und 127 zwischen, um die Objekterstellung zu minimieren. Das Integer Javadoc beschreibt dies ebenso wie diesen Beitrag: stackoverflow.com/a/2974852/871012
Dies funktioniert in Repl für mich viel einfacher.
(Lesezeichenfolge "123")
=> 123
read-string
kann Code gemäß den Dokumenten ausführen: clojuredocs.org/clojure.core/read-string
AFAIK gibt es keine Standardlösung für Ihr Problem. Ich denke, etwas wie das Folgende, das verwendet clojure.contrib.str-utils2/replace
, sollte helfen:
(defn str2int [txt]
(Integer/parseInt (replace txt #"[a-zA-Z]" "")))
1.5
... und es nutzt auch nicht die eingebaute clojure.string/replace
Funktion.
Dies ist nicht perfekt, aber hier ist etwas mit filter
, Character/isDigit
und Integer/parseInt
. Es funktioniert nicht für Gleitkommazahlen und schlägt fehl, wenn die Eingabe keine Ziffer enthält. Sie sollten sie daher wahrscheinlich bereinigen. Ich hoffe, es gibt einen schöneren Weg, der nicht so viel Java beinhaltet.
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)
Ich würde wahrscheinlich ein paar Dinge zu den Anforderungen hinzufügen:
Vielleicht so etwas wie:
(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
und dann vielleicht Bonuspunkte dafür, dass dies eine Multi-Methode ist, die einen vom Benutzer angegebenen Standardwert außer 0 zulässt.
Die Antwort von Snrobot erweitern:
(defn string->integer [s]
(when-let [d (re-find #"-?\d+" s)] (Integer. d)))
Diese Version gibt null zurück, wenn die Eingabe keine Ziffern enthält, anstatt eine Ausnahme auszulösen.
Meine Frage ist, ob es akzeptabel ist, den Namen mit "str-> int" abzukürzen, oder ob solche Dinge immer vollständig spezifiziert werden sollten.
Durch die Verwendung der (re-seq)
Funktion kann der Rückgabewert auch auf eine Zeichenfolge erweitert werden, die alle in der Eingabezeichenfolge vorhandenen Zahlen in der folgenden Reihenfolge enthält:
(defn convert-to-int [s]
(->> (re-seq #"\d" s)
(apply str)
(Integer.)))
(convert-to-int "10not123")
=> 10123
(type *1)
=> java.lang.Integer
Bei der Frage geht es darum, eine Zeichenfolge in eine Zahl zu analysieren.
(number? 0.5)
;;=> true
Aus den obigen Dezimalstellen sollte also auch analysiert werden.
Vielleicht beantworten Sie die Frage jetzt nicht genau, aber für den allgemeinen Gebrauch sollten Sie genau wissen, ob es sich um eine Nummer handelt oder nicht (daher ist "px" nicht zulässig) und den Anrufer mit Nicht-Nummern umgehen lassen, indem Sie nil zurückgeben:
(defn str->number [x]
(when-let [num (re-matches #"-?\d+\.?\d*" x)]
(try
(Float/parseFloat num)
(catch Exception _
nil))))
Und wenn Floats für Ihre Domain problematisch sind, anstatt zu Float/parseFloat
setzen bigdec
oder etwas anderes.
Für alle anderen, die ein normaleres String-Literal in eine Zahl analysieren möchten, dh einen String, der keine anderen nicht numerischen Zeichen enthält. Dies sind die beiden besten Ansätze:
Verwenden von 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")
Auf diese Weise können Sie den Typ, in dem Sie die Nummer analysieren möchten, genau steuern, wenn dies für Ihren Anwendungsfall von Bedeutung ist.
Verwenden des Clojure EDN-Lesegeräts:
(require '[clojure.edn :as edn])
(edn/read-string "333")
Im Gegensatz zur Verwendung, read-string
bei der clojure.core
die Verwendung bei nicht vertrauenswürdigen Eingaben nicht sicher ist, kann sie bei nicht edn/read-string
vertrauenswürdigen Eingaben wie Benutzereingaben sicher ausgeführt werden.
Dies ist häufig praktischer als das Java-Interop, wenn Sie keine spezifische Kontrolle über die Typen benötigen. Es kann jedes Zahlenliteral analysieren, das Clojure analysieren kann, wie z.
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Vollständige Liste hier: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
In einfachen Fällen können Sie einfach eine Regex verwenden, um die erste Ziffernfolge wie oben erwähnt herauszuziehen.
Wenn Sie eine kompliziertere Situation haben, können Sie die InstaParse-Bibliothek verwenden:
(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)
anstatt den Benutzer dazu zu bringen (:require [tupelo.core :refer :all])
?
refer-tupelo
ist nachempfunden refer-clojure
, da es nicht alles enthält, was der Weg (:require [tupelo.core :refer :all])
tut.