commonsのRandomStringUtilsを使わず、JavaのUUIDを使わず、Javaでランダムな文字列作成してみるの巻

眠いので、数年前に作ってお蔵入りしていたソースをとりあえず貼ってみた。
最近はこんな風にランダムな値を作ることはしないのかしらね。
自分でもかなり忘れている。

import java.security.SecureRandom;
import java.util.Random;

/**
 * ランダムな文字列を作成する。
 * ランダムに生成したx個のバイト列を基にbase64の方法でバイトから下の値で変換していく。
 * 
 * ランダムに生成したbyte配列から3byteずつ取り出す。
 * base64形式でbyteをascii文字に変換していくため。
 * 3byte=24bitだからそれを演算で結合していく。
 * 00000000 11111111 00010111こんな感じのを
 * 000000 001111 111100 010111このように6bitずつ使いたい。
 * そのため、byte配列をビット演算する。
 * 1byte目を左に16bitシフト、2byte目を左に8bitシフト、3byte目をor演算。
 * これをint型に入れる。残りの8bitは0が入る。
 * 00000000 00000000 11111111 00010111こんな感じ。
 * 今度はこれから6bitずつ値を取り出す。
 * 右に18bit, 12bit, 6bit, 0bitシフトし、111111(10進数なら63)でand演算をそれぞれ実行。
 * 
 * 00000000 00000000 11111111 00010111
 *          111111
 * 00000000 00000000 11111111 00010111
 *                11 1111
 * 00000000 00000000 11111111 00010111
 *                       1111 11
 * 00000000 00000000 11111111 00010111
 *                              111111
 * 
 * 32bitで見てみると先ほどの24bitを4等分にしたint型の値を取得できる。
 * 
 * 実際のand演算は以下。
 * 
 * 00000000 00000000 00000000 00000000
 * 00000000 00000000 00000000 00111111
 * 
 * 00000000 00000000 00000000 00001111
 * 00000000 00000000 00000000 00111111
 * 
 * 00000000 00000000 00000011 11111100
 * 00000000 00000000 00000000 00111111
 * 
 * 00000000 00000000 11111111 00010111
 * 00000000 00000000 00000000 00111111
 * 
 * 得られた4つのint値でA〜/までの値を含む文字列から該当順序にある文字を取り出す。
 * 
 * これを3byteずつ取り出して繰り返す。
 * 
 * 端数となる場合(残り2byte, 1byteの時)、bitが6の倍数となるようパディングを行う。
 * 残り2byteの場合、2bit足りないので左に2bitシフト演算し、0パディングする。
 * 残り1byteの場合、4bit足りないので左に4bitシフト演算し、0パディングする。
 * その後は上記と同様に処理をして文字を取り出す。
 * base64では文字は4つずつなので足りない場合「=」を付け足して増やす。
 * 
 * 
 * ランダムな英数字のみが必要な場合はその後、+/や=の値を変換する処理を入れる。
 * ちなみに今の処理では、指定する値はbyte数であるため、実際に作成される文字数はそれより多くなる。
 * 
 * @author お屠蘇
 *
 */
public class Samplepleple {

	private static final String des = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	
	private static final char equal = '=';
	
	private static final Random num_randam = new Random();
	//とりあえず、デフォ使用。
	private static final SecureRandom random = new SecureRandom();
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		int len = 10;
		for(int i=0;i<len;i++) {
			int xd = 24;
			getSecureRandumString(xd);
		}
	}
	
	public static String getSecureRandumString(int len) {
		if(len <= 0) {
			return null;
		}
		byte[] bytes = new byte[len];
		random.nextBytes(bytes);
		
		byte[] bytecp = bytes.clone();
		
		StringBuilder buf = new StringBuilder();
		int encount = 0;
		int total = bytes.length;
		int lastcount = 0;
		if (total - 3 < 0) {
			//どうしようかね。
		}
		for (; encount < bytes.length; encount=encount+2) {
			if (total - 3 < 0) {
				lastcount = total;
				break;
			}
			total = total - 3;//3byte取り出す=24bit
			//int 32bit
			byte n1 = bytes[encount];
			byte n2 = bytes[encount+1];
			byte n3 = bytes[encount+2];
			
			//24bit
			//000000 001111 111100 010111
			int n123 = n1 << 16 | n2 << 8 | n3 ;
			//010100 001111 111100 010111
			//111111 111111 111111 111111
			
			//6bitであることに注意。0xfは4bit
			int b1 = (n123 >>> 18) & 63;
			int b2 = (n123 >>> 12) & 63;
			int b3 = (n123 >>> 6) & 63;
			int b4 = n123 & 63;
			
			buf.append(des.charAt(b1))
			   .append(des.charAt(b2))
			   .append(des.charAt(b3))
			   .append(des.charAt(b4));
		}
		//残り2byte 16bit/6=2 rm4bit
		if (total == 2) {
			byte n1 = bytes[encount];
			byte n2 = bytes[encount+1];
			int n123 = (n1 << 8 | n2) << 2;
			//18bit
			//11111111 11111111 0000
			//111111 111111 111100
			int b1 = (n123 >>> 12) & 63;
			int b2 = (n123 >>> 6) & 63;
			int b3 = n123 & 63;
			
			buf.append(des.charAt(b1))
			   .append(des.charAt(b2))
			   .append(des.charAt(b3))
   			   .append(equal);
			encount++;
			encount++;
		} else if (total == 1) {
			//残り1byte 8bit/6=1 rm2bit
			byte n1 = bytes[encount];
			int n123 = n1 << 4;
			int b1 = (n123 >>> 6) & 63;
			int b2 = n123 & 63;
			buf.append(des.charAt(b1))
			   .append(des.charAt(b2))
			   .append(equal)
			   .append(equal);
		}

		String reVal = buf.toString();

		char[] chval = reVal.toCharArray();
		for (int i = 0; i < chval.length; i++) {
			char x= chval[i];
			if (des.charAt(63) == x || des.charAt(62) == x || equal == x) {
				int xd = num_randam.nextInt(61);
				chval[i] = des.charAt(xd);
			}
		}
		String newval = new String(chval);
		int randumlen = len;
		if (newval.length() - len >= 0) {
			randumlen = len;
		} else {
			randumlen = 0;
		}
		String val = newval.substring(0, randumlen);
		
		System.out.println("引数: " + len);
		System.out.println("整形: " + reVal);
		System.out.println("最後: " + newval);
		System.out.println("カウント: " + newval.length());
		System.out.println("求められる文字数: " + len);
		System.out.println("出力する文字数: " + val.length());
		System.out.println("結果: " + val);
		System.out.println();
		
		return val;
	}
}

実行したらこんな感じになりました。
動いたから良かった。(^O^)

引数: 24
整形: zgUc///X///L///s///Z///3/6Bw//+a
最後: zgUcq3GXmPNLNlkskArZI4t3E6BwZYIa
カウント: 32
求められる文字数: 24
出力する文字数: 24
結果: zgUcq3GXmPNLNlkskArZI4t3

読もうと思っているもの。

たまたまネット見ていたら見つけた本。
大学のIT系の人がどういう本を読んでいるのかを知りたかったのだが、
それっぽいのが見つけられずに数年。
ようやく出会えた。
かなり嬉しい。頑張って読もう!!
最ももの凄く高額でA4サイズの本なので、図書館でレンタル中♪

オペレーティングシステムの概念

オペレーティングシステムの概念

ただ今6章まで読破。

java7のtry-with-resourceを書いてみる。

