ランダムな文字列を生成しよう!
皆さんどうも!江嵜です!
今日は 普段そんなに必要じゃないけど、たまに必要でちょっとメンドイあいつ
、ランダム文字列の生成です!
これを今回は各言語ごとに見ていきますね!
ルール
今回コード化するにあたってのルールはこちら。
- 出現する文字列は
- 数字 (0 ~ 9)
- アルファベット大文字 (A ~ Z)
- アルファベット小文字 (a ~ z)
- どの文字も出現確率が(概ね)均一になるようにする
あとは使用しやすいように、
- 長さを指定できるように関数などにくるむ
- 最後に 10 文字のランダム文字列を生成して表示する
以上の内容に沿って、 13 の言語でコードにしてみました!
(ちなみに性能はほとんど考えてないです)
それではどうぞ!
言語別コード!
Bash (ShellScript)
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 10 | head -n 1
/dev/urandom
使ったら一撃ですね。
乱数持ってきて、 a-zA-Z0-9
でフィルタして、 10 文字ごとに改行して 1 行目だけ使う。
多分最初にして一番キレイに書けたんじゃないでしょうか。
C
#include <stdio.h> #include <stdlib.h> #include <time.h> void rand_text(int length, char *result) { int i, rand_int; char char_set[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; for (i = 0; i < length; i++) { result[i] = temp[rand() % sizeof(char_set)]; } result[length] = 0; } int main(void){ int length = 10; char result[length + 1]; srand(time(NULL)); rand_text(length, result); printf("%s", result); }
泥臭い…!と思いましたが、他の言語でも結局こういうことをやってるんですよね。
一番キホンの書き方って感じです。
C++
#include <string> #include <algorithm> #include <random> using namespace std; string random_text(int length) { string text; generate_n(text.begin(), length, []() -> char { random_device rnd; mt19937 mt(rnd()); const char char_set[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; char result = char_set[mt() % (sizeof(char_set) - 1)]; return result; }); return text; } int main(void){ const int length = 10; printf("%s", random_text(length).c_str()); }
C に比べてレベル上がりましたね。
generate_n
でラムダ使ってます。
C#
using System; using System.Text; using System.Linq; public class main{ public static void Main(){ Console.WriteLine(randomText(10)); } private static string randomText(int length) { var list = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToList(); Random rand = new Random(); StringBuilder sb = new StringBuilder(); for(var i = 0; i < length; i++) { sb.Append(list.ElementAt(rand.Next(list.Count()))); } return sb.ToString(); } }
C, C++ に比べて大分スッキリしました。
どっちかというと Java に近いんで比べること自体がアレかもしれないですが。
Go
package main import ( "fmt" "math/rand" "time" ) func init() { rand.Seed(time.Now().UnixNano()) } func main() { fmt.Printf("%s", randomText(10)) } func randomText(length int) string { const charSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" b := make([]byte, length) for i := range b { b[i] = charSet[rand.Intn(len(charSet))] } return string(b) }
Go は文字列に対してそのまま配列みたいに、[]
をくっつけることで文字を取り出せますね。
Go の比較には C がよく引き合いに出されますが、確かに分かりやすくなった C って感じかも…
Java
import java.util.Random; import java.util.stream.IntStream; public class Main { public static void main(String[] args) throws Exception { System.out.println(randomText(10)); } public static String randomText(int length) { String charSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; Random rnd = new Random(); StringBuilder sb = new StringBuilder(); IntStream.range(0,length) .forEach(x -> { int index = rnd.nextInt(charSet.length()); sb.append(charSet.substring(index, index + 1)); }); return sb.toString(); } }
みんな大好き(?) Java です。
こちらは 8 で追加されたラムダを使ってみました。
でもちょっとこれは直感的じゃなくてムリヤリかな…普通に for
使った方が良い気がする。
ちなみにこんなことしなくても Apache Commons Lang のライブラリ使用したら瞬殺です。
JavaScript
const randomText = (length) => { const charSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; let result = ""; for (let i = 0; i < length; i++) { result += charSet[Math.floor(Math.random() * charSet.length)]; } return result; }; console.log(randomText(10));
JS も Go と同じように、文字列に対して配列みたいな方法で文字を引っ張ってこれてますね。
The.シンプル。
他の言語と違うのは、むしろ関数の宣言部分ですね。
無名関数を変数に入れるやつ。
MySQL (ストアドファンクション)
delimiter // CREATE function random_text (length int) returns text begin declare i int default 0; declare result text default ''; declare char_set text default '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; while i < length do set result = concat( result, substring(char_set, ceil(rand() * char_length(char_set)), 1) ); set i = i + 1; end while; return result; end// delimiter ; SELECT random_text(10);
これは需要あるのかな…?
ストアドファンクションと呼ばれるヤツですね。
こちらも結構素直な感じで書けます。
Perl
sub randomText { my @chars; push(@chars, ('0'..'9')); push(@chars, ('a'..'z')); push(@chars, ('A'..'Z')); $result = ''; for (my $i = 0; $i < $_[0]; $i++) { $result .= $chars[int(rand($#chars+1))]; } print $result; } &randomText(10)
('a'..'z')
で a ~ z の配列を作れるのですね。
その他は他の言語とほぼ同じですね。
PHP
function rand_txt($length) { $text_ar = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z')); $s = ''; for ($i = 0; $i < $length; $i++) { $s .= $text_ar[mt_rand(0, count($text_ar) - 1)]; } return $s; } echo rand_txt(10);
PHP も range('a', 'z')
で簡単に a ~ z の配列が作れます。
しかも array_merge
で結合も出来るので、一行で配列を作れてしまいますね。
PHP, Ruby 等はこういった便利関数が充実しているイメージ。
Python (2 & 3)
import string import random def randomText(length): return ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(length)]) print(randomText(10))
激短い!
リスト内包表記ってやつですね。
別にこんなことしなくても素直に書けばいいんじゃないか…という気もしなくもないですが。
Ruby
def randomText(length) charSet = [('0'..'9'), ('a'..'z'), ('A'..'Z')].map(&:to_a).flatten print (0...length).map{charSet[rand(charSet.length)]}.join end print randomText(10)
こちらも大概短いですね。
map 使い放題です。
VisualBasic
public class compiler Private Shared ReadOnly charSet As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" Private Shared Function RandomText (length As Integer) As String Dim sb As New System.Text.StringBuilder(length) Dim rnd As New Random() For i As Integer = 0 To length - 1 Dim c As Char = charSet(rnd.Next(charSet.Length)) sb.Append(c) Next Return sb.ToString() End Function Shared Function Main As Integer Console.WriteLine(RandomText(10)) Return 0 End Function end class
重い…!
上の数言語は短く書けてただけに、急に C に戻ったくらいな感じです。
そして微妙に見づらいのは単語の先頭を大文字にしているからですかね?
まとめてみて
今回の条件では 大文字、小文字、数字 を全て大体均等になるように出力しようとしたので
ほとんどが泥臭く 全ての文字リストを作ってから、そこからランダムにひとつずつ拾っていく
方法で書きました。
が、数字 & 大文字・小文字のどちらか
みたいな内容で良かったり、
16 進数の 0~9, a~f だけ
で良かったりする場合はもっとスマートに書ける場合が多そうです。
あと、ライブラリがある場合はそれを素直に使った方がいいんじゃないかな…という気もしています。
ちなみに、みなさんはこの中でどの言語が好きですか?
私はやっぱり JavaScript が一番慣れているのもあって、キレイに見えますね。
Java みたいにガッツり書くのも嫌いじゃないですが。
皆さんの何かにお役に立てば幸いです。