C#でExcelの表のコピーをMarkdownに変換するアプリを作ってみた

クリップボードにコピーしたExcelの表データを、Markdownの表形式に変換してクリップボードに入れ直すアプリをC#のWPFで作ってみました。 試した環境は下記です。

  • Visual Studio 2015 Express for Windows Desktop

  • Excel 2013

目次

  1. どんなもの?
  2. 処理の流れ
  3. コード
  4. 試してみた

どんなもの?

WordPressにMarkdownで入力をしているのですが、Markdownの表って縦棒とハイフンでテキスト上で表のようにすれば良いので、書式としてはとってもわかりやすいですよね。 ただし、問題なのは列を表す縦棒の位置をぴったりと一致させないといけないということです。「テキストエディタで等幅フォントを使っていれば大丈夫でしょ?」なんて最初は考えるのですが、表内にURLを貼ったりするとテキストエディタを画面いっぱいに拡げても折り返しで縦棒の位置がわからなくなってしまいます。自動折り返しを止めると、表以外の文章が見にくくなるし。 そもそも、できるだけ表現と内容を分離して記述するためにMarkdownで文書を書いているのに、表の表現のためにスペースをポチポチ入力するってのもどうかと思います。やっぱり、表といえばExcelですよね。 というわけで、Excelの表をMarkdown形式に変換するアプリを作ることにしました。Excelのファイルを読み込む形だと部分指定が面倒になるので、クリップボード経由でデータの受け渡しをします。いずれ出力先はテキストエディタかWordPressの編集画面のどちらかなので、クリップボードから読み取ったデータを変換してクリップボードに戻すようにします。ユーザーとしては、Excelで範囲指定して「CTRL+C」→アプリをポチッ→テキストエディタで「CTRL+V」の操作で済むわけです。 160310-1-01

処理の流れ

Excelのセルを選択してCTRL+Cでクリップボードにコピーすると、タブ区切りのテキストになります。というわけで、タブ区切りのCSVデータとして読み取ることでセル毎に分解し、Markdownの装飾をした表の文字列にします。

コード

XAMLは下記の様にしました。

<Window x:Class="ClipboardConverterExcelToMarkdown.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ClipboardConverterExcelToMarkdown"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="50" />
        </Grid.RowDefinitions>
        <Button Name="button" Content="クリップボードのデータを変換" Margin="10" Grid.Row="0" Click="button_Click" />
        <TextBlock Name="textblock" Margin="10" Grid.Row="1" />
    </Grid>
</Window>

Gridの中にButtonとTextBlockを1つずつ配置しました。 コードは下記です。

using System.Linq;
using System.Text;
using System.Windows;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.VisualBasic.FileIO;
using System.Collections.Generic;

namespace ClipboardConverterExcelToMarkdown
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            // クリップボードからテキストデータを取得する。
            string inputedText = "";
            IDataObject clipboardData = Clipboard.GetDataObject();
            if (clipboardData.GetDataPresent(DataFormats.Text))
            {
                inputedText = (string)clipboardData.GetData(DataFormats.Text);
            }
            else
            {
                textblock.Text = "コピーしたデータが文字列ではありません。";
            }

            // 文字列を配列にパースする。
            MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(inputedText));
            TextFieldParser parser = new TextFieldParser(stream, Encoding.GetEncoding("Unicode"));
            parser.TextFieldType = FieldType.Delimited; // 区切り形式とする
            parser.SetDelimiters("\t"); // 区切り文字
            parser.CommentTokens = new string[] { "#" }; // #で始まる行はコメントとする
            parser.HasFieldsEnclosedInQuotes = true; // 引用符付きとする
            parser.TrimWhiteSpace = true; // 空白文字を取り除く

            List textLineList = new List();
            while (!parser.EndOfData)
            {
                string[] row = parser.ReadFields(); // 1行読み込んでフィールド毎に配列に入れる
                for (int i = 0; i < row.Length; i++)
                {
                    row[i] = Regex.Replace(row[i], @"\r*\n", string.Empty); // セル内の改行コードを削除する
                }
                textLineList.Add(row);
            }
            int rowCounter = textLineList.Count;
            string[][] cell = new string[rowCounter][];
            for (int i = 0; i < rowCounter; i++)
            {
                cell[i] = textLineList[i];
            }

            // セルの幅(文字数)を合わせる
            Encoding enc = Encoding.GetEncoding("Shift_JIS");
            for (int k = 0; k < cell[0].Length; k++)
            {
                int[] len = new int[rowCounter];
                for (int i = 0; i < rowCounter; i++)
                {
                    len[i] = enc.GetByteCount(cell[i][k]);
                }
                int maxlen = len.Max();
                for (int i = 0; i < rowCounter; i++)
                {
                    for (int j = 0; j < maxlen - len[i]; j++)
                    {
                        cell[i][k] = cell[i][k] + " ";
                    }
                    len[i] = enc.GetByteCount(cell[i][k]);
                }
            }

            // セパレータ行の文字列を作る。
            string separater = "";
            for (int i = 0; i < cell[0].Length; i++)
            {
                string sep = "";
                for (int j = 0; j < enc.GetByteCount(cell[0][i]); j++)
                {
                    sep = sep + "-";
                }
                separater = separater + "|-" + sep + "-";
            }
            separater = separater + "|\r\n";

            // 各行の文字列を作る。
            string[] body = new string[rowCounter];
            for (int j = 0; j < rowCounter; j++)
            {
                for (int i = 0; i < cell[0].Length; i++)
                {
                    body[j] = body[j] + "| " + cell[j][i] + " ";
                }
                body[j] = body[j] + "|\r\n";
            }

            // 全行を合体する。
            string text = "";
            text = body[0] + separater;
            for (int i = 1; i < rowCounter; i++)
            {
                text = text + body[i];
            }

            // 出来上がった文字列をクリップボードに入れる。
            Clipboard.SetData(DataFormats.Text, (object)text);
            textblock.Text = "変換してクリップボードに入れました。";
        }
    }
}

やたらループばかりでなんだか恥ずかしいですね。本職の人はもっと綺麗なコードにするんだろうなあ。 ちょっと変わったところですが、クリップボードから読み取った文字列を、MemoryStreamに変換して、さらにそれをVisual BasicのTextFieldParserに渡しています。TextFieldParserが、タブ区切りのテキストを文字列配列に変換してくれるわけです。TextFieldParserを使うと、例えばセル内で改行していても「改行を含むセル」として認識してくれます。なんでこんな便利クラスがC#に無いのでしょう。

試してみた

早速試してみます。 元データは下図です。 160310-1-02 1バイト文字と2バイト文字が混在してます。C4セルには数式が入っています。A5セルには改行が入っています。ところどころ、空のセルがあります。面倒くさい表ですね。 これをアプリで変換してテキストエディタにペーストすると、下図になります。

| a1             | b1 | c1 |
|----------------|----|----|
| a2             | b2 | c2 |
| 1              | 2  | 3  |
| あいうえお     |    | 6  |
| いろはにほへと |    |    |

WordPressで表として表示させると下表になります。

a1

b1

c1

a2

b2

c2

1

2

3

あいうえお

6

いろはにほへと

Markdownでイチからポチポチ表を作るより、たぶん簡単にできますね。

公開日

広告