ゲームエフェクトデザイナーのブログ | A Real-Time VFX Artist's Blog

About Making Materials on UE, Making Tools with C#, etc

C#でサブフォルダの総サイズのリストアップ(ListBox編)

お次は、あるフォルダ直下にあるサブフォルダ個々の総ファイルサイズをリストアップするツールについての備忘録です。リストアップを表示するパーツをここでは「ListBox」で試してみました。

下図のような外観で、フォルダパスを入力後にEnterキーでリストアップを実行します。
フォルダのドラッグ&ドロップにも対応する場合は前回の記事が参考になるかと思います。

f:id:moko_03_25:20170812032439j:plain

 

実装したもの


今回実装したのは以下になります。
かなりまどろっこしい感じになってる気がします。
もっと簡潔で良い書き方が色々ありそうですが‥

・あるフォルダ以下にある全てのサブフォルダのパスを配列で取得するメソッド
・あるフォルダ以下の総ファイルサイズを取得するメソッド
・サブフォルダ1つ1つの総ファイルサイズを配列で取得するメソッド

・パス入力欄でEnterを押すとサブフォルダ個々の総サイズをリストアップする

・ListBoxの内容のクリップボードへのコピーとクリア


1つずつ見ていきます。

あるフォルダ以下にある全てのサブフォルダのパスを配列で取得するメソッド


こちらは前回の記事で出てきたもので、やはりDOBON.NETさんをそのままコピーさせて頂いたものです。
青文字はクラス名・メソッド名・変数名です)

//あるフォルダ以下にある全てのサブフォルダのパスを配列で取得する
public string GetSubFolderPath(string path)
{
  string subFolders = Directory.GetDirectories(

    path, "*",
    SearchOption.TopDirectoryOnly); //サブフォルダTOP階層のみ取得

  return subFolders;
}


実質2行でシンプルです。

あるフォルダ以下の総ファイルサイズを取得するメソッド


こちらに関しては完全に下記の記事をまるまるコピーさせていただきました。

フォルダのサイズを取得する: .NET Tips: C#, VB.NET

こんな感じです。

//あるフォルダ以下の総ファイルサイズを取得するメソッド
public static long GetDirectorySize(DirectoryInfo dirInfo)
{
  long size = 0;

  //1つのサブフォルダ内の全ファイルの合計サイズを計算する
  foreach (FileInfo fi in dirInfo.GetFiles())
    size += fi.Length;

  //全サブフォルダのサイズを合計していく
  foreach (DirectoryInfo di in dirInfo.GetDirectories())
    size += GetDirectorySize(di);

  //結果を返す
  return size;
}


サブフォルダの合計をしていく場所で自身のメソッド名が登場していますが、入れ子で実行しているということですかね? まず指定パス直下のファイルを合計してからサブフォルダ1つずつの中を掘り下げて合計していっている感じでしょうか。

そんな記述ができるんですね。。

ただ、変数sizeが繰り返し 0 で初期化されたり return されたりしていて、ステップ実行してもどういう流れになってるのか把握し辛いですね‥

サブフォルダ1つ1つの総ファイルサイズを配列で取得するメソッド


ここが今回の要で、1つめのメソッドで得たサブフォルダのパス情報を引数として受け取って、for文でパス1つ1つに対して2つめのメソッドを実行して総ファイルサイズを順に得ていく感じです。

//サブフォルダ1つ1つの総ファイルサイズを配列で取得
public string GetSubFolderSize(string subFolders)
{
  //サブフォルダの数分の配列"size"と"sizeSt"を定義
  int el = subFolders.Length; //サブフォルダの要素数(element)
  long size = new long[el]; //整数の配列
  string sizeSt = new string[el]; //文字列の配列

  string fn; //フォルダネームを一時的に保存する変数

  //配列の要素の数だけ処理
  for (int i = 0; i < el; i++)
  {
    //入力したパスのフォルダ名を取得
    fn = Path.GetFileName(subFolders[i]);

    //入力したパスをDIrectoryInfoクラスのオブジェクトに代入
    DirectoryInfo chkPath = new DirectoryInfo(subFolders[i]);
    //総ファイルサイズをMiB単位で配列"size"に代入
    size[i] = GetDirectorySize(chkPath) / 1024 / 1024;
    //文字列に変換して配列"sizeSt"に代入
    sizeSt[i] = fn + "\t" + size[i].ToString("D") + " MB";
  }

  return sizeSt;
}


得られるファイルサイズはバイト単位になるので、ここでは1024で割ってKiBに、さらに1024で割ってMiB単位に変えた上で、ToString("D")で文字列(整数)に変換しています。

パス入力欄でEnterを押すとサブフォルダ個々の総サイズをリストアップする


実際にパスを入力してEnterで決定した際の挙動です。

一応ちゃんとパス先にフォルダがあるかチェックしてからGetSubFolderSizeメソッドを実行しています。

