GitHub Pages で簡単に使える「ファイル名チェッカー」構築手順

# GitHub Pages で簡単に使える「ファイル名チェッカー」構築手順

Web上で手軽に「ファイル名が正しい形式かどうか」をチェックするサービスを作りたいときに役立つサンプルです。  
今回は **GitHub Pages** を使い、**正規表現（Regex）を自由に編集・追加しながら**ファイル名をチェックできるツールを構築しました。

---

## 1. 機能の概要

- **正規表現によるファイル名バリデーション**  
  たとえば、「授業名 + `_第○回演習○_` + 学籍番号 + 氏名 + 拡張子」など、細かいフォーマットを厳密にチェックできます。

- **複数のプリセット管理**  
  画面右側のセレクトボックスで **プリセットの正規表現** を選ぶだけで、パターンを切り替えてチェックできます。

- **ユーザーがカスタム正規表現を追加**  
  テキストボックスに入力して「追加」ボタンを押すと、独自の正規表現を**LocalStorage**に保存し、次回アクセス時も継続利用が可能になります。

- **拡張子を URL パラメータで切り替え**  
  例： `?ext=docx` とすると、`.docx` 拡張子を期待する正規表現を自動生成。  
  同じファイル名パターンで `.pdf` や `.xlsx` など、複数種類の拡張子を切り替えられます。

---

## 2. ファイル構成
```
regex-file-checker/ 
├─ index.html 
├─ style.css 
└─ script.js
```


### index.html のポイント

- **ファイルを選択する部分 (`<input type="file" />`)**  
- **正規表現の入力欄 + 「追加」ボタン + プリセットの選択ボックス**  
- **ローカルストレージのデータを使ってプリセットを復元**  
- **注意事項**（ファイル名でよくあるミスについてのリスト）も含めてユーザーに説明  

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>ファイル名チェッカー</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <header>
    <h1>ファイル名チェッカー</h1>
  </header>

  <main>
    <section>
      <p>
        以下の形式のファイル名でアップロードしてください。<br />
        ・授業名 + _第○回演習○_ + 学籍番号 + 氏名.<br />
        （例：<strong>情報科学入門1122_第2回演習2_123456789鈴木一郎.docx</strong> など）
      </p>

      <div class="upload-area">
        <label for="fileInput" class="file-label">ファイルを選択</label>
        <input type="file" id="fileInput" />
      </div>

      <div id="uploadedFileName" class="uploaded-file-name">
        （現在アップロードしているファイル名が表示されます）
      </div>

      <!-- 正規表現の入力欄 + 追加ボタン -->
      <div class="regex-input-area">
        <label for="regexPattern">正規表現を入力：</label>
        <input
          type="text"
          id="regexPattern"
          placeholder="正規表現をここに入力"
          size="50"
        />
        <button id="addRegexBtn" class="add-regex-btn">追加</button>
      </div>

      <!-- プリセットの正規表現選択ボックス -->
      <div class="regex-select-area">
        <label for="regexSelect">プリセットの正規表現：</label>
        <select id="regexSelect">
          <!-- スクリプト側で localStorage から読み込み＆生成 -->
        </select>
      </div>

      <button id="checkBtn" class="check-btn">ファイル名をチェック</button>
      <div id="result" class="result"></div>
    </section>

    <!-- 注意喚起のセクション復元 -->
    <section>
      <h2>ファイル名で間違えやすい点 (注意事項)</h2>
      <ul>
        <li>
          <strong>授業名や学籍番号・氏名をきちんと含めているか</strong><br />
          例：<code>データ処理1101_第5回演習1_123456789山田太郎</code> のように必要事項を漏れなく記載しましょう。
        </li>
        <li>
          <strong>拡張子を間違えていないか</strong><br />
          例：<code>.docx</code> のところを <code>.pdf</code> にしてしまうなど。ファイル選択ダイアログでは拡張子だけでなく、最終的なファイル名の末尾を確認してください。
        </li>
        <li>
          <strong>演習番号が正しいか</strong><br />
          例：<code>第5回演習1</code> のはずが <code>第5回演習2</code> になっていないか、番号を再確認しましょう。
        </li>
        <li>
          <strong>日にち・日付が必要な場合、正しい日付が入っているか</strong><br />
          提出期限や授業日など、指定された日付と合致しているか確認してください。
        </li>
        <li>
          <strong>ファイル名に不要なスペース・文字が含まれていないか</strong><br />
          例：全角スペースや特殊文字（「/」「*」「?」など）を誤って入れていないか注意しましょう。
        </li>
        <li>
          <strong>文字コードの違いで見た目が同じでも別の文字になっていないか</strong><br />
          例：「デ」が「テ+結合濁点」になっているケースなど。アップロード時はファイル名の濁点や拗音などに注意してください。
        </li>
        <li>
          <strong>大文字と小文字を区別するサービスの場合に注意</strong><br />
          例：拡張子 <code>.DOCX</code> と <code>.docx</code> の扱いが異なる場合があります。大文字小文字は正しく揃えましょう。
        </li>
        <li>
          <strong>ファイル名が長すぎたり、区切りが不明瞭になっていないか</strong><br />
          必要な情報を簡潔にまとめつつ、環境によっては長すぎるファイル名を扱いきれないこともあるため注意してください。
        </li>
      </ul>
    </section>
  </main>

  <footer>
    <p class="footer-text">&copy; 2024 Regex Checker</p>
  </footer>

  <script src="script.js"></script>
</body>
</html>
```

### style.css
```css
/* 全体のリセット */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* ベースフォント・背景 */
body {
  font-family: sans-serif;
  background: #f0f8ff;
  color: #333;
  padding: 20px;
}

/* ヘッダー */
header {
  text-align: center;
  margin-bottom: 30px;
}

header h1 {
  font-size: 2em;
  margin-bottom: 10px;
}

/* メイン */
main {
  max-width: 600px;
  margin: 0 auto;
  background: #fff;
  padding: 20px;
  border-radius: 8px;
}

/* アップロードエリア */
.upload-area {
  margin-bottom: 20px;
}

.file-label {
  display: inline-block;
  background: #4caf50;
  color: #fff;
  padding: 10px 15px;
  border-radius: 5px;
  cursor: pointer;
}

#fileInput {
  display: none; /* ラベルでコントロールするため非表示 */
}

/* アップロードしたファイル名 */
.uploaded-file-name {
  margin: 10px 0;
  font-weight: bold;
}

/* 正規表現入力欄 */
.regex-input-area {
  margin: 15px 0;
}

.add-regex-btn {
  margin-left: 5px;
}

/* プリセットの正規表現 */
.regex-select-area {
  margin-bottom: 15px;
}

/* チェックボタン */
.check-btn {
  display: inline-block;
  padding: 10px 15px;
  background: #2196f3;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1rem;
}

/* 結果表示 */
.result {
  margin-top: 20px;
  padding: 15px;
  border-radius: 5px;
}

/* フッター */
footer {
  text-align: center;
  margin-top: 30px;
  color: #666;
}
```

### script.js のポイント
- **LocalStorage を使ってプリセットを保存**

    - regexPresets 配列を JavaScript のオブジェクトとして持ち、JSON.stringify() / JSON.parse() で保存・読込。
- **「追加」ボタン** でテキストボックス入力内容をプリセットに加える

    - 重複があれば追加しない。
- **選択ボックス** で現在選択中の正規表現を確認し、拡張子 ext と結合して new RegExp() で実行。

- **ファイル名の Unicode 正規化 (NFC)**

- **「デ」のように結合文字が含まれていても、見た目通りにマッチするように考慮。**


```js
// 1) localStorageキー
const LOCAL_STORAGE_KEY = "regexPresets";

// 2) デフォルトプリセット
let regexPresets = [
  '^[ぁ-んァ-ヶ一-龠a-zA-Z0-9_-]+_第\\d+回演習\\d+_[0-9]{9}[ぁ-んァ-ヶ一-龠a-zA-Z]+\\.'
];

// localStorage からプリセットを読み込み
function loadRegexPresets() {
  const stored = localStorage.getItem(LOCAL_STORAGE_KEY);
  if (stored) {
    try {
      regexPresets = JSON.parse(stored);
    } catch (e) {
      console.warn("Failed to parse localStorage, using default presets.");
    }
  }
}

// プリセット保存
function saveRegexPresets() {
  localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(regexPresets));
}

// 選択ボックスを再描画
function renderRegexSelect() {
  const regexSelect = document.getElementById("regexSelect");
  regexSelect.innerHTML = "";

  regexPresets.forEach((pattern, index) => {
    const option = document.createElement("option");
    option.value = pattern;
    option.textContent = pattern;
    if (index === 0) {
      option.selected = true;
      document.getElementById("regexPattern").value = pattern;
    }
    regexSelect.appendChild(option);
  });
}

// 初期化処理
loadRegexPresets();
renderRegexSelect();

// URLから拡張子 (例: ?ext=docx)
const urlParams = new URLSearchParams(window.location.search);
let ext = urlParams.get("ext") || "docx";

// ファイル選択
const fileInput = document.getElementById("fileInput");
fileInput.setAttribute("accept", `.${ext}`);

fileInput.addEventListener("change", () => {
  const uploadedFileNameDiv = document.getElementById("uploadedFileName");
  if (fileInput.files.length > 0) {
    let rawName = fileInput.files[0].name;
    rawName = rawName.normalize("NFC");
    uploadedFileNameDiv.textContent = `現在のファイル名: ${rawName}`;
  } else {
    uploadedFileNameDiv.textContent = "ファイルが選択されていません。";
  }
});

// 「追加」ボタン
document.getElementById("addRegexBtn").addEventListener("click", () => {
  const newPattern = document.getElementById("regexPattern").value.trim();
  if (!newPattern) {
    alert("正規表現が未入力です。");
    return;
  }
  // 重複チェック
  if (!regexPresets.includes(newPattern)) {
    regexPresets.unshift(newPattern);
    saveRegexPresets();
    renderRegexSelect();
  }
  // 先頭を選択状態に
  document.getElementById("regexSelect").selectedIndex = 0;
});

// 選択ボックス変更時
document.getElementById("regexSelect").addEventListener("change", (e) => {
  document.getElementById("regexPattern").value = e.target.value;
});

// 「ファイル名をチェック」ボタン
document.getElementById("checkBtn").addEventListener("click", () => {
  const resultDiv = document.getElementById("result");
  resultDiv.textContent = "";
  resultDiv.style.background = "";

  if (!fileInput.files || fileInput.files.length === 0) {
    resultDiv.textContent = "ファイルが選択されていません。";
    resultDiv.style.background = "#ffcccc";
    return;
  }

  const selectedPattern = document.getElementById("regexSelect").value;
  if (!selectedPattern) {
    resultDiv.textContent = "正規表現の選択肢がありません。";
    resultDiv.style.background = "#ffcccc";
    return;
  }

  let fileName = fileInput.files[0].name;
  fileName = fileName.normalize("NFC");

  // 実際に使う正規表現： 選択されたパターン + ext + '$'
  let fullPattern = selectedPattern + ext + '$';

  let regexObj;
  try {
    regexObj = new RegExp(fullPattern);
  } catch (error) {
    resultDiv.textContent = `正規表現が不正です: ${error.message}`;
    resultDiv.style.background = "#ffcccc";
    return;
  }

  if (regexObj.test(fileName)) {
    resultDiv.textContent = `「${fileName}」は適切なファイル名です (拡張子: ${ext}).`;
    resultDiv.style.background = "#c8e6c9"; // 緑背景
  } else {
    resultDiv.innerHTML = `
      「${fileName}」は指定された形式ではありません。<br />
      <strong>正規表現:</strong> <code>${fullPattern}</code>
    `;
    resultDiv.style.background = "#ffcdd2"; // 赤背景
  }
});
```

## 3. GitHub Pages でのホスティング (GitHub Actions + Jekyll)

1. **GitHub リポジトリを作成**  
   例：`regex-file-checker`  
   - `index.html`, `style.css`, `script.js` などのファイルをルート階層にアップロードします。

2. **Jekyll 用のワークフローを設定 (Actions)**  
   - `.github/workflows/jekyll-gh-pages.yml` のような名前で以下の内容を用意し、コミットしてください。  
   - このワークフローでは GitHub Pages のデプロイを GitHub Actions の workflow で実行します。  
   - 例のワークフロー（最小構成）は下記のようなものです。

   ```yaml
   # Sample workflow for building and deploying a Jekyll site to GitHub Pages
   name: Deploy Jekyll with GitHub Pages dependencies preinstalled

   on:
     # Runs on pushes targeting the default branch
     push:
       branches: ["main"]

     # Allows you to run this workflow manually from the Actions tab
     workflow_dispatch:

   # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
   permissions:
     contents: read
     pages: write
     id-token: write

   concurrency:
     group: "pages"
     cancel-in-progress: false

   jobs:
     # Build job
     build:
       runs-on: ubuntu-latest
       steps:
         - name: Checkout
           uses: actions/checkout@v4
         - name: Setup Pages
           uses: actions/configure-pages@v5
         - name: Build with Jekyll
           uses: actions/jekyll-build-pages@v1
           with:
             source: ./
             destination: ./_site
         - name: Upload artifact
           uses: actions/upload-pages-artifact@v3

     # Deployment job
     deploy:
       environment:
         name: github-pages
         url: ${{ steps.deployment.outputs.page_url }}
       runs-on: ubuntu-latest
       needs: build
       steps:
         - name: Deploy to GitHub Pages
           id: deployment
           uses: actions/deploy-pages@v4

