JavaScript開発で役立つ便利なTips 2024年版
モダンなJavaScript開発で使える便利なテクニックとベストプラクティスを紹介します
「正規表現って呪文みたい...」そんなあなたに贈る、実務でよく使う正規表現パターン集。メール、URL、日本語処理など、コピペで使える実用的なパターンを分かりやすく解説します!
正規表現(Regular Expression、略して Regex)。この言葉を聞いただけで頭が痛くなる人、多いんじゃないでしょうか?
私も最初は「なんだこの暗号…」と思っていました。でも、一度パターンを覚えてしまえば、文字列処理がめちゃくちゃ楽になるんです!今日は、実務でよく使う 10 個のパターンを、実例とともに紹介していきます。
まずは定番中の定番から。でも、完璧なメールアドレスの正規表現って実はものすごく複雑なんです…。
// シンプルで実用的なパターン
const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
// 使用例
const validateEmail = (email) => {
return emailPattern.test(email);
};
console.log(validateEmail('user@example.com')); // true
console.log(validateEmail('test.user+tag@sub.example.co.jp')); // true
console.log(validateEmail('invalid@')); // false
console.log(validateEmail('@example.com')); // false
^
: 文字列の開始[a-zA-Z0-9._%+-]+
: ユーザー名部分(英数字と一部の記号)@
: アットマーク(そのまま)[a-zA-Z0-9.-]+
: ドメイン名\.
: ドット(エスケープが必要)[a-zA-Z]{2,}
: TLD(2 文字以上)$
: 文字列の終了SNS やチャットアプリでよく使われる url 抽出パターン:
// URLを抽出する正規表現
const urlPattern =
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
// 使用例
const text = '詳細はhttps://example.com/docs/guide.htmlとhttp://localhost:3000を参照してください。';
const urls = text.match(urlPattern);
console.log(urls);
// ["https://example.com/docs/guide.html", "http://localhost:3000"]
// リンクをクリッカブルにする
const makeClickable = (text) => {
return text.replace(urlPattern, '<a href="$&" target="_blank">$&</a>');
};
日本語を扱うときの正規表現は、Unicode の範囲を理解する必要があります:
// 日本語の文字種別パターン
const patterns = {
hiragana: /[\u3040-\u309F]+/g, // ひらがな
katakana: /[\u30A0-\u30FF]+/g, // カタカナ
kanji: /[\u4E00-\u9FAF]+/g, // 漢字
japanese: /[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]+/g, // 全部
};
// 使用例
const text = '私はJavaScriptでプログラミングを勉強しています。';
console.log(text.match(patterns.hiragana)); // ["は", "で", "を", "しています"]
console.log(text.match(patterns.katakana)); // ["プログラミング"]
console.log(text.match(patterns.kanji)); // ["私", "勉強"]
// カタカナをひらがなに変換(簡易版)
const katakanaToHiragana = (str) => {
return str.replace(/[\u30A0-\u30FF]/g, (match) => {
return String.fromCharCode(match.charCodeAt(0) - 0x60);
});
};
console.log(katakanaToHiragana('プログラミング')); // "ぷろぐらみんぐ"
日本の電話番号は形式がいろいろあって厄介ですよね:
// 日本の電話番号パターン
const phonePatterns = {
// ハイフンあり・なし両対応
mobile: /^0[789]0-?\d{4}-?\d{4}$/,
landline: /^0\d{1,4}-?\d{1,4}-?\d{4}$/,
all: /^0\d{1,4}-?\d{1,4}-?\d{4}$/,
};
// フォーマット関数
const formatPhoneNumber = (phone) => {
// 数字以外を削除
const numbers = phone.replace(/\D/g, '');
// 携帯電話
if (numbers.match(/^0[789]0/)) {
return numbers.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
}
// 市外局番が2桁(東京など)
else if (numbers.match(/^0[3-9]/)) {
return numbers.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3');
}
// その他
return numbers;
};
console.log(formatPhoneNumber('09012345678')); // "090-1234-5678"
console.log(formatPhoneNumber('0312345678')); // "03-1234-5678"
ユーザー入力から html タグを取り除きたいとき:
// HTMLタグを除去する正規表現
const stripHtmlTags = (html) => {
return html.replace(/<[^>]*>/g, '');
};
// より安全な版(scriptタグの中身も削除)
const stripHtmlSafe = (html) => {
return html
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/<[^>]*>/g, '');
};
const dirty = '<p>こんにちは<script>alert("XSS")</script><b>世界</b></p>';
console.log(stripHtmlTags(dirty)); // "こんにちはalert("XSS")世界"
console.log(stripHtmlSafe(dirty)); // "こんにちは世界"
セキュアなパスワードかどうかをチェック:
// パスワード強度チェック
const passwordChecks = {
// 最低8文字
minLength: /.{8,}/,
// 大文字を含む
hasUpperCase: /[A-Z]/,
// 小文字を含む
hasLowerCase: /[a-z]/,
// 数字を含む
hasNumber: /\d/,
// 特殊文字を含む
hasSpecialChar: /[!@#$%^&*(),.?":{}|<>]/,
};
const checkPasswordStrength = (password) => {
const strength = {
score: 0,
feedback: [],
};
if (!passwordChecks.minLength.test(password)) {
strength.feedback.push('8文字以上にしてください');
} else {
strength.score++;
}
if (!passwordChecks.hasUpperCase.test(password)) {
strength.feedback.push('大文字を含めてください');
} else {
strength.score++;
}
// ... 他のチェックも同様に
return strength;
};
日本語入力でよくある全角・半角の統一:
// 全角数字を半角に変換
const zenkakuToHankaku = (str) => {
return str.replace(/[0-9]/g, (s) => {
return String.fromCharCode(s.charCodeAt(0) - 0xfee0);
});
};
// 半角カナを全角に変換(一部のみ)
const hankakuKanaToZenkaku = (str) => {
const kanaMap = {
ア: 'ア',
イ: 'イ',
ウ: 'ウ',
エ: 'エ',
オ: 'オ',
// ... 省略
};
return str.replace(/[ア-ン]/g, (match) => kanaMap[match] || match);
};
console.log(zenkakuToHankaku('12345')); // "12345"
クレジットカード番号のフォーマットチェック(実際の検証には Luhn アルゴリズムも必要):
const creditCardPatterns = {
visa: /^4[0-9]{12}(?:[0-9]{3})?$/,
mastercard: /^5[1-5][0-9]{14}$/,
amex: /^3[47][0-9]{13}$/,
jcb: /^(?:2131|1800|35\d{3})\d{11}$/,
};
const detectCardType = (number) => {
const cleaned = number.replace(/\s/g, '');
for (const [type, pattern] of Object.entries(creditCardPatterns)) {
if (pattern.test(cleaned)) {
return type;
}
}
return 'unknown';
};
// 番号をマスクする
const maskCreditCard = (number) => {
return number.replace(/\d(?=\d{4})/g, '*');
};
console.log(maskCreditCard('4111 1111 1111 1111')); // "**** **** **** 1111"
アップロードされたファイルの種類をチェック:
const fileTypePatterns = {
image: /\.(jpg|jpeg|png|gif|webp|svg)$/i,
video: /\.(mp4|avi|mov|wmv|flv)$/i,
audio: /\.(mp3|wav|ogg|m4a)$/i,
document: /\.(pdf|doc|docx|xls|xlsx|ppt|pptx)$/i,
code: /\.(js|ts|jsx|tsx|py|java|cpp|c|go|rs)$/i,
};
const getFileType = (filename) => {
for (const [type, pattern] of Object.entries(fileTypePatterns)) {
if (pattern.test(filename)) {
return type;
}
}
return 'other';
};
console.log(getFileType('photo.jpg')); // "image"
console.log(getFileType('index.js')); // "code"
console.log(getFileType('document.pdf')); // "document"
見えない文字や余計な空白を整理:
// 様々な空白文字を処理
const whitespacePatterns = {
// 連続する空白を1つに
multipleSpaces: /\s+/g,
// 行頭行末の空白
trim: /^\s+|\s+$/g,
// 全角スペースを半角に
zenkakuSpace: / /g,
// 改行の統一
lineBreaks: /\r\n|\r|\n/g,
};
const normalizeText = (text) => {
return text
.replace(whitespacePatterns.zenkakuSpace, ' ') // 全角→半角
.replace(whitespacePatterns.multipleSpaces, ' ') // 連続空白を1つに
.replace(whitespacePatterns.trim, '') // 前後の空白削除
.replace(whitespacePatterns.lineBreaks, '\n'); // 改行を統一
};
const messy = ' こんにちは 世界 \r\n Hello World ';
console.log(normalizeText(messy)); // "こんにちは 世界\nHello World"
正規表現をデバッグするときの私のおすすめツール:
// デバッグ用ヘルパー関数
const regexDebug = (pattern, text) => {
console.log(`Pattern: ${pattern}`);
console.log(`Text: "${text}"`);
console.log(`Match: ${pattern.test(text)}`);
const matches = text.match(pattern);
if (matches) {
console.log('Matches:', matches);
}
};
正規表現は最初は難しく感じますが、よく使うパターンを覚えておけば、文字列処理が格段に楽になります。今回紹介したパターンは、そのままコピペして使えるものばかりなので、ぜひ活用してください!
ちなみに、私の好きな正規表現ジョークがあります: 「正規表現で問題を解決しようとすると、問題が 2 つになる」
…確かに、複雑になりすぎると逆に大変ですよね。適材適所で使っていきましょう!
次回は「シェルスクリプト高速化テクニック」について解説します。処理時間を劇的に短縮する方法、お楽しみに!