2017年10月13日

[Word] Word 2016で数式に式番号を付ける方法

以前までは、Wordで番号付きの数式を挿入したいときは、以下のページで分かりやすく解説されているように、3列1行で罫線なしの表を作成する方法をとっていましたが、 もっと手軽かつ便利な方法を見つけたので、忘れないようにメモしておこうと思います。この方法は、Word 2016以降で用いることが可能です。
いつものように数式を挿入したあと、末尾に#を追加します。

カーソルが数式オブジェクトの中にある状態で、[参考資料]タブを選択し、[図表番号の挿入]ボタンをクリックします。 [図表番号]ダイアログが表示されるので、[オプション]グループの[ラベル]から[数式] ([数式]がアイテム一覧に含まれていない場合は[Equation]) を選択します。 また、[ラベルを図表番号から除外する]にチェックを入れます。

[OK]ボタンを押すと、数式番号が挿入されます (図では1になっています)。1の両隣に、丸かっこを追加します (お好みで自由に変えることができます)。

カーソルが数式の右端にある状態で、[Enter]キーを押せば完了です。

この作業を各々の数式について行うのは面倒だと思うので、使いまわせるように工夫します。 数式を挿入したあと、上の手順に倣って#、括弧、数式番号を追加し、右端のプルダウンメニューから[新しい数式として保存...]をクリックします。

 [新しい文書パーツの作成]ダイアログが表示されるので、適当な名前 (図では「番号付き数式2」としています) を入力し、[OK]ボタンを押します。

[挿入]タブに移動し、[数式]プルダウンメニューを展開させると、先ほど作成したアイテムが追加されていることが分かります。

このアイテムを選択すると、#、括弧、数式番号が既に追加された状態の数式が、文書内に挿入されます。 #の左側に数式を入力したあとは、先ほどと同様、カーソルを数式内の右端に移動させて[Enter]キーを押せば、自動的に数式番号を右寄せ、数式自体を中央揃えで表示してくれます。

次に、数式を文章中から参照する方法について簡単に書いておきます。以下の図のような文章を作成していて、(1)式への参照を追加したいとします。

上の図のように、まずは式番号のみを選択し、その状態で[挿入]タブの[リンク]メニューにある[ブックマーク]を選択します。[ブックマーク]ダイアログが表示されるので、[ブックマーク名]に適当な名前 (図では「FourierSeriesExpansion」としています) を入れ、[追加]ボタンを押します。ブックマーク名は数式ごとに変える必要があります。

次に、数式への参照を追加したい場所までカーソルを移動させ、[挿入]タブの[リンク]メニューにある[相互参照]を選択します。[相互参照]ダイアログが表示されるので、[参照する項目]には[ブックマーク]を、[相互参照の文字列]には[ブックマーク文字列]をそれぞれ選択し、[ハイパーリンクとして挿入する]にチェックを入れます。[ブックマークの参照先]に表示されているブックマークの中から、いま参照したい数式に設定されたブックマークを選択します (図ではブックマーク名「FourierSeriesExpansion」の数式を追加しようとしています)。

[挿入]ボタンを押すと、以下の図のように「(1)」が追加されていることが分かります。なお、上の説明において、「(1)」ではなく「1」のみを選択してブックマークを追加した場合は、「1」のみが追加されます。あとは、お好みで「式」などの適切な名前を式番号の後ろに付加すれば終わりです。

この方法は、以下のサイト(英語)で知りました。

2017年3月9日

[C#] ファイル名が正しいかどうかチェックする

ファイル名が正しいかどうかをチェックする方法を調べていたところ、Stack Overflowで以下のようなページが見つかりました。

これらの回答を基に、C#で正規表現を使ってファイル名が妥当かどうかをチェックする簡単なメソッドを作成してみました。まずまず動いているようなので安心です。

public static bool IsValidFileName(string fileName)
{
    var validFileNameRegex = new System.Text.RegularExpressions.Regex(
        "^(?!(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\\.[^.]*)?$)" +
        "[^<>:\"/\\\\|?*\\x00-\\x1F]*[^<>:\"/\\\\|?*\\x00-\\x1F\\ .]$",
        System.Text.RegularExpressions.RegexOptions.IgnoreCase);

    System.Text.RegularExpressions.Match regexMatch = validFileNameRegex.Match(fileName);

    return regexMatch.Success;
}

System.IO.Path.GetInvalidFileNameChars()メソッドはファイル名に使用できない文字を含む配列を返すので、以下のようなコードで、ファイル名に使用できない文字が含まれているかどうかを調べることができます。但し、この方法はシンプルではありますが、CONPRNAUXといったファイル名として使用できない文字列が含まれているかどうかを調べていないため、少々不完全です。

public static bool IsValidFileName(string fileName) => 
    fileName.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) < 0;

System.IO.FileInfoクラスのコンストラクタ―は絶対パスあるいは相対パスを1つ引数に取ります。コンストラクタが例外を投げなければファイル名が正しいと判断することができます。しかし、この方法でも、CONPRNAUXなどの本来禁止されているはずのファイル名が通ってしまうので、やはり不完全です。

public static bool IsValidFileName(string fileName)
{
    System.IO.FileInfo fileInfo = null;

    try {
        fileInfo = new System.IO.FileInfo(fileName);
    } catch (Exception e) when (
        e is ArgumentNullException || 
        e is ArgumentException || 
        e is System.IO.PathTooLongException || 
        e is NotSupportedException) {
    }

    return fileInfo != null;
}

Windows APIのCreateFile()関数をP/Invokeを使って呼び出す方法もあります (やはり、CONPRNAUXなどが通ってしまいます) まず最初に、CreateFile()関数をファイル名とともに呼び出して、ファイルハンドルを取得します。次に、.NET FrameworkのSystem.Runtime.InteropServices.Marshal.GetLastWin32Error()メソッドの戻り値が、ファイル名が正しくないことを示すエラーコードERROR_INVALID_NAME(123)であるかどうかをチェックします。

P/InvokeによりWindows APIのGetLastError()関数を呼び出すのは厳禁です。また、CreateFile()関数の、DllImport属性のSetLastErrorフィールドをtrueにする必要があります。

また、以下の例ではMicrosoft.Win32.SafeHandles.SafeFileHandleクラスを使用しているため、CreateFile()関数で有効なファイルハンドルが返された場合でも、CloseHandle()関数を呼んでハンドルを閉じる必要はありません。

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

// ...

public const uint GENERIC_READ = 0x80000000;
public const uint OPEN_EXISTING = 3;
public const int ERROR_INVALID_NAME = 123;

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateFile(
    string lpFileName, uint dwDesiredAccess,
    uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
    uint dwFlagsAndAttributes, IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);

public static bool IsValidFileName(string fileName)
{
    IntPtr hFile = CreateFile(fileName, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
    SafeFileHandle hFileSafe = new SafeFileHandle(hFile, true);
    return Marshal.GetLastWin32Error() != ERROR_INVALID_NAME;
}

一番良さそうなのは最初の正規表現による方法だと思います。この手の処理をやってくれるメソッドなりクラスなりを、早く標準の.NET Frameworkに追加して欲しいものです。