<br>
3. Actions タブを確認してデプロイ

- `main` ブランチにプッシュすると、先ほどのワークフローが自動で走り、Jekyll のビルドが行われた後、GitHub Pages へのデプロイが実行されます。  
- 成功すると、`Settings > Pages` でデプロイ先の URL が確認できます。  
- 例：`https://ユーザー名.github.io/リポジトリ名/`

これにより、**Jekyll を利用した静的サイト**として、今回のファイル名チェッカーも含めたコンテンツが自動ビルド＆公開されます。誰でもアクセス可能になるので、ファイル名のチェックを簡単に試してもらえます。



---

## 4. 使い方まとめ

1. **正規表現を追加**  
   - テキストボックスに書き込み、「追加」ボタンを押すと「プリセットの正規表現」に登録されます。  
   - `localStorage` に保存されるため、再読み込みしても残ります。

2. **拡張子を切り替える**  
   - `?ext=pdf` のようにクエリパラメータを変更 → 同じフォーマットでも拡張子を `pdf` に変更してチェック可能  

3. **ファイルを選択**  
   - 現在の拡張子とマッチするファイルのみダイアログで推奨表示。  
   - 選択後、ファイル名が画面に表示されます。

4. **ファイル名をチェック**  
   - 選択中のプリセット正規表現 + 拡張子で判定し、マッチすれば「緑色」、マッチしなければ「赤色」で詳細が表示されます。

---

## 5. 注意点 & 拡張アイデア

- **正規表現のエスケープ**  
  バックスラッシュや特殊文字を含む場合、文字列リテラルで二重にエスケープする必要があります。

- **ブラウザ依存**  
  `input[type="file"]` の `accept=".pdf,.docx"` は一部ブラウザで完全には制限されないことがあります。サーバー側で再チェックすることを推奨。

- **複数拡張子を一度に指定**  
  `ext` パラメータを配列化する、あるいは正規表現に `(pdf|docx|xlsx)$` のように書く方法もあります。

- **日付や回数部分を厳密にしたい**  
  例：`第\d{1,2}回演習\d{1,2}` などのように回数の桁数を制限する、年月日を `YYYYMMDD` 形式にするなど、要件にあわせて編集可能です。

---

## 6. GitHub リポジトリの紹介

今回作成したサンプルのファイル名チェッカーは、以下のリポジトリにアップしています。  
どなたでも自由にクローン・フォークしていただき、カスタマイズや学習にご利用ください。  

- [brightwaltz/regex-file-checker](https://github.com/brightwaltz/regex-file-checker)

コアとなる `index.html`, `style.css`, `script.js` を含め、GitHub Actions や Jekyll を使った自動デプロイ用のファイルも合わせて公開しています。必要な箇所を修正して、ぜひご自身の用途に合わせてご活用ください。
MIT Licenseです。

---

## 7. 実際に使う際の手順

サンプルとして、以下のURLで公開中のページをご覧ください。  
[https://brightwaltz.github.io/regex-file-checker/?ext=docx](https://brightwaltz.github.io/regex-file-checker/?ext=docx)

1. **ページにアクセス**  
   - URLにアクセスすると、今回ご紹介した「ファイル名チェッカー」が表示されます。
   - `?ext=docx` と指定しているため、デフォルト拡張子は `.docx` となっています。

2. **ファイルを選択**  
   - 画面上部の「ファイルを選択」ボタンを押すと、ファイル選択ダイアログが開きます。
   - `.docx` ファイルが推奨表示されますが、実際には他の拡張子も選択できます（ブラウザによる仕様）。
   - 選択後、現在アップロードしているファイル名が画面上に表示されます。

3. **正規表現の利用方法**  
   - デフォルトで、プリセットの正規表現が1つ選択されています。  
   - 自由に正規表現を追加したい場合は、テキストボックスに書き込んで「追加」ボタンを押すと、プリセットに登録されます。  
   - 登録されたプリセットは localStorage に保存されるため、次回ページを再読み込みしても消えません。

4. **「ファイル名をチェック」ボタンを押す**  
   - 選択したファイル名が、現在選択中の「プリセット正規表現 + 拡張子」で構築されたパターンに合致するか判定されます。
   - 結果は下部に「適切なファイル名です」(緑色) もしくは「指定された形式ではありません」(赤色) として表示されます。

5. **他の拡張子を利用する**  
   - `?ext=` の値を `pdf` や `xlsx` に書き換えてアクセスすると、拡張子が変わります。  
   - 例：`https://brightwaltz.github.io/regex-file-checker/?ext=pdf`  
   - これにより、プリセット正規表現の後ろに `.pdf` が付く形でチェックが行われます。

6. **注意事項**  
   - ブラウザの仕様により、ファイル選択ダイアログで完全に拡張子を制限できない場合があります。
   - あくまでもフロントエンド上でのチェック機能なので、本番運用の場合はサーバー側のバリデーションと組み合わせることがおすすめです。

このページを実際に触ってみることで、**正規表現でのファイル名チェック**がどのような挙動になるかを簡単に確認できます。ぜひ動作テストや学習目的で活用してみてください。

---

## 8. まとめ

「ファイル名チェックを正規表現で行う」というだけでも便利ですが、

- GitHub Pages で手軽にホスティングできる  
- LocalStorage でユーザー独自の正規表現プリセットを管理できる  
- `?ext=xxx` パラメータで拡張子も柔軟に切り替え

といった拡張性があることで、教育機関でのファイル提出から社内プロジェクト管理に至るまで、多様なニーズに応えられます。ぜひ活用してみてください！

以上で紹介を終わります。  
他にも改良案やご提案があれば、ぜひコメントください。

---

## 9. ChatGPTとの共同作業

今回の記事は、ChatGPT (ChatGPT o1) との対話を通じて要件をまとめ、段階的に実装を調整しながら完成させました。以下の点について ChatGPT を活用しました。

- **要件定義・設計**  
  ユーザーがイメージする機能（拡張子切り替え、正規表現のプリセット管理、ファイル名提案など）を質問形式で詳細化し、ChatGPT からサンプルコードや修正提案を得ました。

- **実装サンプルの生成**  
  HTML・CSS・JavaScript それぞれのファイルのテンプレートを生成し、そこに修正を加えることで、段階的に完成度を上げました。特に、正規表現バリデーションや LocalStorage の保存機能なども、何度かやり取りしながらブラッシュアップしています。

- **機能拡張・要件変更**  
  途中で正規表現を自由に追加できるようにしたり、拡張子を URL パラメータから受け取るようにしたり、注意事項セクションを復元するなど、追加要望に対応するためのアイデアを ChatGPT から提示してもらい、それを取り入れて最終形へ近づけました。

- **文章校正・構成**  
  Qiita に投稿するための記事として体裁を整える段階でも、ChatGPT を利用して見出しや文章の流れを調整しました。結果として、初学者にもわかりやすい構成になっています。

このように、ChatGPT とのやりとりを通じて完成させたことから、非常にスムーズに試行錯誤を進めることができました。ぜひ、みなさんも ChatGPT やその他の生成系 AI を活用しながら開発・執筆を効率化してみてください！

---

### 参考リンク

- [GitHub Pages の公式ドキュメント](https://docs.github.com/ja/pages)  
- [MDN Web Docs: RegExp](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp)  
- [localStorage の使い方 (MDN)](https://developer.mozilla.org/ja/docs/Web/API/Window/localStorage)