簡體   English   中英

如何在Java / Clojure中獲取所有字母的集合?

[英]How do I get the set of all letters in Java/Clojure?

在Python中,我可以這樣做:

>>> import string
>>> string.letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

有沒有辦法在Clojure中做類似的事情(除了復制和粘貼上面的字符)? 我查看了Clojure標准庫和java標准庫,但找不到它。

如果你只想要Ascii字符,

(map char (concat (range 65 91) (range 97 123)))

會產生,

(\A \B \C \D \E \F \G \H \I \J \K \L \M \N \O \P \Q \R \S \T \U \V \W \X \Y \Z 
 \a \b \c \d \e \f \g \h \i \j \k \l \m \n \o \p \q \r \s \t \u \v \w \x \y \z)

一個正確的非以ASCII為中心的實現:

private static String allLetters(String charsetName)
{
    CharsetEncoder ce = Charset.forName(charsetName).newEncoder();
    StringBuilder result = new StringBuilder();
    for(char c=0; c<Character.MAX_VALUE; c++)
    {
        if(ce.canEncode(c) && Character.isLetter(c))
        {
            result.append(c);
        }
    }
    return result.toString();
}

用“US-ASCII”調用它,你將獲得所需的結果(除了大寫字母首先)。 您可以使用Charset.defaultCharset()調用它,但我懷疑您在大多數系統上獲得的遠遠超過ASCII字母,即使在美國也是如此。

警告:只考慮基本的多語言平面。 擴展到補充平面不會太難,但需要更長的時間,實用程序是值得懷疑的。

基於Michaels命令式Java解決方案,這是一個慣用的(懶惰序列)Clojure解決方案:

(ns stackoverflow
  (:import (java.nio.charset Charset CharsetEncoder)))

(defn all-letters [charset]
  (let [encoder (. (Charset/forName charset) newEncoder)]
    (letfn [(valid-char? [c]
             (and (.canEncode encoder (char c)) (Character/isLetter c)))
        (all-letters-lazy [c]
                  (when (<= c (int Character/MAX_VALUE))
                (if (valid-char? c)
                  (lazy-seq
                   (cons (char c) (all-letters-lazy (inc c))))
                  (recur (inc c)))))]
      (all-letters-lazy 0))))

更新:感謝cgrand這個更好的高級解決方案:

(defn letters [charset-name]
  (let [ce (-> charset-name java.nio.charset.Charset/forName .newEncoder)]
    (->> (range 0 (int Character/MAX_VALUE)) (map char)
         (filter #(and (.canEncode ce %) (Character/isLetter %))))))

但我的第一種方法之間的性能比較

user> (time (doall (stackoverflow/all-letters "ascii"))) 
"Elapsed time: 33.333336 msecs"                                                  
(\A \B \C \D \E \F \G \H \I \J \K \L \M \N \O \P \Q \R \S \T \U \V \W \X \Y \Z \\
a \b \c \d \e \f \g \h \i \j \k \l \m \n \o \p \q \r \s \t \u \v \w \x \y \z)  

和你的解決方案

user> (time (doall (stackoverflow/letters "ascii"))) 
"Elapsed time: 666.666654 msecs"                                                 
(\A \B \C \D \E \F \G \H \I \J \K \L \M \N \O \P \Q \R \S \T \U \V \W \X \Y \Z \\
a \b \c \d \e \f \g \h \i \j \k \l \m \n \o \p \q \r \s \t \u \v \w \x \y \z) 

非常有趣。

不,因為那只是打印出ASCII字母而不是全套。 當然,使用兩個for循環打印26個小寫字母和大寫字母是微不足道的,但事實是在前127個代碼點之外還有更多的“字母”。 Java中的“isLetter”字符對於這些和其他許多人都是正確的。

string.letters:下面描述的字符串小寫和大寫的串聯。 特定值取決於語言環境,並在調用locale.setlocale()時更新。

我修改了Michael Borgwardt的答案。 在我的實現中,有兩個列表lowerCases和upperCases有兩個原因:

  1. string.letters是小寫,后跟大寫。

  2. Java Character.isLetter(char)不僅僅是大寫和小寫,因此使用Character.isLetter(char)將在一些字符集下返回很多結果,例如“windows-1252”

來自Api-Doc:Character.isLetter(char)

如果Character.getType(ch)提供的常規類別類型是以下任何一個字符,則該字符被視為字母:

 * UPPERCASE_LETTER * LOWERCASE_LETTER * TITLECASE_LETTER * MODIFIER_LETTER * OTHER_LETTER 

並非所有信件都有案例。 許多字符都是字母,但既不是大寫也不是小寫,也不是標題。

因此,如果string.letters只返回小寫和大寫,則必須忽略TITLECASE_LETTER,MODIFIER_LETTER和OTHER_LETTER字符。

public static String allLetters(final Charset charset) {
    final CharsetEncoder encoder = charset.newEncoder();
    final StringBuilder lowerCases = new StringBuilder();
    final StringBuilder upperCases = new StringBuilder();
    for (char c = 0; c < Character.MAX_VALUE; c++) {
    if (encoder.canEncode(c)) {
    if (Character.isUpperCase(c)) {
    upperCases.append(c);
    } else if (Character.isLowerCase(c)) {
    lowerCases.append(c);
    }
    }
    }
    return lowerCases.append(upperCases).toString();
}

另外:更改語言環境時string.letters的行為會發生變化。 這可能不適用於我的解決方案,因為更改默認語言環境不會更改默認字符集。 來自apiDoc:

默認字符集在虛擬機啟動期間確定,通常取決於底層操作系統的區域設置和字符集。

我猜,默認的字符集不能在啟動的JVM中更改。 因此,僅使用Locale.setDefault(Locale)無法實現string.letters的“更改區域設置”行為。 但是更改默認語言環境無論如何都是一個壞主意:

由於更改默認語言環境可能會影響許多不同的功能區域,因此只有在調用方准備重新初始化在同一Java虛擬機中運行的區域設置敏感代碼時,才應使用此方法。

我很確定這些字母在標准庫中不可用,因此您可能只需要手動方法。

與Python解決方案相比,必須手動生成以下語句,與您的問題中提到的結果相同:

public class Letters {

    public static String asString() {
        StringBuffer buffer = new StringBuffer();
        for (char c = 'a'; c <= 'z'; c++)
            buffer.append(c);
        for (char c = 'A'; c <= 'Z'; c++)
            buffer.append(c);
        return buffer.toString();
    }

    public static void main(String[] args) {
        System.out.println(Letters.asString());
    }

}

如果你不記得代碼點范圍。 蠻力方式:-P:

user> (require '[clojure.contrib.str-utils2 :as stru2])
nil
user> (set (stru2/replace (apply str (map char (range 0 256))) #"[^A-Za-z]" ""))
#{\A \a \B \b \C \c \D \d \E \e \F \f \G \g \H \h \I \i \J \j \K \k \L \l \M \m \N \n \O \o \P \p \Q \q \R \r \S \s \T \t \U \u \V \v \W \w \X \x \Y \y \Z \z}
user> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM