My Photo

October 07, 2014

秀丸エディタのTSV・CSVモード

秀まるおのホームページ(サイトー企画)-秀丸エディタの紹介(Ver8.00対応版)(TSVモード,CSVモード)

長年、秀丸エディタを使っていたがこれは知らなかった。便利^^
メニューの表示(V)>タブストップの下に「TSVモード(タブ区切り)」と「CSVモード(カンマ区切り)」がある。TSV・CSVファイルをちょっと参照したり修正したりしたいとき、きれいに列をそろえて表示してくれる。
欲を言えばCSVモードの時にカンマは値の直前にあった方がカンマの位置も揃って見栄えがいいかなと。まあ少し気になるという程度のことで、値の位置さえ揃っていれば実用上問題ないんだけど。

Csv_mode

September 16, 2014

秀丸エディタマクロの検索・置換系命令のinselectオプション

マクロを作っていて replaceall 命令が思ったように動いてくれない。調べたら自分がinselectオプションについて間違って覚えていたことがわかった。
秀丸マクロの検索・置換系命令のinselectオプションを
「選択範囲のみ検索/置換する」
だと思っていたが、実際には 「選択範囲を含む行のみで検索/置換する」
という動作をする。
つまり、検索・置換命令における「選択した範囲内」というのは行単位の選択であり、行の途中から途中の選択には対応していない。選択範囲(表示色が反転している範囲)の外でも、選択範囲と同じ行ならば検索・置換の対象となる。
なぜわかったかというと、マクロではなく検索ダイアログによる検索で「選択した範囲内のみ」をチェックし、行の途中から途中までが選択された状態で検索を実行すると

「選択した範囲のみ」の範囲は行単位(改行までの範囲)となります。

という内容のメッセージボックスが出たから。あの、こういうのは実行時の警告じゃなくてマクロのリファレンス(ヘルプ)に書いておいてくださいorz

December 24, 2013

秀丸エディタのマクロにおける配列

秀丸エディタのマクロには配列がなくて不便だなと思っていたが、ちゃんとあった。
他の言語にあるような波括弧を使って複数の要素で一気に初期化することはできないので使い勝手はよくないが、ないより全然いい。
以下、簡単な例。1行ずつ"aaa","bbb","ccc"を挿入する。

$array[0] = "aaa";
$array[1] = "bbb";
$array[2] = "ccc";

#i = 0;
while ($array[#i] != "") {
	insert $array[#i];
	insert "\n";
	#i = #i + 1;
}

October 07, 2013

正規表現メモ

正規表現についてのメモ。確認はPHPのPCRE関数と秀丸エディタ(Ver.8.13)の検索ダイアログで行った。
それ以外の処理系(JavaScript、Javaなど)でも大体同じように使えると思う。

グループ化、非キャプチャグループ

正規表現を "(" と ")" で囲んでグループ化することができる。例えば "ab" が1回以上繰り返しさらに "cd" が1回以上繰り返すという正規表現は "(ab)+(cd)+" となる。
グループ化した部分にマッチする文字列は、グループより後ろの部分で\1,\2,\3,etc...の形式で後方で参照できる。"\"の後ろの数字はグループ化された順番。

正規表現 意味 文字列 マッチする部分
(ab)+(cd)+ "ab" が1回以上繰り返し
その後に"cd"が1回以上繰り返す。
ababcdcdcd ababacdcdcd
([a-zA-Z]+)
(\d+)-\2\1
英字1個以、次に数字1個以上、次にハイフン、
次に前に現れたのと同じ数字1個以上の文字列、 最後に前に出現した英字1個以上の文字列
abc123-123abc,xyz789-xyz789 abc123-123abc

また、グループ化されたものは\1,\2,\3,etc...の形式で後方で参照できる。
"(?:" と ")" で囲めばグループ化はされるが、後方参照はできなくなる。これを非キャプチャグループと呼ぶ。後方参照が不要な場合はこれを用いてもよい。
非キャプチャグループについては検索して調べると、非キャプチャグループの方が単なるグループより動作が速いと記述も変わらないという記述もある。

OR条件

正規表現で "|" はOR(論理和)を表している。
単純に "ABまたはCD" という意味の正規表現は "AB|CD" でよい。"(AB|CD)" や "((AB)|(CD))" のようにグループ化する必要ない。
しかし、"ABC123" または "ABC456" という風に共通部分があって一部がOR条件の場合は、"ABC(123|456)" のようにグループ化する必要がある。グループ化せずに "ABC123|456" では「"ABC123" または "456"」という意味になる。

後方一致指定、後方不一致指定、前方一致指定、前方不一致指定

後方一致指定(肯定先読み)
特定のパターンの後にさらに特定のパターンが続くことを表すアンカー。
"(?=<パターン>)" と記述する。
後方不一致指定(否定先読み)
特定のパターンの後にさらに特定のパターンが続かないことを表すアンカー
"(?!<パターン>)" と記述する。
前方一致指定(肯定後読み)
特定のパターンの前にさらに特定のパターンが存在することを表すアンカー
"(?<=<パターン>)" と記述する。
前方不一致指定(否定後読み)
特定のパターンの前にさらに特定のパターンが存在しないことを表すアンカー
"(?<!<パターン>)" と記述する。
正規表現 意味 文字列 マッチする部分
abc(?=xyz) 後ろに"xyz"が来る"abc" abcxyz abc
abc(?!xyz) 後ろに"xyz"以外のパターンが来る"abc" abcijk abc
(?<=abc)xyz 前に"abc"がある"xyz" abcxyz xyz
(?<!abc)xyz 前に"abc"以外のパターンがある"xyz" lmnxyz xyz

これらの正規表現はアンカーであり、マッチする文字列には含まれない。アンカーとは位置を指定する正規表現である。
#他のアンカーには "^" や "$" などがある。
また、これらの正規表現の後に正規表現を続けると、AND条件になる。例を示す。

正規表現 意味 文字列 マッチする部分
abc(?=\w+) 後ろに単語が続く"abc" abcdef123 abc
abc(?=\w+)[a-z]+ abcdef123 abcdef
正規表現"abc(?=\d+)[^a-z] abcdef123 マッチしない
2014/06/16追記:JavaScriptの正規表現では前方一致指定と前方不一致指定が使えない。

\w

"\w" は単語文字の文字クラス。半角の単語文字は英数字とアンダースコア、と思っていたら、処理系やロケールによっては2バイト文字やアルファベット以外の文字も含まれるらしい。
「"\w" と "[:alnum:]" は同じ」という明らかに間違った説明をしているサイトがいくつかあった。何かのドキュメントに「"\w" と "[:alnum:]" は同じ」とあって、それをもとにして書かれた内容なのかなと思う。

秀丸エディタの検索ダイアログとマクロの検索コマンドの違い

秀丸エディタの検索ダイアログで正規表現を使った検索する場合、「正規表現」だけをチェックして実行すると大文字と小文字の違いを無視する。大文字と小文字を区別したい場合は「大文字/小文字の区別」にもチェックを入れる。
一方、マクロでの検索系コマンド(searchdownなど)では "regular" オプションを付ければ大文字と小文字を区別する。

searchdown <検索文字列>, regular;
"regular" があれば "casesense" はあってもなくても同じ。"regular" かつ "nocasesense" の場合は大文字と小文字を区別しない。
searchdown <検索文字列>, regular, nocasesense;
同じ検索処理なのに検索ダイアログとマクロで使い方の統一性がない。

秀丸エディタの正規表現の注意事項

秀丸エディタの検索・置換・マクロで使う正規表現(以下、「秀丸Regexp」と呼称)と自分がよく使うPHPのPCRE関数や、その他なプログラミング言語の正規表現(以下、「一般Regexp」と呼称)は大体は同じだが、一部独特なところがあるのでメモっておく。
一般Regexpでは前に書いたように "\w" は英数字とアンダースコアを含む文字クラスである。
#それ以外の文字を含む場合もある。
秀丸Regexpでは "\w" は英字とアンダースコアであり、数字を含まない。一般Regexpの "\w" にあたる英数字とアンダースコアからなる文字クラスは "\c" である。
また、一般Regexpでは "\b" は空白文字の文字クラスワード境界のアンカーまたはバックスペース文字であるが、秀丸エディタではバックスペース文字という意味しかない。一般Regexpで "\b" がアンカーがバックスペースかの区別は、角括弧("[]")で囲まれた文字クラスの中ではバックスペース、文字クラスの外ではアンカーである。秀丸エディタには空白文字のもクラスがないが、"\<" と "\>" という英単語の始まり・終わりのアンカーがある。
例として、下記の文字列を考える。

_ab3 C_D4,eF_5

この文字列の単語を検索したい。つまり "_ab3", "C_D4", "eF_5" の3つがマッチするように検索したい。
PHPのPCRE関数であれば以下のコードでできる。難しくはない。

preg_match_all('/\b\w+\b/', "_ab3 C_D4,eF_5", $matches);

秀丸Regexpで同じことをする正規表現は、結構考えてしまった。文字クラス "\b" は使えない。
"\c+" では単語全体だけではなく単語の部分文字列("ab3", "b3", "3", "_D4",etc...) にもマッチする。
"\<\c+\>" では、"\<""\>" が「単語」の始まり・終わりではなく「英単語」の始まり・終わりなので、数字を除いた "_ab", "C_D", "eF_" にマッチする。
"(?<=[^\c])\c+(?=[^\c])" とすると行頭の単語以外はマッチする
"(?<=^|[^\c])\c+(?=[^\c])" で行頭の単語を含めてすべての単語にマッチする。期待した動作だ。前方一致指定の中に行頭を表すアンカー "^" が入っているのが若干変な気もするが。

以前の記事の補足説明

以前に秀丸エディタの正規表現について書いた記事で例として挙げた下記の正規表現について、説明不足だったのでここで説明する。
下記の正規表現は "all""see" などの「1文字目≠2文字目かつ2文字目=3文字目の3文字の英単語」にマッチする。

\<([a-z])((?!\1)[a-z])\2\>

・先頭の "\<" と末尾の "\>" はそれぞれ単語の始まり、終わりを示す。これらがあるため、例えば3文字の単語にはマッチするが4文字以上の単語の一部分の3文字("tall" や "seem" など)にはマッチしない。
"([a-z])" はアルファベット小文字の1文字。後方参照のためグループ化してある。
"((?!\1)[a-z])" は後方不一致指定(否定先読み)を使っている。"\1" は前のグループ、つまりこの部分の前にあるアルファベット小文字1文字である。"(?!\1)" はその1文字ではないものが後にあるというアンカー。"[a-z]" はアルファベット小文字1文字であり、後方不一致指定の後にパターンがある場合は、後方不一致指定とそのパターンがAND条件となる。よって、"(?!\1)[a-z]" は「前のアルファベット小文字1文字とは異なるアルファベット小文字1文字」となる。アンカーである後方不一致指定だけではなく後に "[a-z]" があるため1文字とマッチし、後方参照のためにグループ化されている。
"\2" は前のグループ、"(?!\1)[a-z]" にマッチしたアルファベット小文字1文字である。
・以上から、全体としては「1文字目と2文字が異なり、2文字目と3文字が同じである3文字のアルファベット小文字の単語」にマッチする。

参考文献:秀丸エディタヘルプ

参考サイト:
正規表現言語 - クイック リファレンス
Windowsユーザーに教えるLinuxの常識(6):使うほどに良さが分かる正規表現 (2/2) - @IT
正規表現(肯定先読み、否定先読み、肯定戻り読み、否定戻り読み) - satosystemsの日記:
正規表現の先読み・後読みを極める! - あらびき日記

October 03, 2013

連続した日付、時間の文字列を作る秀丸マクロのサブルーチン

CSVファイルや大量のINSERT文などテストデータを作る際に連続した日付や時間を作りたかったので秀丸マクロのサブルーチンを3つ作ってみた。
いずれもformatNumという数値に前ゼロを付けて固定長の文字列を返すサブルーチンを使用している。

incrementDate1は年、月、日を渡してその+1日のYYYY/MM/DD形式の日付文字列を返す。年、月をまたぐことができる。閏年にも対応。
ただ、引数が3つあって使い方や下のincrementDate2よりやや面倒。

// 日付をインクリメント。月跨ぎ、年跨ぎ可。
// 引数1:年(整数)
// 引数2:月(整数)
// 引数3:日(整数)
// 戻り値:YYYY/MM/DD形式の文字列の日付
dateIncrement1:
	##iYear  = ##1;
	##iMonth = ##2;
	##iDay   = ##3;
	
	##iDay = ##iDay + 1;
	
	// 31日ある月
	if (##iMonth == 1 || ##iMonth == 3 || ##iMonth == 5
	    || ##iMonth == 7 || ##iMonth == 8 || ##iMonth == 10
	    || ##iMonth == 12) {
		if (##iDay == 32) {
			##iDay = 1;
			if (##iMonth == 12) {
				##iYear = ##iYear + 1;
				##iMonth = 1;
			} else {
				##iMonth = ##iMonth + 1;
			}
		}
		
	// 30日ある月
	} else if (##iMonth == 2) {
		if (##iDay == 31) {
			##iDay = 1;
			##iMonth = ##iMonth + 1;
		}
	
	// 2月
	} else {
		if (##iYear % 4 == 0) {
			if (##iDay == 30) {
				##iDay = 1;
				##iMonth = ##iMonth + 1;
			}
		} else {
			if (##iDay == 29) {
				##iDay = 1;
				##iMonth = ##iMonth + 1;
			}
		}
	}
	
	call formatNum ##iMonth, 2;
	$$strMonth = $$return;
	call formatNum ##iDay, 2;
	$$strDay = $$return;
	
	$$strDate = str(##iYear) + "/" + $$strMonth + "/" + $$strDay;
	return $$strDate;

incrementDate2は年の日数(1月1日なら1)を渡し、その日のMM/DD形式の日付文字列を返す。年はまたげない。月またぎは可能。
引数は1つで使い方は簡単。

// 日付をインクリメント。月跨ぎ可。
// 引数1:年の日数(整数)。
//         1ならば戻り値は"01/01"、32ならば"02/01"となる。
// 戻り値:MM/DD形式の文字列の日付
dateIncrement2:
	##iDayNum   = ##1;
	
	##uruuBi = 0;	// うるう年の場合、1
	
	if (##iDayNum >= 31) {
		$$strMonth = "01";
		##iDay = ##iDayNum % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (59 + ##uruuBi)) {
		$$strMonth = "02";
		##iDay = (##iDayNum - 31) % (28+ ##uruuBi);
		if (##iDay == 0) { ##iDay = (28+ ##uruuBi); }
	} else if (##iDayNum >= (90 + ##uruuBi)) {
		$$strMonth = "03";
		##iDay = (##iDayNum - (59 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (120 + ##uruuBi)) {
		$$strMonth = "04";
		##iDay = (##iDayNum - (90 + ##uruuBi)) % 30;
		if (##iDay == 0) { ##iDay = 30; }
	} else if (##iDayNum >= (151 + ##uruuBi)) {
		$$strMonth = "05";
		##iDay = (##iDayNum - (120 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (181 + ##uruuBi)) {
		$$strMonth = "06";
		##iDay = (##iDayNum - (151 + ##uruuBi)) % 30;
		if (##iDay == 0) { ##iDay = 30; }
	} else if (##iDayNum >= (212 + ##uruuBi)) {
		$$strMonth = "07";
		##iDay = (##iDayNum - (181 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (243 + ##uruuBi)) {
		$$strMonth = "08";
		##iDay = (##iDayNum - (212 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (273 + ##uruuBi)) {
		$$strMonth = "09";
		##iDay = (##iDayNum - (243 + ##uruuBi)) % 30;
		if (##iDay == 0) { ##iDay = 30; }
	} else if (##iDayNum >= (304 + ##uruuBi)) {
		$$strMonth = "10";
		##iDay = (##iDayNum - (273 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else if (##iDayNum >= (334 + ##uruuBi)) {
		$$strMonth = "11";
		##iDay = (##iDayNum - (304 + ##uruuBi)) % 30;
		if (##iDay == 0) { ##iDay = 30; }
	} else if (##iDayNum >= (365 + ##uruuBi)) {
		$$strMonth = "12";
		##iDay = (##iDayNum - (334 + ##uruuBi)) % 31;
		if (##iDay == 0) { ##iDay = 31; }
	} else {	// エラー
		$$strMonth = "99";
		##iDay = 99;
	}
	
	call formatNum ##iDay, 2;
	$$strDay = $$return;
	
	$$strDate = $$strMonth + "/" + $$strDay;
	return $$strDate;

incrementTimeは0時0分を0として日の分数を渡しHH:MM形式の時間の文字列を返す。

// 分をインクリメント
// 引数1:0時0分からの経過時間。分(整数)。
//         0なら戻り値は"00:00"、90ならば"01:30"となる。
// 戻り値:HH:MM形式の時間の文字列
timeIncrement:
	##i  = ##1;
	
	##i = ##i % (60 * 24);
	##iHour = ##i / 60;
	##iMin  = ##i % 60;
	
	call formatNum ##iHour, 2;
	$$strHour = $$return;
	
	call formatNum ##iMin, 2;
	$$strMin = $$return;
	
	$$strTime = $$strHour + ":" + $$strMin;
	return $$strTime;

以下はこれらのサブルーチンを使ってINSERT分をたくさん作るマクロ。

// 日付、時間をインクリメント
disabledraw;
#start = 1;	// 初期値
#end =   15;	// 終了値

// dateIncrement1用パラメータ
#year  = 2013;
#month = 12;
#day   = 25;

// dateIncrement2用パラメータ
#day2 = 55;

// timeIncrement用パラメータ
#min = 290;

#i = #start;
while (#i <= #end) {
	call formatNum #i, 2;
	$num = $$return;

	call dateIncrement1 #year, #month, #day;
	$strDate = $$return;

	call dateIncrement2 #day2;
	$strDate2 = $$return;

	call timeIncrement #min;
	$strTime = $$return;

	insert "INSERT INTO table1 (id, date1, date2, time1, amount) VALUES ";
	insert "(10" + $num + ", '" + $strDate + "', '"
           + $strDate2 + "', '" + $strTime + "', " + str(#i) + ");\n";

	#i = #i + 1;
	
	#day2 = #day2 + 1;	// +1日
	
	#min = #min + 5;	// +5分
	
	#year  = val(midstr($strDate, 0, 4));	// 年を整数で抽出
	#month = val(midstr($strDate, 5, 2));	// 月を整数で抽出
	#day   = val(midstr($strDate, 8, 2));	// 日を整数で抽出
}
enabledraw;
endmacro;


// 数値に前ゼロを付けて指定した桁数の文字列を作成する。
// 引数1:素になる数値
// 引数2:桁数
// 戻り値:引数1に必要ならば前ゼロを付けて引数2の桁数にした文字列。
formatNum:
	##num1 = ##1;
	$$str1 = str(##num1);
	##keta = ##2;
	##len1 = strlen($$str1);
	while (##len1 < ##keta) {
		$$str1 = "0" + $$str1;
		##len1 = ##len1 + 1;
	}
	return $$str1;


// 日付をインクリメント。月跨ぎ、年跨ぎ可。
// 引数1:年(整数)
// 引数2:月(整数)
// 引数3:日(整数)
// 戻り値:YYYY/MM/DD形式の文字列の日付
dateIncrement1:
// 省略

// 日付をインクリメント。月跨ぎ可。
// 引数1:年の日数(整数)。
//         1ならば戻り値は"01/01"、32ならば"02/01"となる。
// 戻り値:MM/DD形式の文字列の日付
dateIncrement2:
// 省略

// 分をインクリメント
// 引数1:0時0分からの経過時間。分(整数)。
//         0なら戻り値は"00:00"、90ならば"01:30"となる。
// 戻り値:HH:MM形式の時間の文字列
timeIncrement:
// 省略

出力は以下のようになる。

INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1001, '2013/12/26', '02/24', '04:50', 1);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1002, '2013/12/27', '02/25', '04:55', 2);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1003, '2013/12/28', '02/26', '05:00', 3);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1004, '2013/12/29', '02/27', '05:05', 4);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1005, '2013/12/30', '02/28', '05:10', 5);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1006, '2013/12/31', '03/01', '05:15', 6);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1007, '2014/01/01', '03/02', '05:20', 7);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1008, '2014/01/02', '03/03', '05:25', 8);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1009, '2014/01/03', '03/04', '05:30', 9);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1010, '2014/01/04', '03/05', '05:35', 10);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1011, '2014/01/05', '03/06', '05:40', 11);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1012, '2014/01/06', '03/07', '05:45', 12);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1013, '2014/01/07', '03/08', '05:50', 13);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1014, '2014/01/08', '03/09', '05:55', 14);
INSERT INTO table1 (id, date1, date2, time1, amount) VALUES (1015, '2014/01/09', '03/10', '06:00', 15);