気が付けば、もう6月。
1年ぶりといってもいいほどノンアクティブ。
インドの話も書こうと思っていたのだが、忙しいのを言い訳にだんだんとログインすることすら
面倒になってしまい、そのまま放置。。
twitterも同様に1年以上放置。理由も同じでアプリのボタンを押すことすら面倒。
飽きっぽいのかな?
閑話休題
最近javaの1.7が出たそうで色々とパワーアップしている模様。
また、oracleによる買収によりjavaの試験もDBと同様に金銀銅メダルになるそうで。
そういうわけで、java7の新機能の一つtry-with-resourceを書いてみた。
本家サイトによると今までIOを閉じるのにfinallyで書いていたおまじないをjvmがやってくれるみたい。おかげでコードがすっきりし読みやすくなる模様。
以下、サンプルコード

package hogehoge;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ParamcheckMain {
	
	/** プロパティからデータを取得するための正規表現  */
	public static final Pattern PROPERTIES_PATTERN = Pattern.compile("^(\\w{6})\\p{Space}??=\\p{Space}??(.*)$");
	
	/** メッセージから代替引数を取得するための正規表現  */
	public static final Pattern MESSAGE_ARGS_PATTERN = Pattern.compile("\\{\\d{1}\\}");
	
	/** クラス名取得用正規表現  */
	public static final Pattern CLASS_NAME_PATTERN = Pattern.compile("class\\p{Space}(\\p{Upper}\\w{1,})\\p{Space}[extends|implements]??");
	
	/** 使用しているメッセージパターン */
	public static final Pattern USED_MESSAGE_PATTERN = Pattern.compile("XXMessageId\\.(\\w{6})\\.setParams\\(([^\\)]*)(\\)??;??)$");
	
	/** 行を跨いでいるパターン */
	public static final Pattern USED_MESSAGE_PATTERN2 = Pattern.compile("([^\\)]*)\\)??;");
	

	/**
	 * xx000111 = これは{1}テストです。
	 * 形式のプロパティからデータを取得する。
	 * 
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		
		String path = "C:\\workspace\\";
		
		String path2 = "C:\\workspace\\hoge\\properties\\test.properties";
		//プロパティファイルの取得、編集
		HashMap<String, PropertyCls> plist = getPropertiesList(path2);
		filterMsgArgsCount(plist);
		
		//javaクラスからデータ取得
		ArrayList<HashMap<String, ArrayList<MsgCls>>> allsrclist = getMsgFromSrc(path);
		//全データをチェック
		compareMsg(plist, allsrclist);
		
	}
	
	
	public static void compareMsg(HashMap<String, PropertyCls> pmap, ArrayList<HashMap<String, ArrayList<MsgCls>>> allsrclist) {
		
		//クラスに含まれるメッセージ群
		for(HashMap<String, ArrayList<MsgCls>> smap : allsrclist) {
			//
			Set<Entry<String, ArrayList<MsgCls>>> enset = smap.entrySet();
			for(Entry<String, ArrayList<MsgCls>> ent : enset) {
				//1クラスで使用していたメッセージ
				ArrayList<MsgCls> msglist = ent.getValue();
				for(MsgCls msgCls : msglist) {
					PropertyCls pclz = pmap.get(msgCls.messageId);
					System.out.println("メッセージID : " + msgCls.messageId);
					System.out.println("行数 : " + msgCls.lineNum);
					System.out.println("引数の数整合性 : " + compareArgs(msgCls.args, pclz.count));
					System.out.println("メッセージID整合性 : " + msgCls.messageId.equals(pclz.messageId));
				}
			}
		}
	}
	
	public static boolean compareArgs(String args, int argsNum) {
		String cargs = args.trim();
		String[] arargs = cargs.split(",");
		return arargs.length == argsNum;
	}
	
		
	/**
	 * プロパティファイルからメッセージを取得する。
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static HashMap<String, PropertyCls> getPropertiesList(String path) throws IOException {
		try(BufferedReader in = new BufferedReader(new FileReader(path))) {
			HashMap<String, PropertyCls> pmap = new HashMap<String, PropertyCls>();
			String tmp;
			while((tmp = in.readLine()) != null) {
				Matcher m = PROPERTIES_PATTERN.matcher(tmp);
				while(m.find()) {
					
					PropertyCls clzz = new PropertyCls();
					clzz.messageId = m.group(1);
					clzz.message = m.group(2);
					clzz.messageflg = true;
					
					pmap.put(m.group(1), clzz);
				}
			}
			return pmap;
		}
	}
	
	/**
	 * 
	 * @param plist
	 */
	public static void filterMsgArgsCount(HashMap<String, PropertyCls> pmap) {
		Set<Entry<String, PropertyCls>> enset = pmap.entrySet();
		for(Entry<String, PropertyCls> ent : enset) {
			PropertyCls pcls = ent.getValue();
			Matcher m = MESSAGE_ARGS_PATTERN.matcher(pcls.message);
			int count = 0;
			while(m.find()) {
				count++;
			}
			pcls.count = count;
		}
	}
	
	/**
	 * 
	 * @param path
	 * @throws Exception
	 */
	public static ArrayList<HashMap<String, ArrayList<MsgCls>>> getMsgFromSrc(String path) throws Exception {
		File root = new File(path);
		File[] flist = root.listFiles();
		ArrayList<String> srclist = new ArrayList<String>();
		
		//パッケージごとにソースをチェック。
		for(File child : flist) {
			File file2 = findDir(child, "src");
			findSrc(file2, ".java", srclist);
		}
		return filterJavaSrc(srclist);
	}
	
	/**
	 * 
	 * @param srclist
	 * @return
	 * @throws IOException
	 */
	public static ArrayList<HashMap<String, ArrayList<MsgCls>>> filterJavaSrc(ArrayList<String> srclist) throws IOException {
		ArrayList<HashMap<String, ArrayList<MsgCls>>> alllist = new ArrayList<HashMap<String, ArrayList<MsgCls>>>();

		for(String path : srclist) {
			HashMap<String, ArrayList<MsgCls>> list = getJarsrc(path);
			if(list == null) {
				continue;
			}
			alllist.add(list);
		}
		return alllist;
	}
	
	/**
	 * javaソースからクラス名とメッセージID、メッセージの引数、それらが出現した行数をまとめたものをリスト化し、
	 * クラス名をキーとするハッシュマップに格納する。
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static HashMap<String, ArrayList<MsgCls>> getJarsrc(String path) throws IOException {
		try (BufferedReader in = new BufferedReader(new FileReader(path))) {
			HashMap<String, ArrayList<MsgCls>> list = new HashMap<String, ArrayList<MsgCls>>();
			
			ArrayList<MsgCls> msglist = new ArrayList<MsgCls>();
			String clsName = "dummy";
			int count = 0;
			boolean clsflg = true;
			String tmp;
			while((tmp = in.readLine()) != null) {
				count++;
				if(clsflg) {
					Matcher m = CLASS_NAME_PATTERN.matcher(tmp);
					while(m.find()) {
						clsName = m.group(1);
						clsflg = false;
					}
				}
				if(!clsflg) {
					Matcher m = USED_MESSAGE_PATTERN.matcher(tmp);
					while(m.find()) {
						MsgCls msgCls = new MsgCls();
						msgCls.messageId = m.group(1);
						
						String args = m.group(2);
						String sufx = m.group(3);

						if(args == null || "".equals(args.trim())) {
							if(sufx.contains(";")) {//引数なし
								msgCls.args = "";
								msgCls.lineNum = count;
							} else {//行跨ぎ
								String tmp2 = in.readLine();
								Matcher m2 = USED_MESSAGE_PATTERN2.matcher(tmp2);
								m2.find();
								msgCls.args = m2.group(1);
								msgCls.lineNum = ++count;
							}
						} else if(!sufx.contains(";")) {//行跨ぎ
							String tmp2 = in.readLine();
							Matcher m2 = USED_MESSAGE_PATTERN2.matcher(tmp2);
							m2.find();
							msgCls.args = new StringBuilder(args.trim()).append(m2.group(1).trim()).toString();
							msgCls.lineNum = count++;
						} else {//通常
							msgCls.args = m.group(2);
							msgCls.lineNum = count;
						}
						msglist.add(msgCls);
					}
				}
			}
			if(clsName.equals("dummy") || msglist.size() == 0) {
				return null;
			}
			list.put(clsName, msglist);
			return list;
		}
	}
	
	
	/**
	 * 1階層内で指定したディレクトリのファイル返す。
	 * @param path
	 * @param dirName
	 * @return
	 */
	public static File findDir(File path, String dirName) {
		if(path == null || path.isFile() || path.getName().startsWith(".") ) {
			return null;
		}
		File[] filelist = path.listFiles();
		for(File fle : filelist) {
			if(fle.isFile() || fle.isHidden() || fle.getName().startsWith(".")) {
				continue;
			}
			if(fle.isDirectory() && fle.getName().startsWith(dirName)) {
				return fle;
			}
		}
		return null;
	}
	
	/**
	 * 階層を進んで該当サフィックスを持つファイルの絶対パスをリストへ格納する。
	 * @param path 
	 * @param suffix 
	 * @param srclist 
	 */
	public static void findSrc(File path, String suffix, ArrayList<String> srclist) {
		if(path == null) {
			return;
		}
		File[] filelist = path.listFiles();
		for(File fle : filelist) {
			if(fle.isDirectory() && !fle.isHidden()) {
				findSrc(fle, suffix, srclist);
			}
			if(fle.isFile() && fle.getName().endsWith(suffix)) {
				srclist.add(fle.getAbsolutePath());
				continue;
			}
		}
	}
	
	/**
	 * クラスから取得したメッセージ
	 */
	static class MsgCls {
		public String messageId;//メッセージID
		public String args;//メッセージ引数
		public int lineNum;//行数
		public int count;//メッセージに含まれる代替引数の個数
	}	
	
	/**
	 * プロパティクラス
	 */
	static class PropertyCls {
		public String messageId;//メッセージID
		public String message;//メッセージ
		public int count;//メッセージに含まれる代替引数の個数
		public boolean messageflg;//コメントになっているか否か
	}
	
}

try with使うついでに何かを書いてみた。
これは、プロパティファイルに書かれている、メッセージID = メッセージがきちんとソースで使用されているかを確認する。
メッセージに代替引数が入っている場合({1}とか)、ソースでメッセージにパラメータを設定しているか確認するもの。
引数を代入するときのメソッドが改行を跨いでいる場合も考慮したつもり。
これ多分、スレッド毎にパッケージで振り分け、タスク1:内部のソースを取得・編集後、タスク2:メッセージチェックと分割させてやれば早く終わるのだろう。
スレッドプール作ってmainでタスク1をプールに投げ、タスク1が終わったスレッドがタスク2をプールに投げればいいかな。
ここまできちんとタスク分けしたら、ちまたで話題のmap shuffle reduceとか使えるのかなあ。
どちらもそのうちやってみようと思う。
気が向いたらJquery体験も書いてみよう。
あと、最近気づいたのだけれど、eclipseのコンソールってinも兼ねていたのね。。
いじょ....org

気が付けば。。

もう9月だ。
時が経つのは早い。
計画を立てて、きちんと物事をやっていかなければ、気付いたら人生が終わっている。
最近計画立てて何もやっていなかったと反省中。
だって、達成しても達成感とか何も感じなかったし、仕事がステップアップすることもなかったし。
とにかく、自分の人生を有意義に幸せにするために頑張ろう。
そのための計画を立てることにしよう。
というわけで、来週からインドへ行ってきます。
その中継を書ければいいなあ。