Excel主義者はきれいな表が好き(見かけだけ)

####Excel主義者はきれいな表が好き(単に見かけだけ)
Excel主義者はともかくきれいな表が好き。きれいというのは、単に見かけだけで、セル結合したり、罫線で囲った表は、再利用がほとんど不可能だ。

####この1カ月、ともかくExcelの表に苦しんだ。

そもそも罫線なのか図形なのか不明。
罫線で囲んだ文字を移動しようとすると、罫線がついてこない。
罫線で囲んだ文字がフォントを変更すると一部見えなくなるが、罫線を拡大できない。
罫線のある表を拡大縮小できない。
罫線のある表を無理に拡大縮小すると、枠の罫線がでこでこになる。
30ピクセルのExcel方眼紙を突如20ピクセルに変更することになり、図が全部縦長に。シネスコの映画をスタンダードにしたみたい。
結合してあるので、一部だけを移動できない。謎のメッセージ。
結合してある表を選択してコピーしてもコピーできない…。コピーできないっていったいどういうことだ!

####もうへとへとです。
そこにとなりからExcel主義者の軽快なキーボード操作(単調なコピーペーストと移動)の繰り返しがリズミカルに響いてくる。
この単調なリズムは、究極の手作業をしている感じ。

さて、そう歎いていてもしかたない。プログラムでなんとかできることはしようということで、やってみた。


####まずテキストは、こんな感じ。
```
見出し	１．画面コントロール

ID/2	名称/9	コントロール名/6	I・O/2	必須/2	説明/20	取得DB名/5	取得カラム名/5
1	タイトル	ラベル	O	-	タイトルを示すラベル。	-	-
2	ユーザID	ラベル	O	-	IDの入力位置を示すラベル。	-	-
3	ユーザID	テキストボックス	O	-	登録用のユーザIDを表示するテキストボックス。変更不可。	LoginDB	ID
4	ユーザ名	ラベル	O	-	ユーザ名の入力位置を示すラベル。	-	-
5	ユーザ名	テキストボックス	I	○	登録用のユーザ名を入力するテキストボックス。	LoginDB	UserName
6	パスワード	ラベル	O	-	PWの入力位置を示すラベル。	-	-
7	パスワード	Hiddenテキストボックス	I	-	PWを入力するテキストボックス。	-	-
8	パスワード（確認）	ラベル	O	-	PWの入力位置を示すラベル。	-	-
9	パスワード（確認）	Hiddenテキストボックス	I	-	確認用PWを入力するテキストボックス。入力文字は分からないように表示する。	-	-
10	キャンセル	ボタン	I	-	処理を中止するボタン。一覧に戻る。	-	-
11	変更	ボタン	I	-	入力内容をチェックして変更するボタン。一覧に戻る。	-	-

見出し	２．イベント処理

No./2	種類/10	説明/39
1	変更ボタン押下時	１．入力チェック（3.2チェック参照）☆　OKの場合２へ。☆　NGの場合エラーメッセージを表示。☆２．ユーザ名チェック（3.2チェック参照）☆　NGの場合エラーメッセージを表示。☆３．画面遷移。☆・３．１　ユーザ変更OKの場合管理者：ユーザ管理（一覧）画面に遷移。☆　NGの場合エラーメッセージを表示。☆・３．２　ユーザ変更OKの場合ユーザ：画面に遷移。☆　NGの場合エラーメッセージを表示。
2	キャンセルボタン押下時	２．画面遷移。☆・２．１　管理者：ユーザ管理（一覧）画面に遷移。☆・２．２　ユーザ：ログイン画面に遷移。
3	パスワード（確認）ロストフォーカス時	１．入力チェック（3.2チェック参照）☆　OKの場合なにもしない。☆　NGの場合エラーメッセージを表示。

見出し	３．チェック

No./2	種類/14	説明/23	表示エラーメッセージ/16
1	PW文字チェック	入力したPWを相互にチェック。	PWが異なります。
2	必須チェック	入力項目の必須文字の入力を促す。	ユーザ名が空です。

見出し	４．特記事項
```

####Markup
ユーザー管理(変更画面)の仕様書と思ってほしい。
ほとんどは平文だが、ごく少量マークアップの文字を使っている。

そもそも「見出し」とテーブルがあり、それぞれは改行ふたつで区切る。
見出しは「見出し」タブと書く。

テーブルは、1行目がヘッダ行となる。
ヘッダ行は、/2で結合するセルの数を示す。/2ならふたつのセルを結合するという意味。
この結合数をマーキングするために、「/」を使っているので、本文では/を使用できない。このあたりはすこし検討の余地がある。
セル内改行(\n)に☆を使っている。

たとえば見出しを####にすれば、Markdownにそっくりになる。
とはいえ、このテキスト、ほとんどテキストといっていいと思う。タブ区切りで素性もいいと思う。
これを下記のコードを通すと、じゃーん! Excelの表のできあがりである。できた表はぜひご自分で確かめられたい。

これを拡張すれば、txt2xlsxとなるなと思っている。

####code

```C#
string excelfilepath= "";
using (var book = new XLWorkbook(excelfilepath))
{
var sheet = book.Worksheet(sheetname);
string path = @"text.txt";
var lastusedlinenumber = sheet.LastRowUsed().RangeAddress.LastAddress.RowNumber;
Cell cell = new Cell("C", lastusedlinenumber+2);
string[] tables =File.ReadAllText(path, Encoding.UTF8).Split(new string[] { "\r\n\r\n" }, StringSplitOptions.None);
foreach(var table in tables)
{
	List<int> cellwidths = new List<int>();
	if (string.IsNullOrEmpty(table.Trim())) continue;
	if (table.Contains("見出し"))
	{
		int targetline = cell.LineNumber + 2;
		string[] cells = table.TrimEnd().Split(new string[] { "\r\n", "\r", "\n", "\t" }, StringSplitOptions.None);
		string startcell = "B";
		ExcelOperation.SetCellValue(sheet, startcell + targetline, cells[1]);
		sheet.Range(startcell + targetline + ":" + startcell + targetline).Style.Font.FontSize = 11;
		cell.LineNumber = cell.LineNumber + 4;
	}
	else
	{
		string[] lines = table.TrimEnd().Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
		foreach (var line in lines.Select((Value, Index) => new { Value, Index }))
		{
			if (line.Value.Contains("/"))
				cellwidths.Clear();
			int targetline = cell.LineNumber + line.Index;
			string[] cells = line.Value.TrimEnd().Split(new string[] { "\t" }, StringSplitOptions.None);
			string endcell = cell.HeadCell;
			foreach (var c in cells.Select((Value, Index) => new { Value, Index }))
			{
				string[] cellvalues = c.Value.Split(new string[] { "/" }, StringSplitOptions.None);
				//結合セル
				int cellwidth = 4;
				if (1 < cellvalues.Length) cellwidth = int.Parse(cellvalues[1]);
				if (c.Value.Contains("/")) cellwidths.Add(cellwidth);
				else if (c.Index < cells.Length) cellwidth = cellwidths[c.Index];

				string startcell = endcell;
				endcell = cell.ToAlphabet(cell.ToInt(startcell) + cellwidth - 1);
				ExcelOperation.SetCellValue(sheet, startcell + targetline, cellvalues[0].Replace("☆", "\n").Replace("I・O", "I／O"));
				sheet.Range(startcell + targetline + ":" + endcell + targetline).Merge().Style.Border.SetOutsideBorder(XLBorderStyleValues.Thin);
				sheet.Range(startcell + targetline + ":" + endcell + targetline).Style.Alignment.WrapText = true;
				sheet.Range(startcell + targetline + ":" + startcell + targetline).Style.Font.FontSize = 9;

				//見出し行
				if (line.Index == 0 || line.Value.Contains("/"))
				{
					sheet.Range(startcell + targetline + ":" + endcell + targetline).Style.Fill.BackgroundColor = XLColor.LightGray;
					sheet.Range(startcell + targetline + ":" + endcell + targetline).Style.Font.Bold = true;
					sheet.Range(startcell + targetline + ":" + endcell + targetline).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
				}

				endcell = cell.ToAlphabet(cell.ToInt(startcell) + cellwidth);
			}
		}
		cell.LineNumber = cell.LineNumber + lines.Length + 3;
	}
}

book.SaveAs(savename);

```

####注記
使用には、ClosedXMLが必要。
別途。Cellクラスというクラスを作っている。セル文字と行を管理しているだけなので、なくても動く。

やっているのは、テキストを行のかたまり=tableにわけ、見出しとテーブルとで処理をわけている。テーブルは、1行目をヘッダ行として特別扱いし、結合するセルの個数をもって、テーブルぜんたいに適応している。

テキストをレイアウトしたあとに、セルを結合し、周囲に罫線を引いている。
という感じ。

これで業務でじっさいに表を作って、かなり省力化できた。ともかく表を作り直すのが楽。検索もできるし、置換もしやすい。
いや作ってよかった。
感覚的にはTeXみたいな感じ。

####ToDo
今後の、ほぼやらないけど課題と感じていることを、いちおう書き出しておこう。

見出しを####に対応して、Markdown的に拡張することを検討。
コードを整理。リファクタリング。
「/」を区切り文字に使うのをやめる。
セルのheightに対応する。
結合セル数を自動指定。これができれば/2のような指定も不要。
セルによってセンタリングと左寄せを使い分け。

仕様書は書き終わったので目的は達成しているので、よほど気が向いて時間があって、ほかにすることがなければ。