May 31, 2009

秀丸マクロサンプル

1.同じような内容を繰り返し書き出すマクロのベース
・テストデータのためにSQLのINSERT文をたくさん作る場合など。

//
// 同じような内容を繰り返し書き出すマクロのベース
// これをコピーして適宜修正し、名前を変えて保存して使用する。
//
 
#start = 1;	// 初期値
#end = 201;	// 終了値
 
#i = #start;
while (#i <= #end) {
	call formatNum #i, 3;
 
	insert "insert into TEST_TABLE values('";
	insert $$return;
	insert "');";
	
	insert "\n";
	#i = #i + 1;
}
endmacro;
 
 
// 数値に前ゼロを付けて指定した桁数の文字列を作成する。
// 引数1:素になる数値
// 引数2:桁数
// 戻り値:引数1に必要ならば前ゼロを付けて引数2の桁数にした文字列。
formatNum:
	##num1 = ##1;
	$$str1 = str(##num1);
	##keta = ##2;
//	message $$str1;
	##len1 = strlen($$str1);
	while (##len1 < ##keta) {
		$$str1 = "0" + $$str1;
		##len1 = ##len1 + 1;
	}
	return $$str1;

2.行の先頭から末尾まで行ごとに何かをするマクロ

//
// 先頭から最終行まで行単位で何かをする。
//
 
gofiletop;
 
while (true) {
	#nowLine = lineno;
 
// ここに行に対して行う処理を記述
	
	// 次の行に移動
	golineend2;
	right;
 
	// 前の行と次の行の行番号が変わらない場合は終了
	#nextLine = lineno;
	if (#nowLine == #nextLine) {
		message "end";
		break;
	}
}
 
endmacro;

May 10, 2008

秀丸のタグ付き正規表現

検索条件のカッコでくくった部分。複数ある場合は前から\1,\2,\3,...となる。

例:「<名前><スペース><数字>」を「<名前><タブ文字><数字>」に置換する場合
検索:([a-z0-9]+) ([0-9])
置換:\1\t\2

例:allやseeのように、1文字目と2文字目が異なり、2文字目と3文字目が一致するものを検索する場合
検索:\<([a-z])((?!\1)[a-z])\2\>

April 2017
Sun Mon Tue Wed Thu Fri Sat
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof