Perl6: 密碼產生器


每每說到「密碼產生器」,就不得不去複習一下 xkcd/936 這則漫畫。

xkcd 936 這則漫畫在闡明的重點其實就是密碼要「夠長」。密碼長則難以猜中而強度高。 從夠大的英文字典檔中隨機揀四個英文單字出來所組成的密碼,其強度已然超過了用許多 特殊符號組成的短密碼。

那麼就以此為題吧:如何從一個英文字典檔中隨機揀四個英文單字出來、組成密碼?

假設已有個陣列 @dict,內含了一個英文字典中所有單字,那麼要 從其中隨機選一個元素出來的做法為:

@dict.pick()

是的,這件事在 Perl6 語言裡是個內建函式:pick。這個函式定義在許多種容器類 別上,表示「從容器中選個東西出來」的意思。

事實上這個函式還能收個參數,表示要揀出的元素個數。所以要四個的話,就是:

@dict.pick(4)

而且被 pick 選中的,還是不重複的元素。更精確地說,是在 @dict 中不同位置的 元素。如果 "apple"@dict 中出現了兩次,兩個都被挑中的機率不會是零。

若要刻意容許選出重複元素的可能性,可以改成呼叫 pick() 四次的做法:

(1..4).map({ dicta.pick() })

那麼 @dict 的內容又要從何而來呢?其實有許多進行中的字典計畫,其授權為公開的, 可自行取用。像是:FreeDictWordNet 等等。但,其實在 macOS 上已有個 承襲自 FreeBSD 的字典檔: /usr/share/dict/words。此字典檔來源為 "Webster's Second International",短名 "web2a",是個於 1934 年出版的字典。在著作權過期後,被 廣泛地用於各種 UNIX 系統上。一般是當作拼字檢查的資料來源。在 FreeBSD / macOS 以外的系統上,這檔案的內容可能會不同,但格式都是相同的。

這個檔案格式很簡單。純文字,一列一個單字。就這樣。因此要將其讀成陣列很也容易。

my @dict = open("/usr/share/dict/words").lines();

是的,這個 lines 也是個內建於 IO::Handle 類別上的方法。上述寫法 會把整個檔案讀進來,依換行字符切開,再把切下來的各部份裝進陣列 @dict 當中。 各元素內容是不包含換行字符的。這是預設行為,但有參數可以微調。

而既然密碼只要四個字,其實也可以直接串下去,最後直接印出來:

open("/usr/share/dict/words").lines().pick(4).join().say;

若無參數,方法呼叫所用的那組空括號就可以省略不寫:

open("/usr/share/dict/words").lines.pick(4).join.say;

這程式既然如此短,也可以直接以一行文(oneliner)寫完收工:

# perl6 -e 'open("/usr/share/dict/words").lines.pick(4).join.say'
phalangiticatherosclerosisrupturedsnarly

然後可以包成 zsh 函式,省得每次都在打那一大串程式碼:

function pw() { perl6 -e 'open("/usr/share/dict/words").lines.pick(4).join.say' }

效果相同

# pw
irrelategushingnessuntumbledrigorism

另外廣告:Javascript / Web UI 版: pw