Memorandums?

This blog is written about technical-discoveries and daily-events.

Windowsとcmdにおける文字処理

Java Windowsプログラムを書いているときに、
なかなかWindowsOSとcmdには苦戦しました。

Windowsでできること

  • ファイル名にゼロ幅文字可能
  • ゼロ幅文字を付加すれば、同名ファイルとならない

Windowsでできないこと

  • ファイル名に特殊文字(",/,\,|,<,>,:)が付けられない
  • 認識できるUnicode上にゼロ幅文字が存在しない
  • ファイル名の末尾に半角空白文字を付けられない
  • 同名ファイルはNG

cmdでできること

cmdでできないこと

  • ゼロ幅文字関連Unicodeは認識不可能
  • 制限される記号文字が多い


以上を念頭において、Javaプログラミングをしなくてはなりません。
今回のプログラムは、

1ファイルを1メッセージとし、ディレクトリ内のファイル一覧を更新し続けることで
コンソール(cmd)上でチャットをおこなう

というものです。

問題1. 同じメッセージ

2個以上同じメッセージがあるときの処理が問題です。
まず、メッセージをファイルに保存しなくてはなりません。
これは、同名メッセージの回数分、ゼロ幅文字を文字列終端に付加することによって解決!!!
と、思いきや!!!
さすがは、ダメダメOS, Windowsです。
ゼロ幅文字Unicodeが認識しません、ダメでした。(Unix環境なら可)
仕方ない、半角スペースを後ろにつけよう!
と、、ダメです、ファイル名の後ろには半角スペースは付けられません。
ということで、結局全角スペースを後ろにつけることで解決しました。
全角スペースは、プログラム中に" "なんて書かずに、'\u3000'と書くことをおすすめします。
そして、コンソール表示時の問題。
全角スペースをつけていると、同じメッセージが大量に来た時、
コンソール上で改行されてしまいます。
よっぽで無い限り、改行しませんが、やっぱりここは完璧主義。全角スペースはなくしましょう。
これは簡単です。

str = str.replaceAll('\u3000'+"+$", "");

正規表現でらくらく解決♪

問題2. 特殊文字対応

特殊文字(",/,\,|,<,>,:)はファイル名につけられないため、
メッセージに打たれると、エラーとなります。ただし!!!
全角はOKです。全角に変換します。コロンの場合、

str = str.replace('\u003a', '\uff1a');

これでOKです。
表示時も全角で表示することを防ぐため、
表示するときは、逆に戻します。

問題3. スケジュール実行の一時停止

一定時間おきに実行させるスレッドを一時停止させる場合、
TimerTaskでは通用しません。

Runnable task = new Read();
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture future = scheduler.scheduleAtFixedRate(task, 0, time, TimeUnit.MILLISECONDS);

これで大丈夫です。

問題4. ファイルタイムテーブルの管理

ファイル名と作製時刻を同期させるのに、はじめに思いつくのはMapです。
ただ、Mapはソートが難しいです。
TreeTableなどなら、Keyをテンプレートにしたがって、ソートできますが、
非常に面倒です。
そこで、リストで実装しました。

time = new ArrayList<>();
msg = new ArrayList<>();
list = dir.listFiles();

for (int i=0; i < list.length; i++) {
	lastModified = list[i].lastModified();
	title = list[i].getName();

	if (uploadTime >= lastModified) {
		continue;
	}
	if (time.isEmpty()) {
		time.add(lastModified);
		msg.add(title);

		continue;
	}
	int size = time.size();
	for (int j=0; j < size; j++) {
		if (lastModified < time.get(j)) {
			time.add(j, lastModified);
			msg.add(j, title);
			break;
		} else if (j == size-1) {
			time.add(lastModified);
			msg.add(title);
			break;
		}
	}
}
for (int i=0; i < msg.size(); i++) {
	System.out.println(msg.get(i));
}
if (time.size() != 0)
	uploadTime = time.get(time.size()-1);

こんな感じで実装できます。

問題5. 諸問題

Fileクラスからディレクトリ生成する場合、
最後に\を付けないほうがいいです。たとえば、

C:\Users\Owner\Folder

とします。そのフォルダにファイルを作る場合は、

DIR + File.separator + File.txt

などとします。



結論

Windowsとcmdは面倒。