//パス入力欄でのキー入力イベント
private void textBoxPath_KeyDown(object sender, KeyEventArgs e)
{
  //Ctrl + A で全選択可能にする
  if (e.Control && e.KeyCode == Keys.A)
  {
    textBoxPath.SelectAll();
  }

  //Enterでパスを取得
  if (e.KeyCode == Keys.Enter)
  {
    string path = textBoxPath.Text;

    //textBoxPathのパス先が存在しているかどうか判定
    if (Directory.Exists(path))
    {
      //サブフォルダのパスを全て取得
      string subFolders = GetSubFolderPath(path);

      //サブフォルダの数分の配列"sizeSt"を定義
      string sizeSt = new string[subFolders.Length];

      //全てのサブフォルダごとの合計サイズを取得
      sizeSt = GetSubFolderSize(subFolders);

      //リストに代入していく
      List<string> lt = new List<string>(sizeSt);

      foreach (var item in lt)
      {
        listBoxResult.Items.Add(item);
      }
    }
    else
    {
      MessageBox.Show("\"" + path + "\"" +
        " は存在しないフォルダです");
    }
  }
}


総サイズを配列で受け取った後はリストに代入して、リストをListBoxに追加しています。
ここは色んな方法がありそうですがListクラスも使ってみたいと思って使ってみた感じです。とは言えここもコピペです。下記の記事を参考にさせて頂きました。

C# 配列を List に変換する | ITLAB51.COM

ListBoxは初めて使ってみましたが、複数行を選択させたいなら「SelectionMode」プロパティをMultiExtendedにすると良いです。MultiSimpleというのもありますがこちらはクリックで選択状態になったあと別の行を選択しても選択状態が解除されないのが気持ち悪いので個人的には「これは使わないなあ」と思いました。

f:id:moko_03_25:20170812063347j:plain

 

ListBoxの内容のクリップボードへのコピーとクリア


最後におまけです。
ListBoxの内容を全てクリップボードへコピーする方法と、全て消去する方法です。

//「クリップボードへコピーする」ボタンの挙動
private void buttonCopy_Click(object sender, EventArgs e)
{
  //LINQでListBoxの中身を1行ずつ配列に代入
  string[] list = this.listBoxResult.Items.Cast<string>().ToArray();
  //文字列を改行を挟みつつ連結
  string st = string.Join("\r\n", list);
  //クリップボードへコピー
  Clipboard.SetText(st);
}

//「クリア」ボタンでListBoxの内容を消去
private void buttonClear_Click(object sender, EventArgs e)
{
  listBoxResult.Items.Clear(); //内容をクリア
}


ListBoxの内容全てを1行ずつ配列の形で取得してから全ての行を連結させています。
もっと直接クリップボードにコピーできないのかなと思いましたがちょこっとググってみただけでは分かりませんでした。

ちなみにListBoxの内容を配列で取得する方法は、こちらのYahoo!知恵袋のベストアンサーの記述方法をそのまま参考にさせて頂きました。

C#のリストボックスの項目取得で困っています。選択されていない項目も含めて... - Yahoo!知恵袋

これがLINQというやつですか。。覚えるととても便利そう!ですね。

それから、文字列の配列を改行を挟みつつ連結する方法はこちらの記事を参考にさせて頂きました。

文字列を連結する: .NET Tips: C#, VB.NET

内容をクリアはメソッドが用意されていますね。

プロジェクトデータと実行ファイル


Googleドライブにアップしました。ライセンスはMITです。

MyTool_FolderSizeCheck_ListBox.zip - Google ドライブ

binフォルダの中にexeが入っています。

C#でサブフォルダをリストアップ

前回に続いて、任意のフォルダの直下にあるサブフォルダ名をリストアップするツールについて、備忘録としてメモしておきたいと思います。

恐らくプログラム初心者ならではの全くスマートではない見苦しいコードになってるのではと思うので、そのあたりご注意ください。。

ツールの外観は下図のような感じで、上段のテキストボックスにフォルダのパスを入力してEnterを押すか、またはフォルダやファイルをツール上へドラッグ&ドロップすることで下段の「サブフォルダリスト」内にサブフォルダ名をずらずらっとリストアップします。

f:id:moko_03_25:20170807235416j:plain

 

実装したもの


ここで実装した内容は以下になります。

・あるパスがファイルかフォルダかを判定してフォルダパスを取得するメソッド
  - ファイルの場合は親フォルダのパスを取得
  - フォルダの場合は自身のフォルダパスを取得
・フォルダパスから直下のサブフォルダ名を取得してリストアップするメソッド

・パスを入力してEnterキー入力でリストアップを実行する
・ファイルかフォルダをドラッグ&ドロップするとリストアップを実行する


1つずつ見ていきます。

usingと変数の宣言

 

using System.IO;

~~~~省略~~~~

//変数の宣言
string filePath; //ファイルのフルパス
string folderPath; //親フォルダのフルパス


既存のファイル情報を得るクラスを使用するので「System.IO」を using に追記します。

また、パスのややこしい部分ですが、ファイルパスとフォルダパスの扱いを明示的に分けたかったので変数を2つ用意しました。

ファイルかフォルダか判定してフォルダパスを取得するメソッド


次に、あるパスがファイルかフォルダかを判定して、どちらの場合でもフォルダのパスを取得するよう記述したメソッドが下記です。
青文字はクラス名・メソッド名・変数名です)

//ファイルかフォルダか判定してフォルダパスを取得するメソッド
private void determine()
{
  if (File.Exists(filePath)) //ファイルの場合
  {
    folderPath = Path.GetDirectoryName(filePath);
  }
  else if (Directory.Exists(filePath)) //フォルダの場合
  {
    folderPath = filePath;
  }
}


File.Exists」「Directory.Exists」を使うことで、パス先がファイルなのかフォルダなのか簡単に判定できるようです。便利!

それから、パスの取得周りはルールがちょっとややこしいです。
こちらの記事を参考にしました。

パスからファイル名、拡張子、ディレクトリ名、ルートディレクトリ名等の情報を取得する: .NET Tips: C#, VB.NET

こちらの一覧表を見ると分かりますが、現在のフォルダ名(上記サイトの例だと"sub"がそれにあたる)を取得するには、フォルダパスに対して「Path.GetFileName」で行うことになるかと思います。

しかしドラッグ&ドロップで入力したものがファイルだった場合に、ファイルパスに対して「Path.GetDirectoryName」でフォルダパスを得ています。

フォルダパスからサブフォルダ名を取得してリストアップするメソッド


今回の要となる、サブフォルダをリストアップするメソッドがこちら。

//フォルダパスからサブフォルダをリストアップするメソッド
private void listUp()
{
  //テキストの内容をTextBoxに表示
  textBoxPath.Text = folderPath;

  //指定フォルダ以下のサブフォルダをすべて取得する
  string subFolders = Directory.GetDirectories(
    folderPath, "*",
    SearchOption.TopDirectoryOnly); //サブフォルダTOP階層のみ取得

  //一旦内容をクリア
  textBoxList.ResetText();

  //取得したサブフォルダ群を1つずつテキストボックスに追加していく
  for (int i = 0; i < subFolders.Length; i++)
  {
    //サブフォルダのパスからフォルダ名を取得
    string stFileName = Path.GetFileName(subFolders[i]);
    //フォルダ名を改行しながら追加
    textBoxList.AppendText(stFileName + "\r\n");
  }
}


先ほどの判定メソッドで得たフォルダパスを利用して、そのフォルダ直下のサブフォルダ名を「Directory.GetDirectories」で取得して配列に代入しています。

こちらの記事を参考にさせて頂きました。

あるフォルダ以下にあるサブフォルダをすべて取得する: .NET Tips: C#, VB.NET

その後にfor分で1つずつテキストボックスに改行しながら追記していきます。

パスを入力してEnterキー入力でリストアップを実行する


まず探すきっかけとなるパスをどう取得するかですが、今回は手動での入力とドラッグ&ドロップに両対応することにしました。

専用のテキストボックスにパスを手動で入力してEnterキーを押した時の挙動がこちら。

//TextBoxPathのキー入力による挙動
private void textBoxPath_KeyDown(object sender, KeyEventArgs e)
{
  //Ctrl + A で全選択可能にする
  if (e.Control && e.KeyCode == Keys.A)
    textBoxPath.SelectAll();

  //Enterでパスを取得
  if (e.KeyCode == Keys.Enter)
  {
    //フォルダが存在しているかどうか判定
    if (Directory.Exists(textBoxPath.Text))
    {
      //TextBoxの内容からパスを取得
      filePath = textBoxPath.Text;
      //ファイルかフォルダかを判定してフォルダパスを取得
      determine();
      //フォルダパスからサブフォルダをリストアップする
      listUp();
    }
    else
    {
      MessageBox.Show("存在しないフォルダです");
    }
  }
}


テキストボックスは1行だけのボックスであっても全選択したいものだと思うので、前回の記事同様にKeyDownイベントに Ctrl + A による全選択を記述しています。

また、Enterキーによる実行もKeyDownイベントなので、こちらも平行して記述します。
まずテキストボックスに入力したパス先にフォルダがあるか判定してから、最初に解説したメソッド2つを実行します。ここはとてもシンプルです。

フォルダと判定できなかった場合はメッセージボックスを表示します。

f:id:moko_03_25:20170809021356j:plain

ファイルかフォルダをドラッグ&ドロップするとリストアップを実行


最後にドラッグ&ドロップされたフォルダ(またはファイル)からパスを取得した時の挙動がこちらになります。

//Formへのドラッグ&ドロップの挙動1
private void Form1_DragEnter(object sender, DragEventArgs e)
{
  //ドロップされたものがファイルかどうかチェックする定番の書き方
  if (e.Data.GetDataPresent(DataFormats.FileDrop))
  {
    //ファイルのときは受け付ける
    e.Effect = DragDropEffects.All;
  }
  else
  {
    //ファイル以外は受け付けない
    e.Effect = DragDropEffects.None;
  }
}
//Formへのドラッグ&ドロップの挙動2
private void Form1_DragDrop(object sender, DragEventArgs e)
{
  //ファイルが複数ドロップされた時のためにフルパスを配列に代入
  string multiPass = (string[])e.Data.GetData(DataFormats.FileDrop, false);
  //0番目のファイルだけフルパスを取得
  filePath = multiPass[0];
  //ファイルかフォルダかを判定してフォルダパスを取得
  determine();
  //フォルダパスからサブフォルダをリストアップする
  listUp();
}


ドラッグ&ドロップの対応はこちらの記事を参考にさせて頂きました。

Drag&Dropされたファイルのファイル名を取得する: .NET Tips: C#, VB.NET

他にもいくつかのサイトを参考にしましたが、この形がお約束のようで、ドラッグ時のウインドウ上部への侵入時の挙動と、ドロップ時のふたつの挙動がセットになるようです。

複数のファイルがドロップされた時のために、配列に代入するのもお約束のようです。
そうして得たパスに対して、最初に解説したメソッド2つを実行します。

プロジェクトデータと実行ファイル


Googleドライブにアップしました。ライセンスはMITです。

MyTool_FolderList.zip - Google ドライブ

binフォルダの中にexeが入っています。

C#で正規表現による置換を行う

C# で最初に作ったツールは「特定のリストを作って専用のフォーマットに整形した上でテキストデータで出力するもの」という感じでしたが、そこで躓いた部分は正規表現による置換でした。

その置換についてメモしておきたいと思って用意したのが下図のツールです。

f:id:moko_03_25:20170807211406j:plain

ここでいきなりUE4の話になりますが、UE4のアセット管理ツールであるコンテンツブラウザ上でアセットを右クリック「リファレンスをコピー」を選択すると、クリップボードにアセットの情報がコピーされます。

f:id:moko_03_25:20170807211352j:plain

それは以下のような書式です。

アセットの種類 + ' + アセットのフルパス + . + アセット名 + '


で、例えばあるアセットの場所をメールでお知らせするような時には、フルパスの前後の文字列が邪魔だったりします。それを正規表現の置換で除去するのが今回のツールです。

ちなみに余談ですが、コンテンツブラウザ上でアセットを選択して Ctrl + C でリファレンスをコピーできることを最近同僚に教えてもらいました。
知りませんでした。。これは便利!

f:id:moko_03_25:20170807211403j:plain

そしてアセットのリファレンス情報をツールの上段のテキストボックス内にペーストします。
すると下図のように貼り付けられました。
先に書いた通り、メールに記載したりするには余計な情報がごちゃっと付いています。

f:id:moko_03_25:20170807211400j:plain

次に「パスから余計な情報を除去」ボタンを押すと、下段のテキストボックスに置換された結果が表示されます。

あとはオマケで「クリップボードへコピー」ボタンと「クリア」ボタンも追加しました。

f:id:moko_03_25:20170807211349j:plain

実装したもの


さて、ここで実装した内容は以下になります。

・テキストボックス内で Ctrl + A を入力すると文字列を全選択する
・「クリア」ボタンでテキストボックス内の文字列を全消去する
・「クリップボードへコピー」ボタンで文字列をクリップボードにコピーする
・「パスから余計な情報を除去」ボタンでリファレンス情報を単なるフルパス情報に整形する

1つずつ見ていきます。

テキストボックス内での文字列の全選択


ツールを作ってみて初めて気付いたのですが、普段はテキスト入力できるパネル内で当たり前のように可能な全選択ショートカットですが、これは実装しないと可能になりません。

実装は簡単で、テキストボックスの KeyDown イベントに下記の2行を追記するだけです。
青文字部分はテキストボックスのNameプロパティで設定した名前です)

//Ctrl + A で全選択可能にする
private void textBoxInput_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Control && e.KeyCode == Keys.A)
    textBoxInput.SelectAll();
}

 

文字列のクリアとクリップボードへのコピー


これらも実装は簡単で、ボタンのクリックイベントそれぞれに下記1行を追加するだけです。
青文字部分はテキストボックスのNameプロパティで設定した名前です)

//「クリア」ボタンでtextBoxの内容を消去
private void buttonInputClear_Click(object sender, EventArgs e)
{
    textBoxInput.ResetText();
}
//「クリップボードへコピーする」ボタンの挙動
private void buttonResultCopy_Click(object sender, EventArgs e)
{
    Clipboard.SetText(textBoxResult.Text);
}
//「クリア」ボタンでtextBoxの内容を消去
private void buttonResultClear_Click(object sender, EventArgs e)
{
    textBoxResult.ResetText();
}

 

正規表現による置換


問題はここからです。

正規表現の置換には「RegularExpressions」を利用しました。
LINQを使うとか色々方法があるっぽいのですが、よく検索で出てくるこちらを試しました。

まずは Form1.cs の先頭に下記の1行を追加します。

using System.Text.RegularExpressions;


次にボタンクリックイベントでの置換の内容です。

//「パスから余計な情報を除去」ボタンの挙動
private void buttonReplace_Click(object sender, EventArgs e)
{
    //一時的に文字列を扱うための変数
    string st = textBoxInput.Text;

    //先頭の余分な情報を置換でカット
    st = Regex.Replace(
        st,
        "^[^/]+'", //行頭から連続するスラッシュ以外の文字列
        "", //空に置換(文末以外の空白行も削除される)
        RegexOptions.Multiline); //改行記号も1つの文末として処理する
    //末尾の余分な情報を置換でカット
    st = Regex.Replace(
        st,
        "[.][^/]+'", //行末まで連続した.で始まるスラッシュ以外の文字列
        "", //空に置換
        RegexOptions.Multiline); //改行記号も1つの文末として処理する
    //文末の空行を削除
    st = Regex.Replace(
        st,
        "[\r\n]*$", //文末で0回以上繰り返す改行
        "\r\n"); //改行1つだけに置換

    textBoxResult.AppendText(st);
}


文字列を改行部分で分割して配列に代入し、for文で1行ずつ置換していく‥といったような面倒なことをしなくても、改行を含んだ文字列に対して「Multiline(複数行)」オプションを指定してそのまま置換するだけでいけました。

3度の 置換によって

 ・行頭の情報の除去(文末以外の空行も同時に除去)
 ・行末の情報の除去
 ・文末に空行が複数ある場合に1行だけにする

‥を行っています。

正規表現のルールはこちらの記事を参考にさせて頂きました。

正規表現の基本: .NET Tips: C#, VB.NET


正規表現の厄介なところは、文字列の指定が記号だらけになって内容も結果も分かり辛く、なぜ置換がうまくいかないかも判断し辛いところですね。。

ただ、上記の置換のコード先頭でブレークポイントを設定して、変数 st をウォッチリストに追加して、1つの処理を順に進めて(ステップ実行して)いくと、変数 st の内容がどう変わっていくかをチェックすることができます。
(このあたりは1つ前に投稿した記事で紹介した書籍でも丁寧に解説されています)

例えばピリオド(\n以外の任意の文字列)を使って .+ で置換するとキャリッジリターンまで置換対象になって改行文字の「\r\n」が「\n」だけになってしまい、テキスト出力すると謎の連続改行が入ってしまうといったことが起こり、原因究明にしばらく手間取りましたが、ステップ実行して変数の中身を調べると原因がすぐに分かりました。

行末の情報を除去するための対象文字列に、行末を表す $ を使用していないのはそのためで、$ を使うと「.+」によって「\r」が削られてしまいました。
ここでは Path.GetFileName のようなメソッドで親フォルダパスを取得する方法は使わずあくまで置換でやりたかったので対処療法な感じになりましたが、どこかのタイミングで本職の方にちゃんとした指定方法を教えてもらおうと思っています。。

ウォッチリストの変数 st の状態。最初は空っぽ(null)

f:id:moko_03_25:20170807220954j:plain

▼ステップ実行でテキストボックスの内容が代入された様子("\r\n"が改行を表す部分)

f:id:moko_03_25:20170807220950j:plain

▼行頭の余計な情報が除去された(文末以外の空行も一緒に除去)

f:id:moko_03_25:20170807220947j:plain

▼行末の余計な情報が除去された

f:id:moko_03_25:20170807220944j:plain


ちなみに、置換結果の文字列はテキストボックスの中へ書き換えるのではなく「AppendText」を使って追記する形にしています。
すでに別の置換結果の文字列が存在する場合に新しい行へ追記したいと思ったからです。

そのためには、置換の最後には必ず1回分の改行を行って終わるように記述しています。

プロジェクトデータと実行ファイル


Googleドライブにアップしました。

MyTool_UE4PassReplace.zip - Google ドライブ

binフォルダの中にexeが入っています。

 

2017.11.18追記

 

C#のはじめ方

CGアーティストだってWindows用ツールを作りたい!

ということで C# を少しばかり触って簡単なツールをいくつか作ってみました。
思っていたよりもずっと敷居は低かったので、備忘録がてら軽くまとめておこうと思います。

※プログラム自体の初心者が書くアーティスト向けの記事です

基本的な文法の習得


とは言え最低限の文法をまずは覚える必要があります。
「何か作りたい!」のに作れない、この第一歩が最初のもどかしい時間とは思いますが、どうしてもここは頑張るしかないところだと思います。

さて、どんなプログラミング言語でも大体同じなのかなと思いますが、最初に覚える必要がある要素は以下のあたりと思います。

 変数の宣言・代入・型
 メソッド・引数・戻り値
 if文による条件分岐
 配列とfor文による繰り返し
 クラス・オブジェクトの生成
 using(pythonで言うところのimport)
 アクセス修飾子(publicとか)

とりあえずここまで覚えれば、クラスの継承とかコンストラクタとかオーバーロードといったクラス周りのややこしいルールの学習は後回しで良いと思います。
ほんのちょっとしたツールだとクラスを自作しなくても大丈夫な感じですし、実装したいことは工程ごとにGoogleで調べてコピペで大体いけます。

ではどうやって覚えるかというと、自分の場合はこちらのシリーズにお世話になりました。
(正確に言えば同じ投稿者の方のJava超入門シリーズでまずはJavaから覚えました。JavaC#と文法がかなり近いです)

メモ帳でC#超入門 #1 環境構築 by T.Umezawa - ニコニコ動画

この方の動画はとても丁寧で分かりやすく解説されていて、以下のあたりが特徴です。

・声が聞きとりやすい
・メモ帳でコーディングしてコマンドプロンプトコンパイルするスタイル
RPGゲームを作っていきながらの解説
・エラーが出て修正する手順を丁寧に繰り返して解説
・煩雑になったコードを修正対応しやすい&読みやすいコードにどう整形していくかを丁寧に解説
・ドキュメントの調べ方を解説


‥という感じで、単に文法の説明で終わらずとてもポイント高いと感じます。

また、いきなり本を買って手元でページをめくりながら試すよりも、動画を流しながら試す方が初心者にも入りやすくてとても良いですね!

いやいや、メモ帳とかコマンドプロンプトじゃなくて最初からIDE(開発環境)でやりたい!
手っ取り早く文法だけ知りたい!

‥という方にはこちらのシリーズがおすすめです。

【プログラミング講座】第1回 プログラム言語の種類【独り言】

こちらの動画の良いところは以下のあたりです。

Visual Studioを使ったスタイル
・WindowsFormでのツールを制作していきながらの解説
・若干ゲームを作るパートもある
・WindowsFormの各コントロール(ウインドウを構成するパーツ)の解説が充実している


どうすればウインドウサイズを変えても崩れないレイアウトを作成できるのかというあたりの解説も含まれていて、これが案外ググっても出てこないため個人的にとてもポイント高いと思いました。

そうして基本的なところが一通り馴染んだら、次のステップに移って良いと思います。

逆に、上に挙げたあたりは最初に覚えておかないと、入門書を買って読んでもついていけない可能性がありますし、やりたいことをネットで検索してサンプルコードを見つけてもそのままでは動かない時に、自力では対応することができなくて詰んでしまいます。。

実際に何かツールを作ってみる


早速色々作りたいところですが、自分の場合はこちらの書籍を購入して進めました。

作って覚えるVisual C# 2017 デスクトップアプリ入門

作って覚えるVisual C# 2017 デスクトップアプリ入門

 

色んなツールを作りながらステップアップしていける、とても良い本でおすすめです!

C#で具体的なツールを実際に作ってみよう的な本って思いのほか少ないんですね。
多くの書籍が文法の解説や逆引きだったりします。

こちらの書籍は Visual Studio Community 2017 を導入して、.NET向けAPI(?)の「Windowsフォームアプリケーション」というWindowsでお馴染なGUIのツールをC#で作っていく内容になっています。

Visual Studioというと、本格的なプログラミング環境でとても大層でハードル高いイメージがありましたが、むしろ初心者に優しい機能がてんこ盛りなんですね。

何といってもWindows向けのツールのGUIを、ボタンやテキストボックスなどペタペタ貼り付けるだけでほとんどコーディングせず作れてしまいます。

また、変数や関数などに色が付いたり(シンタックスハイライト)、コンパイル時にエラーが出る部分がリアルタイムで表示されたり、定義済みの変数や関数が予測変換でリストアップされたりと便利すぎます。

1命令ずつ順に実行(ステップ実行)してその時々の変数の値を確認しながらデバッグできたりといったあたりも大変便利。。

ただこちらの書籍、最初は簡単なツールから始まって色んなツールを順番に作っていくのですが、最後の家計簿ツールで難易度が急に上がって感じました。。

家計簿ツールの途中でどこを間違ったのか動かなくなってしまい、手順を追っていっても何をしているのか理解できなくなってきていたため、いったん書籍は中断して、好きなものを作っていくステップに移りました(家計簿ツールは一年後に再挑戦してみたらちゃんと完成しました!)。

結果的にもそれで良かったです。

書籍を最後までやらずともツールを作る手順は大体学べます。
あとはネットで調べつつ色々作ってみるべし!という感じでしょうかっ。

ツールを作っていく過程で、クラスをnewしなくても直接使えるものがあったり、見つけたサンプルコードで使われている型が int や string ではなく「var」であったり、触っている上で色々と「?」に遭遇しますが、そういうのは気になったタイミングで調べて、よく分からなければ後回し‥で良いと思います。


という訳で、まずは基礎文法を勉強して上記書籍を一通り試してみるという感じでツール作りに入っていきました。ここまでで、休日などがっつりと費やして一気に進めたら 3週間ほどという感じでした。

その後さらに2週間ほどでほんのちょっとした小さなツール(テキストボックスに入力したファイルパスからファイル名の部分を削るだけのツールとか)を6つ7つ作ってみたらツール作りにも徐々に慣れてきて、今後も徐々にステップアップしながら色々と作っていける手応えを感じました。

楽しくてお仕事にも役に立つので、CGアーティストな皆さまもぜひぜひC#でツールを作ってみてみてください!

エクセルで条件付き書式を適用したのち書式を残してクリアする方法

エクセルの備忘録。

下図のようなリストで、B列の値が一定の値以上ならその行の書式を変えたいといったような場合には「条件付き書式」機能を使うと便利です。

f:id:moko_03_25:20160831022607p:plain

まず書式を反映させたい列、または範囲を選択しておきます。

f:id:moko_03_25:20160831022608p:plain

ホームタブにある「条件付き書式」の「新しいルール」を選択。

f:id:moko_03_25:20160831022609p:plain

数式を使用して、書式設定するセルを決定」を選択し、下図のように計算式を記述した後に「書式」ボタンを押して自由に書式を設定します。

=$B1>=80

この図の場合は「B列に対してB1のセル以降の値が80以上なら赤文字にする」です。

f:id:moko_03_25:20160831022610p:plain

するとB列の値が80以上の行の文字が赤くなりました。

f:id:moko_03_25:20160831022611p:plain

ただしこれで表が完成した場合、このままでは少し問題があります。
下図のように行を挿入すると赤くなる文字がズレました‥
今はまだ「条件付き書式」の情報が残っていて編集する際に厄介なので、これを通常の書式に換えて条件付き書式はクリアしてしまいます。

f:id:moko_03_25:20160831022612p:plain

この時、もう一度「条件付き書式」から「ルールのクリア」を行うだけで書式を残したままクリアがすんなりいく場合と、書式が全て失われてしまう場合があります。
オプション設定なのかエクセルのバージョンなのか‥
とりあえず後者の場合の対処方法を追記しておきます。

まず条件付き書式を外したいセルを選択して一旦コピーします。
次にホームタブの「貼り付け」の右下にあるクリップボードオプションのボタンを押して「すべて貼り付け」ボタンを押して、そのまま上から貼り付けてしまいます。

f:id:moko_03_25:20160831022613p:plain

これで書式自体は移せたので、あとは「条件付き書式」>「ルールのクリア」で条件付き書式をすっきりと除去することができます。

f:id:moko_03_25:20160831022614p:plain

以上です。

エクセルで便利なVBAマクロのメモ

エクセルのVBAで便利な記述の備忘録。
標準操作でできる範囲ならマクロの記録である程度なんとかなりますね。。

A1からA列の最下行の範囲


A列の1行目からA列で何か記入している最下行の範囲を表す場合。

Range("A1", .Range("A" & .Rows.Count).End(xlUp))

 

データサイズのバイト表記をKiB表記にする

A列にデータサイズが並んでいてB列にKiB表記にしたいときはこちら。

' A列を1024で割ったものをB列に入れる
Range("B1").Select
ActiveCell.FormulaR1C1 = "=RC[-1]/1024"
Range("B1").AutoFill Destination:=Range("B1:B" & Cells(2).CurrentRegion.Rows.Count)

' B列を文字列に変えてKB表示にする
Columns("B:B").Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Selection.NumberFormatLocal = "0 ""KiB"""

MiB表示にしたいならさらに1024で割ればOK。

A列の値が50以上のA~B列を赤文字・太文字にする

条件付き書式>新しいルール>数式を使用して、書式設定をするセルを決定‥と同じもの。

Columns("A:B").Select
Selection.FormatConditions.Add Type:=xlExpression, Formula1:="=$A1>=50"
Selection.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
With Selection.FormatConditions(1).Font
.Bold = True
.Italic = False
.Color = -16776961
.TintAndShade = 0
End With
Selection.FormatConditions(1).StopIfTrue = False

 

随時、追記していきます。

秀丸で便利な置換のメモ

秀丸での便利な置換についての備忘録。
ちなみに「秀丸の検索機能を強化するHmJre.dllについて」も併せてどうぞ。

空行を削除


改行しか無い行を削除して詰めてしまいたいときはこちら。

検索 ^¥n

置換

 

○○○を含まない行を削除


ある文字列○○○を含む行は残して、含まない行は削除してしまいたいときはこちら。

検索 ^(?!○○○).+¥n

置換

これを使えば、例えば大量にファイルのパスが並んだリストから、自分が欲しい情報の行だけピックアップするのが楽になります。

○○○を残してそれ以外の文字列を削除


「○○○△△××」という文章から○○○のみを残したい場合はこちら。

検索 ○○○¥f△△¥f××¥f

置換 ¥0


 随時、追記していきます。

秀丸の検索機能を強化するHmJre.dllについて

秀丸正規表現の置換や置換マクロを試していて「ネットで調べた正規表現の置換ができないなぁ」‥と思っていたら、単に付属のdllのバージョンが古かったというだけのことでした。。

という訳でdllについての備忘録。

今お使いの秀丸で使用されている「HmJre.dll」のバージョン確認や設定の方法、そのdllによる機能強化等についてはこちらの記事にまとまっています。

そして公式の配布先はこちら。 

秀まるおのホームページ(サイトー企画)−HmJre.dll

残念ながらdllのみの配布は現在は行われていないようなので、バージョンが古い場合は最新の秀丸本体をDLしてインストールするのが良いようです。

とりあえず秀丸の置換を手元で試してうまくいかない場合は確認してみることをおすすめします。

エクセルで同じ文字列が並んだ行の合計値を出す

エクセルの備忘録。

下図のようにA列に同じ文字列が隣接する場合に、その文字列ごとのB列の合計値を算出‥それを全ての行に対して手軽に行う方法について。
この例だと同じパスのアセットのファイルサイズを合計したい場面になります。

f:id:moko_03_25:20160823023951p:plain

つまりこうなればOKという訳です。

f:id:moko_03_25:20160823023952p:plain

もっと簡単な表でやってみます。
空欄のC1セルを選んで‥

=IF($A1=$A2, "",SUMIF($A:$A,$A1,$B:$B))

と入力します。こちらは我がエクセル師匠に教えてもらいました。
意味は‥
①(IF部分)もしセルA1がセルA2と同じ値なら今選択しているセルC1は空白にする
②(SUMIF部分)IFに該当しないならA列全体の範囲で値がA1と同じ行のB列を合計する

しかしA列の値がA B A B C Aみたいにバラバラだったらうまくいかないのでソートして固めておかないとダメですね。

さてまだ何も表示されません。

f:id:moko_03_25:20160823023945p:plain

セルをだーーっと最下行までコピーします。
するとA列で同じ文字列が続いた場合は最後の行のC列に合計値が入ります。
それ以外のセルが空白のままというのがミソです。

f:id:moko_03_25:20160823023946p:plain

一番頭に一行挿入してC列をフィルタリング。

f:id:moko_03_25:20160823023947p:plain

空白セルを非表示にします。

f:id:moko_03_25:20160823023948p:plain

これで同じ行をまとめてしまって、ついでにB列の合計値を出せました。

f:id:moko_03_25:20160823023949p:plain

結果を別のシートにでもコピー&ペースト。
いらなくなったB列を削除して完成です。

f:id:moko_03_25:20160823023950p:plain

以上になります。

同じ文字列が隣接する場合にその行を削除してしまいつつも直接B列に合計値を上書けたらもっと手間が省けるのですがVBAでやるしか無いんですかね。。
良い方法をご存じでしたらご教授くださいっ。

*翌日の追記*
検索でこちらのページが見つかり、そのままサックリできました!

Sub Sample()
    Dim c As Range
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    With Sheets("Sheet1")
        For Each c In .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
            dic(c.Value) = dic(c.Value) + Val(c.Offset(, 1).Value)
        Next
        .Columns("C:D").ClearContents
        .Range("C1").Resize(dic.Count).Value = WorksheetFunction.Transpose(dic.keys)
        .Range("D1").Resize(dic.Count).Value = WorksheetFunction.Transpose(dic.items)
    End With
 End Sub
(上記サイト様より引用させて頂いています)

ついでにA列B列を削除して値の表示形式をユーザー定義でMiB表記に変えて、一定以上の値は赤字に塗るとかも追記してしまうと良さそうですね。

秀丸の正規表現でパスとアセット名を分離

正規表現の備忘録です。

例えばエクセルでリストを作っている時に、下図のようにパスとアセット名が一緒になった状態からパスとアセット名を別セルに分離したい場面があったりします。

 f:id:moko_03_25:20160823013319p:plain

f:id:moko_03_25:20160823013320p:plain

しかしエクセルの標準機能でのやり方が分からないので、秀丸正規表現で分離することにしました(もっとお手軽な方法があればご教授ください)。

まずはリストを秀丸にコピペします。

f:id:moko_03_25:20160823013321p:plain

次に置換ダイアログを表示。
正規表現をONにして「検索」と「置換」に下図のように入力します。

f:id:moko_03_25:20160823013322p:plain

.+¥¥¥f¥f
¥0¥t¥1

検索欄の文字列の意味は‥
「1文字以上の文字列で末尾は¥」と「以降の文字列」にグループを分ける
文字列の後ろに¥fを追加するとグループ分けできます。
置換欄の文字列の意味は‥
「0番のグループの文字列」「水平タブ」「1番のグループの文字列」
‥です。
元のパスの¥による階層の区切りの数が行によってバラバラですが、検索欄の指定がたったこれだけでOKなのは秀丸正規表現には「最長の一致」という性質があるからのようです。

それから、文字列をグループに分けて置換に元の文字列を残しつつ形態を変える方法についてはこちらの記事でとても丁寧に解説されています。

‥という訳で置換を実行したら下図にようになりました。
全体を選択してコピーします。

f:id:moko_03_25:20160823013323p:plain

エクセルにペースト。はい、分離できました!

f:id:moko_03_25:20160823013324p:plain

もしエクセルにペーストした時にタブスペースを挟んで文字列が別セルに分かれない場合には、ホームタブの「区切り番号」で区切り文字を「タブ」にチェックを入れてからもう一度ペーストしてみてください。

以上になります。

あ、ちなみにセルA1とセルB1の文字列を結合するのは簡単です。
どこかのセルに「=A1&B1」のように入力すれば文字列が合体します。