Help us understand the problem. What is going on with this article?

可能な限りRFCに準拠したEメールアドレス検証用正規表現

More than 1 year has passed since last update.

はじめに

ウェブサービスを作っていると、入力されたメールアドレスが正しい形式か確かめたいということがあると思います。

今ならHTML5でinput要素にtype="email"が使えますし、そもそも検証せずとも実際に送ってみればいいのですが、やっぱり事前に検証したいというときもありますよね。

ただ、RFC5321RFC5322で規定されているメールアドレスの形式って結構複雑で、ほとんどのサービスでは簡易的な正規表現でチェックしてたりします。

そこで、できるだけRFCに準拠した正規表現に挑戦してみました。

とりあえず結果教えろ

はい。

/^([\w!#$%&'*+\-\/=?^`{|}~]+(\.[\w!#$%&'*+\-\/=?^`{|}~]+)*|"([\w!#$%&'*+\-\/=?^`{|}~. ()<>\[\]:;@,]|\\[\\"])+")@(([a-zA-Z\d\-]+\.)+[a-zA-Z]+|\[(\d{1,3}(\.\d{1,3}){3}|IPv6:[\da-fA-F]{0,4}(:[\da-fA-F]{0,4}){1,5}(:\d{1,3}(\.\d{1,3}){3}|(:[\da-fA-F]{0,4}){0,2}))\])$/

わけわかんねーよ解説しろよ

そんなこといわれても解説できません。こっちだってわけわかんねーんだよヽ(`Д´#)ノウワァァァン

生成過程を公開しますので、がんばって解析してください。
(ECMAScriptで書いてます)

// 各パートで使用可能な文字セット(デリミタや特殊文字を除く)
const REGEXP_CHARSET_DOT = "[\\w!#$%&'*+\\-\\/=?^`{|}~]"; // dot-string
const REGEXP_CHARSET_QUOTED = "[\\w!#$%&'*+\\-\\/=?^`{|}~. ()<>\\[\\]:;@,]"; // quoted-string
const REGEXP_CHARSET_TLD = "[a-zA-Z]"; // トップレベルドメイン
const REGEXP_CHARSET_SLD = "[a-zA-Z\\d\\-]"; // サブレベルドメイン
const REGEXP_CHARSET_IPV4 = "\\d"; // IPv4
const REGEXP_CHARSET_IPV6 = "[\\da-fA-F]"; // IPv6

// 各パートの構成要素として受理可能なパターン(デリミタや特殊文字を除く)
const REGEXP_COMPONENT_DOT = `${REGEXP_CHARSET_DOT}+`;
const REGEXP_COMPONENT_QUOTED = `(${REGEXP_CHARSET_QUOTED}|\\\\[\\\\"])+`;
const REGEXP_COMPONENT_TLD = `${REGEXP_CHARSET_TLD}+`;
const REGEXP_COMPONENT_SLD = `${REGEXP_CHARSET_SLD}+`;
const REGEXP_COMPONENT_IPV4 = `${REGEXP_CHARSET_IPV4}{1,3}`;
const REGEXP_COMPONENT_IPV6 = `${REGEXP_CHARSET_IPV6}{0,4}`;

// ローカル部
const REGEXP_LOCAL_DOT = `${REGEXP_COMPONENT_DOT}(\\.${REGEXP_COMPONENT_DOT})*`;
const REGEXP_LOCAL_QUOTED = `"${REGEXP_COMPONENT_QUOTED}"`;
const REGEXP_LOCAL = `(${REGEXP_LOCAL_DOT}|${REGEXP_LOCAL_QUOTED})`;

// ドメイン部
const REGEXP_DOMAIN_GENERAL = `(${REGEXP_COMPONENT_SLD}\\.)+${REGEXP_COMPONENT_TLD}`;
const REGEXP_DOMAIN_IPV4 = `${REGEXP_COMPONENT_IPV4}(\\.${REGEXP_COMPONENT_IPV4}){3}`;
const REGEXP_DOMAIN_IPV6_COMMON = `${REGEXP_COMPONENT_IPV6}(:${REGEXP_COMPONENT_IPV6}){1,5}`;
const REGEXP_DOMAIN_IPV6_REST = `(:${REGEXP_DOMAIN_IPV4}|(:${REGEXP_COMPONENT_IPV6}){0,2})`;
const REGEXP_DOMAIN_IPV6 = `IPv6:${REGEXP_DOMAIN_IPV6_COMMON}${REGEXP_DOMAIN_IPV6_REST}`;
const REGEXP_DOMAIN_IP = `\\[(${REGEXP_DOMAIN_IPV4}|${REGEXP_DOMAIN_IPV6})\\]`;
const REGEXP_DOMAIN = `(${REGEXP_DOMAIN_GENERAL}|${REGEXP_DOMAIN_IP})`;

// メアド = ローカル部 + "@" + ドメイン部
const REGEXP_EMAIL = `^${REGEXP_LOCAL}@${REGEXP_DOMAIN}$`;
const PATTERN = new RegExp(REGEXP_EMAIL);

注意点

  • RFCに完全に準拠しているわけではありません。だいたいのパターンでは正常に判定できますが、準拠していないものを通してしまう場合も一部あります。
    • IPv6部分が正常に判定できない場合があります(不正な表記を受理する可能性)。
    • IPv4部分も、255より大きな数値が指定されても受理してしまいます。
    • RFCではローカル部、ドメイン部、全体の長さに制限がありますが、チェックしていません。
  • ドメイン部の一部に、ごく軽いものですが病理的な正規表現を使っています。
    • 正規表現のエンジンやマッチングする文字列によっては、まれにマッチング速度がとても遅くなることがあります。
    • …とはいってもめちゃくちゃヤバいものではなく、意識しないとみなさんも思わずやってしまいがちなレベルです(「病理的な正規表現」という言葉を初めて聞いた方は、もしかすると今までやっていたかも?)

ここ違うんじゃね?とか、こうしたほうがいいんじゃね?みたいな部分があれば指摘おねがいします!

参考

shimataro999
手品業界からWeb業界に来ました。
https://shimataro.me/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした