Perlメモ

更新日 2019/5/3
カウンター

戻る
Perl正規表現雑技
メールアドレスの正規表現

更新履歴

2019/05/03 「はじめに」ブラウザについて削除
2009/03/22 「メールアドレスの正規表現」RFCと不具合について追記
2007/02/24 「自動で URI(URL) のリンクを張る」「メールアドレスの正規表現」メールアドレスの例を修正 /「はじめに」修正
2006/07/21 「正しくパターンマッチさせるindex 関数に関する記述修正
2004/01/09 「URIエスケープ・アンエスケープする」文章とスクリプト追記

目次

トップへ

はじめに

  • このページは Perl5 を対象としています. また,perl を対象としていますので, jperl で動くという保証はありません.
  • perl スクリプトは EUC-JP で書かれることを想定しています.
  • このページは CGIメーリングリスト などでの質疑応答・FAQを参考に,私が独自にメモとしてまとめたものです. ただし,CGI に特化したものではありません.
  • 主に参照させていただいたページは私のページ (雑多なリンク) の 文字PerlWWW にリンクを張ってあります.
  • このページに書かれているスクリプトは, 個人の責任において実行してください.また, 随時不具合の修正をしていますので,ご利用される方はご注意ください.
  • このページに書かれているスクリプトの 利用・改造は自由 です. その際はどこかにこのページの URI( http://www.din.or.jp/~ohzaki/perl.htm )を参考として記述していただければ幸いです(任意).
  • ご意見・ご感想・ご要望などは メール にお願いします.こう書いた方がいい, 動かん,わからん,バグってる,これ書け,などなどお待ちしています.
  • このページへの リンクは自由 に張ってくださって結構です.URI は http://www.din.or.jp/~ohzaki/perl.htm です.
  • 引用または転載する場合は,出典としてこのページの URI( http://www.din.or.jp/~ohzaki/perl.htm )を明記してください. URI を明記する場合に限り許可は必要ありませんが, 事後でかまわないのでお知らせくださればうれしいです. URI を明記しない場合には事前の許可なしに引用または転載することを 禁止 します.
トップへ

排他制御(ファイルロック)をする

sub my_flock {
  my %lfh = (dir => './lockdir/', basename => 'lockfile',
	     timeout => 60, trytime => 10, @_);
  $lfh{path} = $lfh{dir} . $lfh{basename};

  for (my $i = 0; $i < $lfh{trytime}; $i++, sleep 1) {
    return \%lfh if (rename($lfh{path}, $lfh{current} = $lfh{path} . time));
  }
  opendir(LOCKDIR, $lfh{dir});
  my @filelist = readdir(LOCKDIR);
  closedir(LOCKDIR);
  foreach (@filelist) {
    if (/^$lfh{basename}(\d+)/) {
      return \%lfh if (time - $1 > $lfh{timeout} and
	  rename($lfh{dir} . $_, $lfh{current} = $lfh{path} . time));
      last;
    }
  }
  undef;
}

sub my_funlock {
  rename($_[0]->{current}, $_[0]->{path});
}

# ロックする(タイムアウトあり)
$lfh = my_flock() or die 'Busy!';

# アンロックする
my_funlock($lfh);

 1

(一)使  
(二)

 flock   symlink 使 1 使 mkdir 使 rename 使2 flock 使  symlink  mkdir rename 使
symlink  mkdir rename 使  atomic  mkdir 使
rmdir($lockdir) if (time - (stat($lockdir))[9] >60);


 60  symlink  mkdir rename 
プロセスAプロセスBプロセスC
異常と判断異常と判断
ロック解除
ロック
ロック解除


1
  rename 
 lockfile  lockfile987654321   rename 
dir  /  $lfh = my_flock(basename => 'lockfileA'); my_flock() () undef 
# ロックする(タイムアウトなし)
1 while (not defined($lfh = my_flock()));




(一)  
(二)  
(三)  
(四)  
(五)





# ファイル $file の中身を逆順に表示する

$bufsize = 1024;
open(FILE, "< $file");
binmode(FILE);
$size = (-s FILE) / $bufsize;
$pos += $size <=> ($pos = int($size));
while ($pos--) {
  seek(FILE, $bufsize * $pos, 0);  
  read(FILE, $buf, $bufsize);
  $buf .= $buf_tmp;
  ($buf_tmp, @lines) = $buf =~ /[^\x0D\x0A]*\x0D?\x0A?/g;
  pop(@lines);
  foreach (reverse @lines) {
    print $_;
    print "\n" if $_ !~ /[\x0D\x0A]$/;
  }
}
close(FILE);
print $buf_tmp;


 $bufsize 
$size  -s 1 $pos  $bufsize while  $pos
$buf  $bufsize   $buf  $buf =~ /[^\x0D\x0A]*\x0D?\x0A?/g;   0  1  0 \x0D?\x0A?  \x0D\x0A  \x0D  \x0A  1  $buf  pop(@lines); 
$buf  split 使 split(/\x0D\x0A|\x0D|\x0A/, $buf);   $buf  split 3 split  "foo\nbar\n\n\n"  split  ('foo', 'bar')  ('foo', 'bar', '', '')
3 split(/\x0D\x0A|\x0D|\x0A/, $buf, -1); "foo\nbar\n"  split  ('foo', 'bar', '') pop(@lines) if $lines[-1] eq '';  $bufsize  read  $buf_tmp = "\n" if $buf_tmp eq ''; 使 調使




# ファイル $file の最後の最大 $n行だけ表示する

$bufsize = 1024;
open(FILE, "< $file");
binmode(FILE);
$size = (-s FILE) / $bufsize;
$pos += $size <=> ($pos = int($size));
while ($pos--) {
  seek(FILE, $bufsize * $pos, 0);  
  read(FILE, $buf, $bufsize);
  $buf .= $buf_tmp;
  ($buf_tmp, @lines) = $buf =~ /[^\x0D\x0A]*\x0D?\x0A?/g;
  pop(@lines);
  unshift(@tail, @lines);
  last if @tail >= $n;
}
close(FILE);
unshift(@tail, $buf_tmp);
@tail = @tail[-$n .. -1] if @tail > $n;
foreach (@tail) {
  print $_;
}


$n  while 
 @tail 調 $n  $n  .. (-$n, -$n+1,..., -2, -1)   $n 


 1



# ファイル $file から1行ランダムに選択する

srand;
open(FILE, "< $file");
rand($.) < 1 and $line = $_ while <FILE>;
close(FILE);
print $line;



 while  1 while  2 and  and if 使
if (rand($.) <1) {
  $line = $_;
}


 $.  1/$.  1 1/1 2 1/2 3 1/3   1  3 11 23  1/1 * (1 - 1/2) * (1 - 1/3) = 1/3  2  2 3 2   1/2 * (1 - 1/3) = 1/3 


()



# ディレクトリ $dir のサイズ $size を求める

use File::Find;

find(sub {$size += -s if -f}, $dir);
print $size, "bytes\n";


 $dir   File::Find   find 使 2  $_  11 1
# ディレクトリ $dir のサイズ $size を求める(わかりやすく)

use File::Find;

find(\&wanted, $dir);
print $size, "bytes\n";

sub wanted {
  $size += -s $_ if -f $_;
}


-s  1 -f  finddepth 使




$str  EUC-JP   EUC-JP   EUC-JP
# $str の中のタグを削除した $result を作る
# $tag_regex と $tag_regex_ は別途参照

$text_regex = q{[^<]*};

$result = '';
while ($str =~ /($text_regex)($tag_regex)?/gso) {
  last if $1 eq '' and $2 eq '';
  $result .= $1;
  $tag_tmp = $2;
  if ($tag_tmp =~ /^<(XMP|PLAINTEXT|SCRIPT)(?![0-9A-Za-z])/i) {
    $str =~ /(.*?)(?:<\/$1(?![0-9A-Za-z])$tag_regex_|$)/gsi;
    ($text_tmp = $1) =~ s/</&lt;/g;
    $text_tmp =~ s/>/&gt;/g;
    $result .= $text_tmp;
  }
}


 URI(URL)  $tag_regex  $tag_regex_  HTML 使 $str  HTML XMP PLAINTEXT XMP PLAINTEXT  <  &lt;  >  &gt; SCRIPT
 <  >
# $str の中のタグを削除した $result を作る(不完全)

($result = $str) =~ s/<[^>]*>//g;




<!-- <FOO> -->   <!-- <FOO>    
<FOO BAR=">">   >    <FOO BAR=">   
<XMP><FOO></XMP>   XMP PLAINTEXT  SCRIPT  <FOO> 

HTML <  > 
 BR A  $tag_tmp = $2;   $tag_tmp  $result 
  $result .= $tag_tmp if $tag_tmp =~ /^<\/?(BR|A)(?![0-9A-Za-z])/i;


 FONT IMG  $tag_tmp = $2;   $tag_tmp  $result 
  $result .= $tag_tmp if $tag_tmp !~ /^<\/?(FONT|IMG)(?![0-9A-Za-z])/i;


 HTML::TokeParser  get_text  get_trimmed_text striphtml 使


 URI(URL) 


$strEUC-JP という前提ですので, 必要ならばあらかじめ EUC-JP に変換しておいてください. 漢字コードの変換に関しては 「漢字コードを EUC-JP に変換して処理する」を参照.

# $str の中の URI(URL) にリンクを張った $result を作る
# $tag_regex と $tag_regex_ は別途参照
# $http_URL_regex と $ftp_URL_regex および $mail_regex は別途参照

$text_regex = q{[^<]*};

$result = '';  $skip = 0;
while ($str =~ /($text_regex)($tag_regex)?/gso) {
  last if $1 eq '' and $2 eq '';
  $text_tmp = $1;
  $tag_tmp = $2;
  if ($skip) {
    $result .= $text_tmp . $tag_tmp;
    $skip = 0 if $tag_tmp =~ /^<\/[aA](?![0-9A-Za-z])/;
  } else {
    $text_tmp =~ s{($http_URL_regex|$ftp_URL_regex|($mail_regex))}
      {my($org, $mail) = ($1, $2);
       (my $tmp = $org) =~ s/"/&quot;/g;
       '<A HREF="' . ($mail ne '' ? 'mailto:' : '') . "$tmp\">$org</A>"}ego;
    $result .= $text_tmp . $tag_tmp;
    $skip = 1 if $tag_tmp =~ /^<[aA](?![0-9A-Za-z])/;
    if ($tag_tmp =~ /^<(XMP|PLAINTEXT|SCRIPT)(?![0-9A-Za-z])/i) {
      $str =~ /(.*?(?:<\/$1(?![0-9A-Za-z])$tag_regex_|$))/gsi;
      $result .= $1;
    }
  }
}

$http_URL_regex  http URL  $ftp_URL_regex  ftp URL  $mail_regex 使 $tag_regex  $tag_regex_  HTML 使 $str  HTML  http URL  ftp URL

()  
A  
XMPPLAINTEXT  SCRIPT

 $str  1 while    $skip  A1 A  $skip  0  A http URL  ftp URL 
 XMP PLAINTEXT while  http URL  ftp URL SCRIPT
$str  2  g  g  1 while XMP PLAINTEXTSCRIPT  while   $str   g
2
    $text_tmp =~ s/($http_URL_regex)/<A HREF="$1">$1<\/A>/go;
    $text_tmp =~ s/($ftp_URL_regex)/<A HREF="$1">$1<\/A>/go;
    $text_tmp =~ s/($mail_regex)/<A HREF="mailto:$1">$1<\/A>/go;


1  &quot; 
2 http URL ftp URL
http://www.din.or.jp/~ohzaki/?ftp://ftp.din.or.jp/+foobar@example.com
ftp://ftp.din.or.jp/foobar@example.com
"http://www.din.or.jp/~ohzaki/?ftp://ftp.din.or.jp/"@example.com


 http URLftp URL http URL http URL  ftp URL  11




# 半角スペース
$space = '\x20';

# 全角スペース
$Zspace = '(?:\xA1\xA1)'; # EUC-JP
$Zspace_sjis = '(?:\x81\x40)'; # SJIS

# 全角数字 [0-9]
$Zdigit = '(?:\xA3[\xB0-\xB9])'; # EUC-JP
$Zdigit_sjis = '(?:\x82[\x4F-\x58])'; # SJIS

# 全角大文字 [A-Z]
$Zuletter = '(?:\xA3[\xC1-\xDA])'; # EUC-JP
$Zuletter_sjis = '(?:\x82[\x60-\x79])'; # SJIS

# 全角小文字 [a-z]
$Zlletter = '(?:\xA3[\xE1-\xFA])'; # EUC-JP
$Zlletter_sjis = '(?:\x82[\x81-\x9A])'; # SJIS

# 全角アルファベット [A-Za-z]
$Zalphabet = '(?:\xA3[\xC1-\xDA\xE1-\xFA])'; # EUC-JP
$Zalphabet_sjis = '(?:\x82[\x60-\x79\x81-\x9A])'; # SJIS

# 全角ひらがな [ぁ-ん]
$Zhiragana = '(?:\xA4[\xA1-\xF3])'; # EUC-JP
$Zhiragana_sjis = '(?:\x82[\x9F-\xF1])'; # SJIS

# 全角ひらがな(拡張) [ぁ-ん゛゜ゝゞ]
$ZhiraganaExt = '(?:\xA4[\xA1-\xF3]|\xA1[\xAB\xAC\xB5\xB6])'; # EUC-JP
$ZhiraganaExt_sjis = '(?:\x82[\x9F-\xF1]|\x81[\x4A\x4B\x54\x55])'; # SJIS

# 全角カタカナ [ァ-ヶ]
$Zkatakana = '(?:\xA5[\xA1-\xF6])'; # EUC-JP
$Zkatakana_sjis = '(?:\x83[\x40-\x96])'; # SJIS

# 全角カタカナ(拡張) [ァ-ヶ・ーヽヾ]
$ZkatakanaExt = '(?:\xA5[\xA1-\xF6]|\xA1[\xA6\xBC\xB3\xB4])'; # EUC-JP
$ZkatakanaExt_sjis = '(?:\x83[\x40-\x96]|\x81[\x45\x5B\x52\x53])'; # SJIS

# 半角カタカナ [ヲ-゜]
$Hkatakana = '(?:\x8E[\xA6-\xDF])'; # EUC-JP
$Hkatakana_sjis = '[\xA6-\xDF]'; # SJIS

# EUC-JP文字
$ascii = '[\x00-\x7F]'; # 1バイト EUC-JP文字
$twoBytes = '(?:[\x8E\xA1-\xFE][\xA1-\xFE])'; # 2バイト EUC-JP文字
$threeBytes = '(?:\x8F[\xA1-\xFE][\xA1-\xFE])'; # 3バイト EUC-JP文字
$character = "(?:$ascii|$twoBytes|$threeBytes)"; # EUC-JP文字

# EUC-JP文字(機種依存文字・未定義領域・3バイト文字を含まない)
$character_strict = '(?:[\x00-\x7F]|' # ASCII
  . '\x8E[\xA1-\xDF]|' # 半角カタカナ
  . '[\xA1\xB0-\xCE\xD0-\xF3][\xA1-\xFE]|' # 1,16-46,48-83区
  . '\xA2[\xA1-\xAE\xBA-\xC1\xCA-\xD0\xDC-\xEA\xF2-\xF9\xFE]|' # 2区
  . '\xA3[\xB0-\xB9\xC1-\xDA\xE1-\xFA]|' # 3区
  . '\xA4[\xA1-\xF3]|' # 4区
  . '\xA5[\xA1-\xF6]|' # 5区
  . '\xA6[\xA1-\xB8\xC1-\xD8]|' # 6区
  . '\xA7[\xA1-\xC1\xD1-\xF1]|' # 7区
  . '\xA8[\xA1-\xC0]|' # 8区
  . '\xCF[\xA1-\xD3]|' # 47区
  . '\xF4[\xA1-\xA6])'; # 84区

# EUC-JP未定義文字(機種依存文字・3バイト文字を含む)
$character_undef = '(?:[\xA9-\xAF\xF5-\xFE][\xA1-\xFE]|' # 9-15,85-94区
  . '\x8E[\xE0-\xFE]|' # 半角カタカナ
  . '\xA2[\xAF-\xB9\xC2-\xC9\xD1-\xDB\xEB-\xF1\xFA-\xFD]|' # 2区
  . '\xA3[\XA1-\xAF\xBA-\xC0\xDB-\xE0\xFB-\xFE]|' # 3区
  . '\xA4[\xF4-\xFE]|' # 4区
  . '\xA5[\xF7-\xFE]|' # 5区
  . '\xA6[\xB9-\xC0\xD9-\xFE]|' # 6区
  . '\xA7[\xC2-\xD0\xF2-\xFE]|' # 7区
  . '\xA8[\xC1-\xFE]|' # 8区
  . '\xCF[\xD4-\xFE]|' # 47区
  . '\xF4[\xA7-\xFE]|' # 84区
  . '\x8F[\xA1-\xFE][\xA1-\xFE])'; # 3バイト文字

# SJIS文字
$oneByte_sjis = '[\x00-\x7F\xA1-\xDF]'; # 1バイト SJIS文字
$twoBytes_sjis =
  '(?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC])'; # 2バイト SJIS文字
$character_sjis = "(?:$oneByte_sjis|$twoBytes_sjis)"; # SJIS文字

# SJIS文字(機種依存文字・未定義領域を含まない)
$character_sjis_strict = '(?:[\x00-\x7F\xA1-\xDF]|' # ASCII,半角カタカナ
  . '[\x89-\x97\x99-\x9F\xE0-\xE9][\x40-\x7E\x80-\xFC]|' # 17-46,49-82区
  . '\x81[\x40-\x7E\x80-\xAC\xB8-\xBF\xC8-\xCE\xDA-\xE8\xF0-\xF7\xFC]|' # 1,2区
  . '\x82[\x4F-\x58\x60-\x79\x81-\x9A\x9F-\xF1]|' # 3,4区
  . '\x83[\x40-\x7E\x80-\x96\x9F-\xB6\xBF-\xD6]|' # 5,6区
  . '\x84[\x40-\x60\x70-\x7E\x80-\x91\x9F-\xBE]|' # 7,8区
  . '\x88[\x9F-\xFC]|' # 15,16区
  . '\x98[\x40-\x72\x9F-\xFC]|' # 47,48区
  . '\xEA[\x40-\x7E\x80-\xA4])'; # 83,84区

# SJIS未定義文字(機種依存文字を含む)
$character_sjis_undef =
  '(?:[\x85-\x87\xEB-\xFC][\x40-\x7E\x80-\xFC]|' # 9-14,85-120区
  . '\x81[\xAD-\xB7\xC0-\xC7\xCF-\xD9\xE9-\xEF\xF8-\xFB]|' # 1,2区
  . '\x82[\x40-\x4E\x59-\x5F\x7A-\x7E\x80\x9B-\x9E\xF2-\xFC]|' # 3,4区
  . '\x83[\x97-\x9E\xB7-\xBE\xD7-\xFC]|' # 5,6区
  . '\x84[\x61-\x6F\x92-\x9E\xBF-\xFC]|' # 7,8区
  . '\x88[\x40-\x7E\x80-\x9E]|' # 15,16区
  . '\x98[\x73-\x7E\x80-\x9E]|' # 47,48区
  . '\xEA[\xA5-\xFC])'; # 83,84区

# ドコモ絵文字
$iPictograph_base = '(?:\xF8[\x9F-\xFC]|' # 基本絵文字(SJIS)
  . '\xF9[\x40-\x49\x50-\x52\x55-\x57\x5B-\x5E\x72-\x7E\x80-\xB0])';
$iPictograph_ext = '(?:\xF9[\xB1-\xFC])'; # 拡張絵文字(SJIS)
$iPictograph =
  '(?:$iPictograph_base|$iPictograph_ext)'; # iモード対応 絵文字(SJIS)



()

 EUC   
 SJIS 



HTML



# HTMLタグの正規表現 $tag_regex

$tag_regex_ = q{[^"'<>]*(?:"[^"]*"[^"'<>]*|'[^']*'[^"'<>]*)*(?:>|(?=<)|$(?!\n))}; #'}}}}
$comment_tag_regex =
    '<!(?:--[^-]*-(?:[^-]+-)*?-(?:[^>-]*(?:-[^>-]+)*?)??)*(?:>|$(?!\n)|--.*$)';
$tag_regex = qq{$comment_tag_regex|<$tag_regex_};


 $comment_tag_regex $tag_regex_   < 
 [^>]*  > 
"[^"]*"   >  [^>]  (?:[^>]|"[^"]*"|'[^']')* [^>]  使  > 
(?:"[^"]*"|'[^']*'|[^>])* 調 1  [^>]  [^"'>] 
$tag_regex_ = q{(?:[^"'>]|"[^"]*"|'[^']*')*}; #'}}}


<P<B>  >  <P [^>]*  [^<>]*   >  (?:>|(?=<)|$(?!\n))   >   <  $(?!\n)
$tag_regex_ = q{(?:[^"'<>]|"[^"]*"|'[^']*')*(?:>|(?=<)|$(?!\n))}; #'}}}


 Jeffrey E. F. Friedl    1.5
  SGML
 ----  0 <!  <!   > 
# 正常なコメントタグの正規表現 $comment_tag_regex

$comment_tag_regex = q{<!(?:--(?:(?!--).)*--\s*)*>};



 (?:>|$(?!\n)|--.*$)   >   -- $(?!\n)  $  $(?!\n)  $  $str = "test\n";  m/^test$/ m/^test$(?!\n)/  $  'test'  "test\n" $  "<!\n" perl5.005  $(?!\n)  \z \z  $  \Z

# コメントタグの正規表現(遅い)

$comment_tag_regex = '<!(?:--(?:(?!--).)*--(?:(?!--)[^>])*)*(?:>|$(?!\n)|--.*$)';


(?:(?!--).)*  --  1 -  --  -  --  1 -- 
--  1 (?:(?!--).)*  -- - -  - (?:(?!--).)*  (?:[^-]|-[^-])*  [^-]*(?:-[^-][^-]*)*  [^-]*(?:-[^-]+)* 
--[^-]*(?:-[^-]+)*--   2  -  --  1  2 (?:-[^-]+)*  (?:  - (?:-[^-]+)*  - [^-]*(?:-[^-]+)*--  [^-]*-(?:[^-]+-)*-  -  [^-]*  -  1
1 (?:[^-]+-)*  (?:[^-]+-)*? *  *?  *  *?   *  *?  <--  -->   -  -   (?:[^-]+-)  (?:[^-]+-)  *  *? 
 (?:(?!--)[^>])*  (?:[^>-]*(?:-[^>-]+)*  *  *?  (?:[^>-]*(?:-[^>-]+)*?
 (?: regex)??  ?? --  >  (?:[^>-]*(?:-[^>-]+)*? ?? 


URI(URL) 



# $uri が正しい URI か判定する

$digit = q{[0-9]};
$upalpha = q{[A-Z]};
$lowalpha = q{[a-z]};
$alpha = qq{(?:$lowalpha|$upalpha)};
$alphanum = qq{(?:$alpha|$digit)};
$hex = qq{(?:$digit|[A-Fa-f])};
$escaped = qq{%$hex$hex};
$mark = q{[-_.!~*'()]};
$unreserved = qq{(?:$alphanum|$mark)};
$reserved = q{[;/?:@&=+$,]};
$uric = qq{(?:$reserved|$unreserved|$escaped)};
$fragment = qq{$uric*};
$query = qq{$uric*};
$pchar = qq{(?:$unreserved|$escaped|} . q{[:@&=+$,])};
$param = qq{$pchar*};
$segment = qq{$pchar*(?:;$param)*};
$path_segments = qq{$segment(?:/$segment)*};
$abs_path = qq{/$path_segments};
$uric_no_slash = qq{(?:$unreserved|$escaped|} . q{[;?:@&=+$,])};
$opaque_part = qq{$uric_no_slash$uric*};
$path = qq{(?:$abs_path|$opaque_part)?};
$port = qq{$digit*};
$IPv4address = qq{$digit+\\.$digit+\\.$digit+\\.$digit+};
$toplabel = qq{(?:$alpha|$alpha(?:$alphanum|-)*$alphanum)};
$domainlabel = qq{(?:$alphanum|$alphanum(?:$alphanum|-)*$alphanum)};
$hostname = qq{(?:$domainlabel\\.)*$toplabel\\.?};
$host = qq{(?:$hostname|$IPv4address)};
$hostport = qq{$host(?::$port)?};
$userinfo = qq{(?:$unreserved|$escaped|} . q{[;:&=+$,])*};
$server = qq{(?:(?:$userinfo\@)?$hostport)?};
$reg_name = qq{(?:$unreserved|$escaped|} . q{[$,;:@&=+])+};
$authority = qq{(?:$server|$reg_name)};
$scheme = qq{$alpha(?:$alpha|$digit|[-+.])*};
$rel_segment = qq{(?:$unreserved|$escaped|} . q{[;@&=+$,])+};
$rel_path = qq{$rel_segment(?:$abs_path)?};
$net_path = qq{//$authority(?:$abs_path)?};
$hier_part = qq{(?:$net_path|$abs_path)(?:\\?$query)?};
$relativeURI = qq{(?:$net_path|$abs_path|$rel_path)(?:\\?$query)?};
$absoluteURI = qq{$scheme:(?:$hier_part|$opaque_part)};
$URI_reference = qq{(?:$absoluteURI|$relativeURI)?(?:#$fragment)?};

$pattern = $URI_reference;

print "ok\n" if $uri =~ /^$pattern$/o;


URI  RFC 2396(  )  URI References 
(?:(?:[a-z]|[A-Z])(?:(?:[a-z]|[A-Z])|[0-9]|[-+.])*:(?:(?://(?:(?:(
?:(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])
(?:[0-9]|[A-Fa-f])|[;:&=+$,])*@)?(?:(?:(?:(?:(?:[a-z]|[A-Z])|[0-9]
)|(?:(?:[a-z]|[A-Z])|[0-9])(?:(?:(?:[a-z]|[A-Z])|[0-9])|-)*(?:(?:[
a-z]|[A-Z])|[0-9]))\.)*(?:(?:[a-z]|[A-Z])|(?:[a-z]|[A-Z])(?:(?:(?:
[a-z]|[A-Z])|[0-9])|-)*(?:(?:[a-z]|[A-Z])|[0-9]))\.?|[0-9]+\.[0-9]
+\.[0-9]+\.[0-9]+)(?::[0-9]*)?)?|(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[
-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[$,;:@&=+])+)(?:
/(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(
?:[0-9]|[A-Fa-f])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[
-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*)*(?:
/(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(
?:[0-9]|[A-Fa-f])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[
-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*)*)*)
?|/(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f]
)(?:[0-9]|[A-Fa-f])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])
|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*)*(
?:/(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f]
)(?:[0-9]|[A-Fa-f])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])
|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*)*)
*)(?:\?(?:[;/?:@&=+$,]|(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%
(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f]))*)?|(?:(?:(?:(?:[a-z]|[A-Z])|
[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[;?:@&=+
$,])(?:[;/?:@&=+$,]|(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:
[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f]))*)|(?://(?:(?:(?:(?:(?:(?:(?:[a-
z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f]
)|[;:&=+$,])*@)?(?:(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|(?:(?:[a-z]|[A-
Z])|[0-9])(?:(?:(?:[a-z]|[A-Z])|[0-9])|-)*(?:(?:[a-z]|[A-Z])|[0-9]
))\.)*(?:(?:[a-z]|[A-Z])|(?:[a-z]|[A-Z])(?:(?:(?:[a-z]|[A-Z])|[0-9
])|-)*(?:(?:[a-z]|[A-Z])|[0-9]))\.?|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
)(?::[0-9]*)?)?|(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[
0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[$,;:@&=+])+)(?:/(?:(?:(?:(?:[a-z
]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])
|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[
0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*)*(?:/(?:(?:(?:(?:[a-z
]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])
|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[
0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*)*)*)?|/(?:(?:(?:(?:[a
-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f
])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?
:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*)*(?:/(?:(?:(?:(?:[a
-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f
])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?
:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*)*)*|(?:(?:(?:(?:[a-
z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f]
)|[;@&=+$,])+(?:/(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:
[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]
|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|
[:@&=+$,])*)*(?:/(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:
[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]
|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|
[:@&=+$,])*)*)*)?)(?:\?(?:[;/?:@&=+$,]|(?:(?:(?:[a-z]|[A-Z])|[0-9]
)|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f]))*)?)?(?:#(?:[
;/?:@&=+$,]|(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A
-Fa-f])(?:[0-9]|[A-Fa-f]))*)?


RFC 2396  URI  使


http URL 



# $http が正しい http URL か判定する

$digit = q{[0-9]};
$upalpha = q{[A-Z]};
$lowalpha = q{[a-z]};
$alpha = qq{(?:$lowalpha|$upalpha)};
$alphanum = qq{(?:$alpha|$digit)};
$hex = qq{(?:$digit|[A-Fa-f])};
$escaped = qq{%$hex$hex};
$mark = q{[-_.!~*'()]};
$unreserved = qq{(?:$alphanum|$mark)};
$reserved = q{[;/?:@&=+$,]};
$uric = qq{(?:$reserved|$unreserved|$escaped)};
$query = qq{$uric*};
$pchar = qq{(?:$unreserved|$escaped|} . q{[:@&=+$,])};
$param = qq{$pchar*};
$segment = qq{$pchar*(?:;$param)*};
$path_segments = qq{$segment(?:/$segment)*};
$abs_path = qq{/$path_segments};
$port = qq{$digit*};
$IPv4address = qq{$digit+\\.$digit+\\.$digit+\\.$digit+};
$toplabel = qq{(?:$alpha|$alpha(?:$alphanum|-)*$alphanum)};
$domainlabel = qq{(?:$alphanum|$alphanum(?:$alphanum|-)*$alphanum)};
$hostname = qq{(?:$domainlabel\\.)*$toplabel\\.?};
$host = qq{(?:$hostname|$IPv4address)};
$http_URL = qq{http://$host(?::$port)?(?:$abs_path(?:\\?$query)?)?};

$pattern = $http_URL;

print "ok\n" if $http =~ /^$pattern$/;


http URL  RFC 2616  3.2.2 http URL  URI(URL)  URI(URL)   http URL  http URL
http://(?:(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|(?:(?:[a-z]|[A-Z])|[0-9]
)(?:(?:(?:[a-z]|[A-Z])|[0-9])|-)*(?:(?:[a-z]|[A-Z])|[0-9]))\.)*(?:
(?:[a-z]|[A-Z])|(?:[a-z]|[A-Z])(?:(?:(?:[a-z]|[A-Z])|[0-9])|-)*(?:
(?:[a-z]|[A-Z])|[0-9]))\.?|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(?::[0-9
]*)?(?:/(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-
Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[
0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,]
)*)*(?:/(?:(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()])|%(?:[0-9]|[A-
Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,])*(?:;(?:(?:(?:(?:[a-z]|[A-Z])|[
0-9])|[-_.!~*'()])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f])|[:@&=+$,]
)*)*)*(?:\?(?:[;/?:@&=+$,]|(?:(?:(?:[a-z]|[A-Z])|[0-9])|[-_.!~*'()
])|%(?:[0-9]|[A-Fa-f])(?:[0-9]|[A-Fa-f]))*)?)?


 http URL 
# $http が正しい http URL か判定する(文字クラス改良版)

$alpha = q{[a-zA-Z]};
$alphanum = q{[a-zA-Z0-9]};
$hex = q{[0-9A-Fa-f]};
$uric = q{(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]} . qq{|$escaped)};
$pchar = q{(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]} . qq{|$escaped)};
$toplabel = qq{(?:$alpha|$alpha} . q{[-a-zA-Z0-9]*} . qq{$alphanum)};
$domainlabel = qq{(?:$alphanum|$alphanum} . q{[-a-zA-Z0-9]*} . qq{$alphanum)};


 http URL 
http://(?:(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][-a-zA-Z0-9]*[a-zA-Z0-9])\.
)*(?:[a-zA-Z]|[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9])\.?|[0-9]+\.[0-9]+\
.[0-9]+\.[0-9]+)(?::[0-9]*)?(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0
-9A-Fa-f][0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa
-f][0-9A-Fa-f])*)*(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][
0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-F
a-f])*)*)*(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A
-Fa-f])*)?)?


RFC 2616  3.2.2 http URL RFC 2616  HTTP3.2.2 http URL  http URL HTTP HTML 使 HTTP使 http URL  scheme  http URI References 
http://user:passwd@www.din.or.jp/~ohzaki/perl.htm#URI  URI References user:passwd@ userinfo #URI  Fragment Identifier  HTTP使 http URL HTML () HTTP RFC 2396(  ) 4 Fragment Identifier  URI Fragment Identifier  user agent 
scheme  http  URI References  URI(URL)  URI(URL)  HTTP使 http URL hostport abs_pathquery scheme  http  Secure Hyper Text Tranasfer Protocol(S-HTTP) 使 shttp:  Secure Sockets Layer(SSL) 使 https:
$server = qq{(?:$userinfo\@)?$hostport};
$authority = qq{$server};
$scheme = q{(?:https?|shttp)};
$hier_part = qq{$net_path(?:\\?$query)?};
$absoluteURI = qq{$scheme:$hier_part};
$URI_reference = qq{$absoluteURI(?:#$fragment)?};



$alpha = q{[a-zA-Z]};
$alphanum = q{[a-zA-Z0-9]};
$hex = q{[0-9A-Fa-f]};
$unreserved = q{[-_.!~*'()a-zA-Z0-9]};
$uric = q{(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]} . qq{|$escaped)};
$pchar = q{(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]} . qq{|$escaped)};
$toplabel = qq{(?:$alpha|$alpha} . q{[-a-zA-Z0-9]*} . qq{$alphanum)};
$domainlabel = qq{(?:$alphanum|$alphanum} . q{[-a-zA-Z0-9]*} . qq{$alphanum)};
$userinfo = q{(?:[-_.!~*'()a-zA-Z0-9;:&=+$,]|} . qq{$escaped)*};



(?:https?|shttp)://(?:(?:[-_.!~*'()a-zA-Z0-9;:&=+$,]|%[0-9A-Fa-f][
0-9A-Fa-f])*@)?(?:(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][-a-zA-Z0-9]*[a-zA-
Z0-9])\.)*(?:[a-zA-Z]|[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9])\.?|[0-9]+\
.[0-9]+\.[0-9]+\.[0-9]+)(?::[0-9]*)?(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=
+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%
[0-9A-Fa-f][0-9A-Fa-f])*)*(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9
A-Fa-f][0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f
][0-9A-Fa-f])*)*)*)?(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-
Fa-f][0-9A-Fa-f])*)?(?:#(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-F
a-f][0-9A-Fa-f])*)?


使 $http  scheme  http  URI References  http URL 使
# $str から http URI References を抽出する

$str = "このページの URI は http://www.din.or.jp/~ohzaki/perl.htm です";

$pattern = $URI_reference;

while ($str =~ /($pattern)/g) {
  print $1, "\n";
}

実行結果(失敗例)
http://www.din.or.j


 Perl  NFAs(Nondeterministic Finite Automata) 
print "数字1文字or数字で始まり数字か小文字が続くもの\n";
$str = '123abc';
@patterns = ('(?:\d|\d[0-9a-z]+)', '(?:\d[0-9a-z]*)');
foreach $pattern (@patterns) {
  print "  文字列 $str  パターン $pattern  ";
  print '結果 ' . join('/', $str =~ /$pattern/g) . "\n";
}
print "\n数字1文字or最初が数字か小文字で,次が小文字のもの\n";
$str = '1a';
@patterns = ('(?:\d|[\da-z][a-z])', '(?:[\da-z][a-z]|\d)');
foreach $pattern (@patterns) {
  print "  文字列 $str  パターン $pattern  ";
  print '結果 ' . join('/', $str =~ /$pattern/g) . "\n";
}

実行結果
数字1文字or最初が数字で,その後数字か小文字が続くもの
  文字列 123abc  パターン (?:\d|\d[0-9a-z]+)  結果 1/2/3
  文字列 123abc  パターン (?:\d[0-9a-z]*)  結果 123abc

数字1文字or最初が数字か小文字で,次が小文字のもの
  文字列 1a  パターン (?:\d|[\da-z][a-z])  結果1文字列 1a  パターン (?:[\da-z][a-z]|\d)  結果 1a


2  Perl  
1 (?:regex1|regex1regex2+) regex1regex2* 
$toplabel = qq{$alpha(?:} . q{[-a-zA-Z0-9]*} . qq{$alphanum)?};
$domainlabel = qq{$alphanum(?:} . q{[-a-zA-Z0-9]*} . qq{$alphanum)?};


2 (?:regex1|regex2) regex1  regex2  regex2  regex1  (?:regex2|regex1) host  hostname  IPv4address IPv4address  hostname  127.0.0.1.www.din.or.jp  host  IPv4address  127.0.0.1   host  hostname
pseudohttp://foo/bar.htm  HTTP  scheme 
$http_URL_regex = q{\b} . $URI_reference;



# http URL の正規表現 $http_URL_regex

$digit = q{[0-9]};
$alpha = q{[a-zA-Z]};
$alphanum = q{[a-zA-Z0-9]};
$hex = q{[0-9A-Fa-f]};
$escaped = qq{%$hex$hex};
$uric = q{(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]} . qq{|$escaped)};
$fragment = qq{$uric*};
$query = qq{$uric*};
$pchar = q{(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]} . qq{|$escaped)};
$param = qq{$pchar*};
$segment = qq{$pchar*(?:;$param)*};
$path_segments = qq{$segment(?:/$segment)*};
$abs_path = qq{/$path_segments};
$port = qq{$digit*};
$IPv4address = qq{$digit+\\.$digit+\\.$digit+\\.$digit+};
$toplabel = qq{$alpha(?:} . q{[-a-zA-Z0-9]*} . qq{$alphanum)?};
$domainlabel = qq{$alphanum(?:} . q{[-a-zA-Z0-9]*} . qq{$alphanum)?};
$hostname = qq{(?:$domainlabel\\.)*$toplabel\\.?};
$host = qq{(?:$hostname|$IPv4address)};
$hostport = qq{$host(?::$port)?};
$userinfo = q{(?:[-_.!~*'()a-zA-Z0-9;:&=+$,]|} . qq{$escaped)*};
$server = qq{(?:$userinfo\@)?$hostport};
$authority = qq{$server};
$scheme = q{(?:https?|shttp)};
$net_path = qq{//$authority(?:$abs_path)?};
$hier_part = qq{$net_path(?:\\?$query)?};
$absoluteURI = qq{$scheme:$hier_part};
$URI_reference = qq{$absoluteURI(?:#$fragment)?};
$http_URL_regex = q{\b} . $URI_reference;

このスクリプトから求めた http URL の正規表現は次のように なりました.

\b(?:https?|shttp)://(?:(?:[-_.!~*'()a-zA-Z0-9;:&=+$,]|%[0-9A-Fa-f
][0-9A-Fa-f])*@)?(?:(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.)
*[a-zA-Z](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.?|[0-9]+\.[0-9]+\.[0-9]+\.
[0-9]+)(?::[0-9]*)?(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f]
[0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-
Fa-f])*)*(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f
])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)*)
*)?(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])
*)?(?:#(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*
)?

この正規表現を使えば,http URL の抽出がうまくいくように なります.以下がこれを直接代入して使うスクリプトになります.

$http_URL_regex =
q{\b(?:https?|shttp)://(?:(?:[-_.!~*'()a-zA-Z0-9;:&=+$,]|%[0-9A-Fa-f} .
q{][0-9A-Fa-f])*@)?(?:(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.)} .
q{*[a-zA-Z](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.?|[0-9]+\.[0-9]+\.[0-9]+\.} .
q{[0-9]+)(?::[0-9]*)?(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f]} .
q{[0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-} .
q{Fa-f])*)*(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f} .
q{])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)*)} .
q{*)?(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])} .
q{*)?(?:#(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*} .
q{)?};

さて,ここまで長々と書いてきましたが,正確に正規表現を書くことを あきらめて,もっと簡単でいいやという人のための http URL の正規表現が以下になります.

s?https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+

使
# 文書 $text から http URL を抽出して @http に格納する

@http = $text =~ /s?https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+/g;


/  \/ $  @ 2 \$  \@  2 \  $  $,  @  @&


ftp URL 



# ftp URL の正規表現 $ftp_URL_regex

$digit = q{[0-9]};
$alpha = q{[a-zA-Z]};
$alphanum = q{[a-zA-Z0-9]};
$hex = q{[0-9A-Fa-f]};
$escaped = qq{%$hex$hex};
$uric = q{(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]} . qq{|$escaped)};
$fragment = qq{$uric*};
$query = qq{$uric*};
$pchar = q{(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]} . qq{|$escaped)};
$segment = qq{$pchar*};
$ftptype = q{[AIDaid]};
$path_segments = qq{$segment(?:/$segment)*(?:;type=$ftptype)?};
$abs_path = qq{/$path_segments};
$port = qq{$digit*};
$IPv4address = qq{$digit+\\.$digit+\\.$digit+\\.$digit+};
$toplabel = qq{$alpha(?:} . q{[-a-zA-Z0-9]*} . qq{$alphanum)?};
$domainlabel = qq{$alphanum(?:} . q{[-a-zA-Z0-9]*} . qq{$alphanum)?};
$hostname = qq{(?:$domainlabel\\.)*$toplabel\\.?};
$host = qq{(?:$hostname|$IPv4address)};
$hostport = qq{$host(?::$port)?};
$user = q{(?:[-_.!~*'()a-zA-Z0-9;&=+$,]|} . qq{$escaped)*};
$password = $user;
$userinfo = qq{$user(?::$password)?};
$server = qq{(?:$userinfo\@)?$hostport};
$authority = qq{$server};
$scheme = q{ftp};
$net_path = qq{//$authority(?:$abs_path)?};
$hier_part = qq{$net_path(?:\\?$query)?};
$absoluteURI = qq{$scheme:$hier_part};
$URI_reference = qq{$absoluteURI(?:#$fragment)?};
$ftp_URL_regex = q{\b} . $URI_reference;


ftp URL  RFC 1738  RFC 1738  RFC 2396(  ) RFC 2396  URI  ftp URL  ftp URLRFC 2396  URI  http URL  http  URI References   ftp  URI References 
RFC 1738  ftp URL
$segment = qq{$pchar*};
$ftptype = q{[AIDaid]};
$path_segments = qq{$segment(?:/$segment)*(?:;type=$ftptype)?};
$user = q{(?:[-_.!~*'()a-zA-Z0-9;&=+$,]|} . qq{$escaped)*};
$password = $user;
$userinfo = qq{$user(?::$password)?};
$server = qq{(?:$userinfo\@)?$hostport};
$authority = qq{$server};
$scheme = q{ftp};
$net_path = qq{//$authority(?:$abs_path)?};
$hier_part = qq{$net_path(?:\\?$query)?};
$absoluteURI = qq{$scheme:$hier_part};
$URI_reference = qq{$absoluteURI(?:#$fragment)?};
$ftp_URL_regex = q{\b} . $URI_reference;


ftp URL  RFC 1738  ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ]] login  path_segments  ;  fpath 使segment  ;  param path_segments  ftp URL  login  login = [ user [ ":" password ] "@" ] hostport  userinfo  user [ ":" password ] :  user  password 使userinfo  :  userpassword  userinfo  scheme  ftp  ftp  URI References  URI_reference  absoluteURI
 ftp URL   
\bftp://(?:(?:[-_.!~*'()a-zA-Z0-9;&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*
(?::(?:[-_.!~*'()a-zA-Z0-9;&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)?@)?(?
:(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.)*[a-zA-Z](?:[-a-zA-
Z0-9]*[a-zA-Z0-9])?\.?|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(?::[0-9]*)?
(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*(?:/(?
:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)*(?:;type=[
AIDaid])?)?(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9
A-Fa-f])*)?(?:#(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A
-Fa-f])*)?


使
$ftp_URL_regex =
q{\bftp://(?:(?:[-_.!~*'()a-zA-Z0-9;&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*} .
q{(?::(?:[-_.!~*'()a-zA-Z0-9;&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)?@)?(?} .
q{:(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.)*[a-zA-Z](?:[-a-zA-} .
q{Z0-9]*[a-zA-Z0-9])?\.?|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(?::[0-9]*)?} .
q{(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*(?:/(?} .
q{:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)*(?:;type=[} .
q{AIDaid])?)?(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9} .
q{A-Fa-f])*)?(?:#(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A} .
q{-Fa-f])*)?};








 





RFC 2821  RFC 2822  RFC 5321  RFC 5322  obsolete 
RFC 821  RFC 822  RFC 2821(  13 4,5 6 ) RFC 2822(  ) obsolete 
RFC 821(  ) RFC 822(  )perl5.6.0 perl Jeffrey E. F. Friedl   Jeffrey E. F. Friedl  6,598 https://resources.oreilly.com/examples/9781565922570/blob/master/email-opt.pl
[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x
80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\
\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000
-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\
\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\04
0\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\
xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80
-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\0
15()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\x
ff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]
*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:"
.\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xf
f][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(
?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\
\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[
\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x8
0-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\
x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\03
7\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\
\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\x
ff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\
x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\
040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-
\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\01
5()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:"
.\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-
\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(
?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\
015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\
n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-
\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff
\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\
\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?
:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\
x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\
n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\
[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\0
15()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\x
ff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]
*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\
([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*
\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]
\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|
\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\
\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*
(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()
]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\
\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\
xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040
)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\
037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\
040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80
-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x
80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\
n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80
-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040
\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\
([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*
\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]
\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|
\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\
\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*
(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()
]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\
\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\
xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040
)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\
037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\
040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80
-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x
80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n
\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-
\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\
t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@
,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80
-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015(
)]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff]
[^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*
(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([
^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\)
)[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\0
00-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[
^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\
040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80
-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x
80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\0
15()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\x
ff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]
*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:"
.\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80
-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xf
f]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()
]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^
\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]
*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015(
)]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?!
[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\01
5\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:
(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x
80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)




email-opt.pl 
($ctrl  '\000-\037\0177' )
(RFC822  $CRlist  \n )
# $email が正しいメールアドレスか判定する

$esc         = '\\\\';               $Period      = '\.';
$space       = '\040';               $tab         = '\t';
$OpenBR      = '\[';                 $CloseBR     = '\]';
$OpenParen   = '\(';                 $CloseParen  = '\)';
$NonASCII    = '\x80-\xff';          $ctrl        = '\000-\037';
$CRlist      = '\n\015';
$qtext       = qq/[^$esc$NonASCII$CRlist\"]/;
$dtext       = qq/[^$esc$NonASCII$CRlist$OpenBR$CloseBR]/;
$quoted_pair = qq<${esc}[^$NonASCII]>;
$ctext       = qq<[^$esc$NonASCII$CRlist()]>;
$Cnested     = qq<$OpenParen$ctext*(?:$quoted_pair$ctext*)*$CloseParen>;
$comment     =
    qq<$OpenParen$ctext*(?:(?:$quoted_pair|$Cnested)$ctext*)*$CloseParen>;
$X           = qq<[$space$tab]*(?:${comment}[$space$tab]*)*>;
$atom_char   = qq/[^($space)<>\@,;:\".$esc$OpenBR$CloseBR$ctrl$NonASCII]/;
$atom        = qq<$atom_char+(?!$atom_char)>;
$quoted_str  = qq<\"$qtext*(?:$quoted_pair$qtext*)*\">;
$word        = qq<(?:$atom|$quoted_str)>;
$domain_ref  = $atom;
$domain_lit  = qq<$OpenBR(?:$dtext|$quoted_pair)*$CloseBR>;
$sub_domain  = qq<(?:$domain_ref|$domain_lit)$X>;
$domain      = qq<$sub_domain(?:$Period$X$sub_domain)*>;
$route       = qq<\@$X$domain(?:,$X\@$X$domain)*:$X>;
$local_part  = qq<$word$X(?:$Period$X$word$X)*>;
$addr_spec   = qq<$local_part\@$X$domain>;
$route_addr  = qq[<$X(?:$route)?$addr_spec>];
$phrase_ctrl = '\000-\010\012-\037';
$phrase_char =
   qq/[^()<>\@,;:\".$esc$OpenBR$CloseBR$NonASCII$phrase_ctrl]/;
$phrase      =
    qq<$word$phrase_char*(?:(?:$comment|$quoted_str)$phrase_char*)*>;
$mailbox     = qq<$X(?:$addr_spec|$phrase$route_addr)>;

print "ok\n" if $email =~ /^$mailbox$/o;


perl5.6.0 perl   $Cnested  $comment  1 2
use re 'eval';
$comment     =
  qr<$OpenParen$ctext*(?:(?:$quoted_pair|(??{$comment}))$ctext*)*$CloseParen>;


使  (??{ code })   use re 'eval'; no re 'eval'; 
調 Email::Valid  Mail::CheckUser 使 使 RFC 822 調 使
FromRFC 822  mailbox   mailbox 使  mailbox  addr-spec mailbox  addr-spec  Foo Bar <foobar@example.com>  mailbox  addr-spec  foobar@exmaple.com  addr-spec  mailbox 
使 addr-spec 
# メールアドレスの正規表現 $mail_regex

$esc         = '\\\\';               $Period      = '\.';
$space       = '\040';
$OpenBR      = '\[';                 $CloseBR     = '\]';
$NonASCII    = '\x80-\xff';          $ctrl        = '\000-\037';
$CRlist      = '\n\015';
$qtext       = qq/[^$esc$NonASCII$CRlist\"]/;
$dtext       = qq/[^$esc$NonASCII$CRlist$OpenBR$CloseBR]/;
$quoted_pair = qq<${esc}[^$NonASCII]>;
$atom_char   = qq/[^($space)<>\@,;:\".$esc$OpenBR$CloseBR$ctrl$NonASCII]/;
$atom        = qq<$atom_char+(?!$atom_char)>;
$quoted_str  = qq<\"$qtext*(?:$quoted_pair$qtext*)*\">;
$word        = qq<(?:$atom|$quoted_str)>;
$domain_ref  = $atom;
$domain_lit  = qq<$OpenBR(?:$dtext|$quoted_pair)*$CloseBR>;
$sub_domain  = qq<(?:$domain_ref|$domain_lit)>;
$domain      = qq<$sub_domain(?:$Period$sub_domain)*>;
$local_part  = qq<$word(?:$Period$word)*>;
$addr_spec   = qq<$local_part\@$domain>;
$mail_regex  = $addr_spec;


 addr-spec 
(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\
\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][
^\\\x80-\xff\n\015"]*)*")(?:\.(?:[^(\040)<>@,;:".\\\[\]\000-\037\x
80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\
xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*"))*@(?:[^(\0
40)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000
-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])
(?:\.(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;
:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x
80-\xff])*\]))*


使
$mail_regex =
q{(?:[^(\040)<>@,;:".\\\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\\} .
q{\[\]\000-\037\x80-\xff])|"[^\\\\\x80-\xff\n\015"]*(?:\\\\[^\x80-\xff][} .
q{^\\\\\x80-\xff\n\015"]*)*")(?:\.(?:[^(\040)<>@,;:".\\\\\[\]\000-\037\x} .
q{80-\xff]+(?![^(\040)<>@,;:".\\\\\[\]\000-\037\x80-\xff])|"[^\\\\\x80-} .
q{\xff\n\015"]*(?:\\\\[^\x80-\xff][^\\\\\x80-\xff\n\015"]*)*"))*@(?:[^(} .
q{\040)<>@,;:".\\\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\\\[\]\0} .
q{00-\037\x80-\xff])|\[(?:[^\\\\\x80-\xff\n\015\[\]]|\\\\[^\x80-\xff])*} .
q{\])(?:\.(?:[^(\040)<>@,;:".\\\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,} .
q{;:".\\\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\\\x80-\xff\n\015\[\]]|\\\\[} .
q{^\x80-\xff])*\]))*};


 $mail_regex 使$email perl5.004 \z  $(?!\n) 
# $email が正しいメールアドレス(addr_spec)か判定する

if ($email !~ /^$mail_regex\z/o) {
  print "不正なメールアドレスです\n";
}


DoCoMo(i-mode)  J-Phone(J-Sky)  irregular.@docomo.ne.jp  @  .()  使 RFC 822 @  local-part  .() .() @ DoCoMo(i-mode) J-Phone(J-Sky) 使



perl  EUC-JP 



perl   perl  perl  JIS
$str = "このTESTで充分";
$str =~ s/このTESTで充分/このテストで十分/;  # JIS でも SJIS でも駄目
print $str, "\n";

unmatched () in regexp   ESC (B (   )  SJIS  unmatched [] in regexp  SJIS  0x8F 0x5B 0x5B  ASCII  [ 
 SJIS   \Q  \E 
$str = "このTESTで充分";
$str =~ s/\QこのTESTで充分\E/このテストで十分/;  # これで SJIS でも大丈夫?
print $str, "\n";

SJIS  0x8F 0x5C 0x5C  ASCII  \  1 \  1  2 \
 SJIS  2 \  2 @  2 \  \ 2 @ SJIS  2 \  Ы 2 @  А  SJIS 
 SJIS  \Q  \E 
if ($str =~ /\Q$keyword\E/) {
  print "マッチした\n";
}


 $keyword  \Q  \E  SJIS  $str = '';  $keyword = 'X';   SJIS  0x83 0x58 0x58  ASCII X  $str = '';  $keyword = '';  0x82 0x83 0x82 0x81 0x81 0x83 0x82 0x82  1 0x83 0x82 0x81 0x81 0x83 0x82 
perl 1 jperl 使 jperl  perl  Windows jperl ( )
http://homepage2.nifty.com/kipp/perl/jperl/

 EUC-JP EUC-JP EUC-JP  JIS  SJIS  perl  perl  perl  EUC-JP EUC-JP
 EUC-JP  SJIS 


 EUC-JP 



perl  EUC-JP  SJIS  JIS   EUC-JP perl  EUC-JP   EUC-JP  perl 
 EUC-JP  jcode.pl ( )使 EUC-JP  $str  EUC-JP
# $str を EUC-JP に変換する

require 'jcode.pl';

jcode::convert(\$str, 'euc');


'euc'  'sjis'  'jis'  SJIS  JIS  $code 
# 漢字コードが $code である $str を EUC-JP に変換する

require 'jcode.pl';

jcode::convert(\$str, 'euc', $code);


調  $code 使 使
 my 使
# my 宣言された変数を変換するときの間違った例

require 'jcode.pl';

my $str = 'my 宣言された変数の型グロブはない';

jcode::convert(*str, 'euc');

my   my  \$str 


jcode.pl
ftp://ftp.iij.ad.jp/pub/IIJ/dist/utashiro/perl/
 jcode.pl-2.13   jcode.pl 使 jcode.pl 使 jcode.pl    jcode.pl
Jcode.pm - jcode.pl ( ) Jcode.pm  UNICODE 使 jcode.pl  Windows perl  ActivePerl 5.6(  )
http://homepage2.nifty.com/kipp/perl/Jcode/index.html

 2.10 jcode.pl  perl使  perl  $_  @_ my local   2.10 jcode.pl  local  *_ jcode.pl  Jcode.pm  perl 
 perl 調 perl -V  usethreads=undef  jcode.pl 使 perl5.005 perl   2.10 jcode.pl 使  $_  @_


調



# $str の漢字コードを調べる

require 'jcode.pl';

($match, $code) = jcode::getcode(\$str);
$code = 'euc' if $code eq undef and $match > 0;

jcode.pl  getcode 使 $code  'euc'  'sjis''jis'  jcode.pl 
調SJIS () SJIS  2 EUC-JP  1 EUC-JP  SJIS  jcode::getcode()  undef  EUC-JP  undef  EUC-JP 
jcode::getcode()  SJIS SJIS   EUC-JP 
# $str の漢字コードを調べる

require 'jcode.pl';

($match, $code) = jcode::getcode(\$str);
$code = 'euc' if $code eq undef and $match > 0;

$ascii = '[\x00-\x7F]';
if ($code eq 'euc') {
  if ($str !~ /^(?:$jcode::re_euc_c|$jcode::re_euc_kana|
                   $jcode::re_euc_0212|$ascii)*$/ox) {
    if ($str =~ /^(?:$jcode::re_sjis_c|$jcode::re_sjis_kana|$ascii)*$/o) {
      $code = 'sjis';
    }
  }
}

 SJIS  EUC-JP   $code  EUC-JP




$str  EUC-JP   EUC-JP   EUC-JP
# $str に全角文字(半角カタカナを含まない)が含まれているか判定する

if ($str =~ /[\xA1-\xFE][\xA1-\xFE]/) {
  print "含まれている\n";
}

 JIS X 0208  JIS X 0212   JIS X 0201  JIS X 0208  JIS X 0212 ASCII  JIS X 0201  /[\xA1-\xFE][\xA1-\xFE]/ 使
# $str に半角カタカナが含まれているか判定する

if ($str =~ /\x8E/) {
  print "含まれている\n";
}


EUC-JP  /\x8E/ 調
# $str に ASCII 以外が含まれているか判定する

if ($str =~ /[\x8E\xA1-\xFE]/) {
  print "含まれている\n";
}


ASCII  /[\x8E\xA1-\xFE]/ 調 \x8E  JIS X 0201  1 [\xA1-\xFE]  JIS X 0208  1 JIS X 0212  2 ASCII 
$str  EUC-JP   jcode.pl 使調 jcode.pl 使 調 $str 調 undef  ASCII  undef  ASCII  $match 使 undef 調
# $str に ASCII 以外が含まれているか判定するときの間違った例

require 'jcode.pl';
$code = jcode::getcode(\$str);

if ($code eq undef) {
  print "ASCII以外は含まれていない\n";
  print "この判断は間違い\n";
}

jcode::getcode()  EUC-JP  SJIS  undef 調  $match 使 undef




$str  EUC-JP   EUC-JP   EUC-JP
# $str の最後の文字が途切れているか判定する

if ($str =~ /\x8F$/ or $str =~ tr/\x8E\xA1-\xFE// % 2) {
  print "最後の文字が途切れている\n";
}


EUC-JP JIS X 0201()JIS X 0208() JIS X 0212() JIS X 0212  3 \x8F  $str  \x8F JIS X 0212  1  JIS X 0201  JIS X 0208  1JIS X 0212  2 tr/\x8E\xA1-\xFE//  $str JIS X 0201  JIS X 0208  1 2 JIS X 0212  2 3




$str  EUC-JP   EUC-JP   EUC-JP
# $str の全角英数字を半角英数字に変換する

require 'jcode.pl';

jcode::tr(\$str, '0-9A-Za-z', '0-9A-Za-z');


jcode.pl   tr使 tr  jcode.pl   tr
# $str の全角スペースなどを半角スペースなどに変換する

require 'jcode.pl';

jcode::tr(\$str, ' ()_@-', ' ()_@-');


12




$str  EUC-JP   EUC-JP   EUC-JP
# $str の半角カタカナを全角カタカナに変換する

require 'jcode.pl';

jcode::h2z_euc(\$str);


jcode.pl   h2z_euc 使



$str および $patternEUC-JP という前提ですので, 必要ならばあらかじめ EUC-JP に変換しておいてください. 漢字コードの変換に関しては 「漢字コードを EUC-JP に変換して処理する」を参照.

perl で日本語を扱う場合にはスクリプトを EUC-JP で書き, 漢字コードが EUC-JP である日本語を処理するというのが 一番問題が起きにくい方法であるということを 「perl スクリプトは EUC-JP で書く」と 「漢字コードを EUC-JP に変換して処理する」で述べました.しかし,それだけでは少し困ったことが 起きることがあります.たとえば,次のようなスクリプトを実行すると 間違ってマッチしてしまいます.

# 間違ってマッチしてしまう例

$str = 'これはテストです';
$pattern = '好';

if ($str =~ /$pattern/) {
  print "マッチした\n";
}

なぜこのようなことが起きてしまうのかというと,EUC-JP の「ス」の文字コードは 0xA5 0xB9 ,「ト」は 0xA5 0xC8,「好」は 0xB9 0xA5 であり,ちょうど「スト」の真ん中の部分が「好」と同じになるのでマッチして しまうのです.このようにずれた場所でマッチして しまっては困る場合には次のように書きます.

# $str に $pattern を正しくマッチさせる

$ascii = '[\x00-\x7F]';
$twoBytes = '[\x8E\xA1-\xFE][\xA1-\xFE]';
$threeBytes = '\x8F[\xA1-\xFE][\xA1-\xFE]';

if ($str =~ /^(?:$ascii|$twoBytes|$threeBytes)*?(?:$pattern)/) {
  print "マッチした\n";
}

 /$pattern/  $pattern   $pattern  EUC-JP  1 1 ASCII 2 JIS X 0201( ) JIS X 0208()3  JIS X 0212()  (?:$ascii|$twoBytes|$threeBytes) $pattern 
.()使 .()  (?:$ascii|$twoBytes|$threeBytes)   /$pattern/  /^.*?(?:$pattern)/ 
 EUC-JP  1  2 3 $str  $pattern  \000 使
# 区切り文字をつけて正しくマッチさせる(非常に遅い)

$twoBytes = '[\x8E\xA1-\xFE][\xA1-\xFE]';
$threeBytes = '\x8F[\xA1-\xFE][\xA1-\xFE]';

$pattern =~ s/($twoBytes|$threeBytes)/$1\000/og;
$str =~ s/($twoBytes|$threeBytes)/$1\000/og;

if ($str =~ /$pattern/) {
  print "マッチした\n";
}


使
 2使 使
調 使 (15)  使 使 使
使 使

# 間違って置換してしまう例

$str = 'これはテストです';
$pattern = '好';
$replace = '嫌';

$str =~ s/$pattern/$replace/g;



# $str の $pattern を $replace に正しく置換する

$ascii = '[\x00-\x7F]';
$twoBytes = '[\x8E\xA1-\xFE][\xA1-\xFE]';
$threeBytes = '\x8F[\xA1-\xFE][\xA1-\xFE]';

$str =~ s/\G((?:$ascii|$twoBytes|$threeBytes)*?)(?:$pattern)/$1$replace/g;


EUC-JP  1 $1  \G 使 $1 使 $pattern   $pattern  (?:$ascii|$twoBytes|$threeBytes)*?  $1 
 \G  \G 使 g  g 1  g \G  ^  g $str  $pattern ^ 使\G  g \G   ^  $pattern  \G 調 \G 使 $pattern

# 区切り文字をつけて正しく置換させる(非常に遅い)

$twoBytes = '[\x8E\xA1-\xFE][\xA1-\xFE]';
$threeBytes = '\x8F[\xA1-\xFE][\xA1-\xFE]';

$pattern =~ s/($twoBytes|$threeBytes)/$1\000/og;
$str =~ s/($twoBytes|$threeBytes)/$1\000/og;

$str =~ s/$pattern/$replace/g;
$str =~ tr/\000//d;
# $str =~ s/($twoBytes|$threeBytes)\000/$1/og;


 \000 使  tr使 $str  \000  tr 使 $str  \000  $str  \000   tr 使 $str =~ tr/\000//d;  $str =~ s/($twoBytes|$threeBytes)\000/$1/og;
 2 使 (35)  使 4使 tr 使 使
 $pattern  Perl   (  \( CGI $pattern   ( 
 $keyword   $pattern  \Q$keyword\E 
if ($str =~ /^(?:$ascii|$twoBytes|$threeBytes)*?\Q$keyword\E/) {
  print "マッチした\n";
}


$str =~ s/\G((?:$ascii|$twoBytes|$threeBytes)*?)\Q$keyword\E/$1$replace/g;

\Q  \E 
 1 使
 $pattern 
if ($str =~ /$pattern/) {
  if ($str =~ /^(?:$ascii|$twoBytes|$threeBytes)*?(?:$pattern)/) {
    print "マッチした\n";
  }
}


$keyword  /\Q$keyword\E/ 使 index 使
if (index($str, $keyword) > -1) {
  if ($str =~ /^(?:$ascii|$twoBytes|$threeBytes)*?\Q$keyword\E/) {
    print "マッチした\n";
  }
}


index  index 使


 perl(perl5.8.8)index 使 /\Q$keyword\E/ 使  perl 

 EUC-JP SJIS  SJIS  SJIS  1 SJIS  1
EUC-JP  perl5.005 
# EUC-JP で perl5.005 以降限定の方法

$eucpre = qr{(?<!\x8F)};
$eucpost = qr{
    (?=                         
     (?:[\xA1-\xFE][\xA1-\xFE])* # JIS X 0208 が 0文字以上続いて
     (?:[\x00-\x7F\x8E\x8F]|\z)  # ASCII, SS2, SS3 または終端
    )
 }x;

if ($str =~ /$eucpre(?:$pattern)$eucpost/) {          # パターンマッチ
  print "マッチした\n";
}

if ($str =~ /$eucpre\Q$keyword\E$eucpost/) {      # キーワードマッチ
  print "マッチした\n";
}

$str =~ s/$eucpre(?:$pattern)$eucpost/$replace/g;     # パターン置換

$str =~ s/$eucpre\Q$keyword\E$eucpost/$replace/g; # キーワード置換


$eucpre  $eucpost  (lookbehind) (lookahead) 使 (?<regex) (?=regex) (?<!regex) 使
JIS X 0212  2 JIS X 0212  2 JIS X 0212  1 \x8F  \x8F  JIS X 0212  2
JIS X 0208  2  JIS X 0212  3  EUC-JP  JIS X 0208 JIS X 0208 
0  $eucpre  $eucpost  $1 


()



# $str の先頭の空白文字(全角スペース含)を削除する
$str =~ s/^(?:\s|$Zspace)+//o; # $str が EUC-JP の場合
$str =~ s/^(?:\s|$Zspace_sjis)+//o; # $str が SJIS の場合

# $str の末尾の空白文字(全角スペース含)を削除する
$str =~ s/^($character*?)(?:\s|$Zspace)+$/$1/o; # $str が EUC-JP の場合
$str =~ s/$eucpre(?:\s|$Zspace)+$//o; # $str が EUC-JP の場合(perl5.005以降)

$str =~ s/^($character_sjis*?)(?:\s|$Zspace_sjis)+$/$1/o; # $str が SJIS の場合


使

# $str の末尾の空白文字(全角スペース含)を削除する(間違い)
$str =~ s/(?:\s|$Zspace)+$//o; # $str が EUC-JP の場合
$str =~ s/(?:\s|$Zspace_sjis)+$//o; # $str が SJIS の場合


SJIS  $str = '@';  perl  EUC-JP 




$str  EUC-JP   EUC-JP   EUC-JP
# $str を文字単位に分割して配列 @chars に代入する

$ascii = '[\x00-\x7F]';
$twoBytes = '[\x8E\xA1-\xFE][\xA1-\xFE]';
$threeBytes = '\x8F[\xA1-\xFE][\xA1-\xFE]';

@chars = $str =~ /$ascii|$twoBytes|$threeBytes/og;


@chars = ($str =~ /($ascii|$twoBytes|$threeBytes)/og; EUC-JP  1 $ascii|$twoBytes|$threeBytes  @chars  ($1, $2, $3,)   g ($1, $2, $3,, $1, $2, $3,)  1 EUC-JP  1
 @chars   =  =~ @char = $str
g  1 $1 使




# $str を $bytesバイトで折り返す

require 'fold.pl';

while (length($str)) {
  (my $folded, $str) = fold($str, $bytes);
  print $folded, "\n";
}


fold.pl ( )使fold.pl  使  substr 使 fold  31  $bytes  $bytes 41  fold.pl  fold.pl  SJIS  EUC-JP  2
Jcode.pm  jfold  使
 EUC-JP  $str  EUC-JP  EUC-JP  EUC-JP
# $str を禁則処理しつつ折り返す

require 'fold.pl';
require 'jcode.pl';

$no_begin = "!%),.:;?]}¢°’”‰′″℃、。々〉》」』】〕" .
    "ぁぃぅぇぉっゃゅょゎ゛゜ゝゞァィゥェォッャュョヮヵヶ" .
    "・ーヽヾ!%),.:;?]}";              # 行頭禁則文字
$no_begin_jisx0201 = "。」、・ァィゥェォャュョッー゛゜";
jcode::z2h_euc(\$no_begin_jisx0201);
$no_begin .= $no_begin_jisx0201;                 # 行頭禁則文字(半角カタカナ)
$no_end = "\$([{£\‘“〈《「『【〔$([{¥";  # 行末禁則文字
$no_end_jisx0201 = "「";
jcode::z2h_euc(\$no_end_jisx0201);
$no_end .= $no_end_jisx0201;                     # 行末禁則文字(半角カタカナ)
$allow_end = $no_begin;                          # ぶら下げ行頭禁則文字
$del_space = '(?:\s|\xA1\xA1)';                  # 削除する行頭行末空白
$basebytes = 74;                                 # 基本長
$maxbytes = 76;                                  # 最大長
$ascii = '[\x00-\x7F]';
$twoBytes = '[\x8E\xA1-\xFE][\xA1-\xFE]';
$threeBytes = '\x8F[\xA1-\xFE][\xA1-\xFE]';

map {$no_begin{$_} = 1;} ($no_begin =~ /$ascii|$twoBytes|$threeBytes/og);
map {$no_end{$_} = 1;} ($no_end =~ /$ascii|$twoBytes|$threeBytes/og);
map {$allow_end{$_} = 1 + /[\xA1-\xFE]/ - /\x8E/;}
    ($allow_end =~ /$ascii|$twoBytes|$threeBytes/og);

sub fold_properly {
  my $str = shift;
  my($folded, $strtmp, $bytestmp, $begin_char, $end_char, $flag);
  $flag = 1; # 行頭禁則処理状態(1:ぶら下げ, 0:追い出し)
  $bytestmp = $basebytes;
  $str =~ tr/\t\n\r\f/ /; # 空白文字をスペースに変換
  $str =~ s/^$del_space+//o; # 行頭空白削除
  ($begin_char) = %no_begin; # 行頭禁則文字を1文字代入
  while ($no_begin{$begin_char} or $no_end{$end_char}) {
    ($folded, $strtmp) = fold($str, $bytestmp, 0, 1);
    while (length($folded) - ($folded =~ tr/\x8E//) <= $basebytes and
   $strtmp ne '' and $flag) { # 半角カタカナのための表示幅処理
      ($folded, $strtmp) = fold($str, $bytestmp, 0, 1);
      my ($folded_tmp, $strtmp_tmp) = fold($str, $bytestmp + 1, 0, 1);
      if (length($folded_tmp) - ($folded_tmp =~ tr/\x8E//) <= $basebytes) {
        ($folded, $strtmp) = ($folded_tmp, $strtmp_tmp);
        $bytestmp++;
      } else {
        last;
      }
    }
    ($begin_char) = $strtmp =~ /^$del_space*($ascii|$twoBytes|$threeBytes)/o;
    ($end_char) = $folded =~ /($threeBytes|$twoBytes|$ascii)$/o;
    if ($flag) { # ぶら下げ禁則処理
      if ($no_begin{$begin_char} and $allow_end{$begin_char}) { # ぶら下げ可能
        if (length($folded) - ($folded =~ tr/\x8E//)
            + $allow_end{$begin_char} <= $maxbytes) {
          $bytestmp++;
        } else {
          $flag = 0;
          $bytestmp = $basebytes - 1 + ($folded =~ tr/\x8E//);
        }
      } else {
        $flag = 0;
        $bytestmp--;
      }
    } else {
      $bytestmp--;
    }
    if ($bytestmp == 0) { # 禁則処理不可能
      ($folded, $strtmp) = fold($str, $basebytes, 0, 1);
      last;
    }
  }
  $folded =~ s/^((?:$ascii|$twoBytes|$threeBytes)*?(?=$del_space))
      $del_space+$/$1/ox; # 行末空白削除
  ($folded, $strtmp);
}

while (length($str)) {
  (my $folded, $str) = fold_properly($str);
  print $folded, "\n";
}




Base64



$str  EUC-JP   EUC-JP   EUC-JP
# $data を Base64エンコードして $encoded_data を求める

use MIME::Base64;

$encoded_data = encode_base64($data);


Base64  MIME::Base64  encode_base64 使 Base64 RFC 2045(  ) Base64 76 encode_base64 2  76
# $encoded_data を Base64デコードして元のデータ $data に戻す

use MIME::Base64;

$data = decode_base64($encoded_data);


Base64  MIME::Base64  decode_base64 使 $encoded_data  76
 encoded-word encoded-word  RFC 2047(  )encoded-word  =?charset?encoding?encoded-text?= =?ISO-2022-JP?B?GyRCTmMbKEI=?=  ""  encoded-word  encoding  B encoded-word 
encoding  B encoded-text  B B Base64 encoded-word  Base64 B
# $str をBエンコードして encoded-word に変換する(不完全)

require 'jcode.pl';
use MIME::Base64;

jcode::convert(\$str, 'jis', 'euc', 'z');
$str = '=?ISO-2022-JP?B?' . encode_base64($str, '') . '?=';


B encode_base64 使2 charset  ISO-2022-JP   $str  JIS  ISO-2022-JP ISO-2022-JP  JIS  ISO-2022-JP 使 jcode::convert 4 'z' 
encoded-word   RFC 2047  RFC 2047  encoded-word 

(一)encoded-word   75  
(二)encoded-word   76  
(三)encoded-word    
(四)encoded-text   ASCII   
(五)encoded-word   

Subject  Comment   'text'   
"("   ")"   'comment'   
From ToCC'phrase'   
'addr-spec'   
'quoted-string'    

(六) encoded-word   'linear-white-space' 

14 encoded-word  341212
1encoded-word  75B 2 encoded-word  2 encoded-word B encoded-text 344  2 3 jcode.pl 使 JIS   ASCII 
2encoded-word  76 encoded-word  76 encoded-word 調 encoded-word  76
 encoded-word  5 encoded-word  encoded-word 
# $str を encoded-word に変換し $line に追加する

require 'jcode.pl';
use MIME::Base64;

$ascii = '[\x00-\x7F]';
$twoBytes = '[\x8E\xA1-\xFE][\xA1-\xFE]';
$threeBytes = '\x8F[\xA1-\xFE][\xA1-\xFE]';

sub add_encoded_word {
  my($str, $line) = @_;
  my $result;

  while (length($str)) {
    my $target = $str;
    $str = '';
    if (length($line) + 22 +
 ($target =~ /^(?:$twoBytes|$threeBytes)/o) * 8 >76) {
      $line =~ s/[ \t\n\r]*$/\n/;
      $result .= $line;
      $line = ' ';
    }
    while (1) {
      my $encoded = '=?ISO-2022-JP?B?' .
      encode_base64(jcode::jis($target, 'euc', 'z'), '') . '?=';
      if (length($encoded) + length($line) >76) {
 $target =~ s/($threeBytes|$twoBytes|$ascii)$//o;
 $str = $1 . $str;
      } else {
 $line .= $encoded;
 last;
      }
    }
  }
  $result . $line;
}

$line = add_encoded_word($str, $line);


実行例
$line = 'Subject: ';
$str = 'これはテストです.This is test.';
$line = add_encoded_word($str, $line);
print $line, "\n";

実行結果
Subject: =?ISO-2022-JP?B?GyRCJDMkbCRPJUYlOSVIJEckOSElGyhCVGhpcyBpcyB0ZXN0?=
 =?ISO-2022-JP?B?Lg==?=


 $line  $str  encoded-word $str encoded-word  75 encoded-word RFC 2047  encoded-word ASCII  is  test.  encoded-word  Subject  unstructured header 
# unstructured header $header を MIMEエンコードする
# add_encoded_word() については上のスクリプトを参照

sub mime_unstructured_header {
  my $oldheader = shift;
  my($header, @words, @wordstmp, $i) = ('');
  my $crlf = $oldheader =~ /\n$/;
  $oldheader =~ s/\s+$//;
  @wordstmp = split /\s+/, $oldheader;
  for ($i = 0; $i < $#wordstmp; $i++) {
    if ($wordstmp[$i] !~ /^[\x21-\x7E]+$/ and
 $wordstmp[$i + 1] !~ /^[\x21-\x7E]+$/) {
      $wordstmp[$i + 1] = "$wordstmp[$i] $wordstmp[$i + 1]";
    } else {
      push(@words, $wordstmp[$i]);
    }
  }
  push(@words, $wordstmp[-1]);
  foreach $word (@words) {
    if ($word =~ /^[\x21-\x7E]+$/) {
      $header =~ /(?:.*\n)*(.*)/;
      if (length($1) + length($word) >76) {
 $header .= "\n $word";
      } else {
 $header .= $word;
      }
    } else {
      $header = add_encoded_word($word, $header);
    }
    $header =~ /(?:.*\n)*(.*)/;
    if (length($1) == 76) {
      $header .= "\n ";
    } else {
      $header .= ' ';
    }
  }
  $header =~ s/\n? $//mg;
  $crlf ? "$header\n" : $header;
}

$header = mime_unstructured_header($header);


実行例
$header = "Subject: ASCII 日本語 ASCIIと日本語 ASCII ASCII\n";
$header = mime_unstructured_header($header);
print $header;

実行結果
Subject: ASCII =?ISO-2022-JP?B?GyRCRnxLXDhsGyhCIEFTQ0lJGyRCJEhGfEtcGyhC?=
 =?ISO-2022-JP?B?GyRCOGwbKEI=?= ASCII ASCII


add_encoded_word()  $line = add_encoded_word($str, $line); 使
ASCII  encoded-word 6 encoded-word  'linear-white-space'  1encoded-word  'linear-white-space' 'linear-white-space'  encoded-word 'linear-white-space'  encoded-word 'linear-white-space'   1 encoded-word 
# $str をBデコードして encoded-word を元に戻す

require 'jcode.pl';
use MIME::Base64;

$lws = '(?:(?:\x0D\x0A|\x0D|\x0A)?[ \t])+';
$ew_regex = '=\?ISO-2022-JP\?B\?([A-Za-z0-9+/]+=*)\?=';
$str =~ s/($ew_regex)$lws(?=$ew_regex)/$1/gio;
$str =~ s/$lws/ /go;
$str =~ s/$ew_regex/decode_base64($1)/egio;
jcode::convert(\$str, 'euc', 'jis');


 $str  encoded-word  encoded-word  'linear-white-space' encoded-word  "(" 'linear-white-space'  encoded-word  encoded-word  encoded-word   $str $str = q{"=?ISO-2022-JP?B?GyRCTmMbKEI=?="};   quoted-string  encoded-word
 Outlook Express  encoded-word  quoted-string RFC 2047 Outlook Express 5  Outlook Express 5   encoded-word  76
encoded-word  mime_pls(mimew.pl) ( ) RFC 2047  encoded-word  Subject  From  word  $str = "test"; "test "  $` $& $' 使 encoded-word  encoded-word 
Jcode.pm  MIME  mime_encode  MIME mime_decode  0.63
RFC 2047  encoded-word IM(Internet Message)  IM::Iso2022jp  使IM 使 Iso2022jp.pm 


URI



''  '%a5%a8%a5%b9%a5%b1%a1%bc%a5%d7'  URI
# $str を URIエスケープする

$str =~ s/(\W)/'%' . unpack('H2', $1)/eg;


 '%a5%a8%a5%b9%a5%b1%a1%bc%a5%d7'  URI ''
# $str を URIアンエスケープする

$str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;


調 URI  unpack 使 sprintf  ord 使 pack  'H2' 使 hex  chr  hex  pack  'C' 使  i使 {2} 使 '%A5%A8%A5%B9%A5%B1%A1%BC%A5%D7' sprintf  ord 使
||= 使CGI 使
# $str を URIエスケープする(再利用版)

$str =~ s/(\W)/$escape{$1} ||= '%' . unpack('H2', $1)/eg;


# $str を URIアンエスケープする(再利用版)

$str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/$unescape{$1} ||= pack('H2', $1)/eg;


'%' . unpack('H2', $1)  pack('H2', $1) 使 ||=  調 URI700%  7

# $str を URIエスケープする(変換テーブル版)

foreach $i (0x00 .. 0xFF) {
  $escape{chr($i)} = sprintf('%%%02x', $i);
}

$str =~ s/(\W)/$escape{$1}/g;


# $str を URIアンエスケープする(変換テーブル版)

foreach $i (0x00 .. 0xFF) {
  $unescape{sprintf('%02x', $i)} = chr($i);
  $unescape{sprintf('%02X', $i)} = chr($i);
}

$str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/$unescape{$1}/g;


  e
URI  \W  URI URI URI RFC 2396(  ) unreserved  unreserved URI
# $str を URIエスケープする(必要最小限版)

$str =~ s/([^a-zA-Z0-9_.!~*'()-])/'%' . unpack('H2', $1)/eg;


 CGI  URI 
URIRFC 2396 URI使 uric   URI::Escape  uri_escape 使  [;\/?:@&=+\$,A-Za-z0-9\-_.!~*'()]  CGI  uri_escape URI  URI CGI  $value = 'A&B=C';  print "http://foo.bar/cgi-bin/hoge.cgi?value=$value"; uri_escape 使 $value  &  =  URI value=A  B=C  2 &   uri_escape 2 URI::Escape 使
 + CGI  FORM  GET  POST 使  2 2  + FORM  GET  POST  使 HTML 4.0(  )  17.13.4 Form content types  content types   application/x-www-form-urlencoded  CGI/1.1 5. The CGI Script Command Line 
application/x-www-form-urlencoded  control names  values  +   %HH  URI controle names  values  =   &  +  URI name1=value1&name2=value2 control names  values 
# $str に対しエンコードの文字処理部分を行なう

$str =~ s/([^\w ])/'%' . unpack('H2', $1)/eg;
$str =~ tr/ /+/;


 +  URI s/%20/+/g; 
# $str に対しデコードの文字処理部分を行なう

$str =~ tr/+/ /;
$str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;


 URI+   +   $str =~ s/\+/ /g;  tr 使
使 URL application/x-www-form-urlencoded  URL %HH  URI URL URI  URL  +  
search-string = search-word *( "+" search-word ) http://foo.bar/cgi-bin/hoge.cgi?arg1+arg2+arg3   +  search-string   +  +  search-string  URI %20   +   $value = 'A B C';  CGI  print "http://foo.bar/cgi-bin/hoge.cgi?$value"; http://foo.bar/cgi-bin/hoge.cgi?A+B+C  hoge.cgi  3 'A''B' 'C'   http://foo.bar/cgi-bin/hoge.cgi?A%20B%20C
 +   +  QUERY_STRING  query  ?  FORM  GET  POST 使 QUERY_STRING  +  QUERY_STRING  = 調 application/x-www-form-urlencoded 




s/\x0D\x0A|\x0D|\x0A/\n/g;


 Windows(DOS)MacUNIX  Windows(DOS) \x0D\x0A Mac \x0DUNIX  \x0A  \x0D\x0A|\x0D|\x0A   \x0D\x0A
 s/\r\n|\r/\n/g; Windows(DOS)  UNIX  perl Mac  perl   Windows(DOS)  \r\nMac  \rUNIX  \n
 Windows(DOS) \x0D\x0AMac \x0D UNIX  \x0A \r  \n  Perl   \x0D\x0A \x0D  \x0A Perl  \n  Windows(DOS)  Mac  UNIX  \n
 \r  \n (Mac)
Windows(DOS)
Mac
UNIX
改行コード値
\x0D\x0A
\x0D
\x0A
改行文字
\n
\n
\n
復帰文字
\r
\r
\r
print FH "\n";
\x0D\x0A を出力
\x0D を出力
\x0A を出力
print FH "\r";
\x0D を出力
\x0A を出力
\x0D を出力
binmode FH;
print FH "\n";
\x0A を出力
\x0D を出力
\x0A を出力
print FH "\r\n";
\x0D\x0D\x0A を出力
\x0A\x0D を出力
\x0D\x0A を出力
binmode FH;
print FH "\r\n";
\x0D\x0A を出力
\x0A\x0D を出力
\x0D\x0A を出力

UNIX  perl  \x0D  \r  \x0A  \n   \x0A \x0D  \r  \x0A  \n \r  \n  Windows(DOS)MacUNIX
  tr使 2 
s/\x0D\x0A/\n/g;
tr/\x0D\x0A/\n\n/;


Perl Windows(DOS)  Mac  UNIX  \r  \x0D  \x0A  \n 
s/\x0D\x0A/\n/g;
tr/\r/\n/;        # 意味的には tr/\x0D\x0A/\n\n/;


tr 使


 <BR> 



s/\x0D\x0A|\x0D|\x0A/<BR>/g;


 Windows(DOS) \x0D\x0A Mac \x0D UNIX  \x0A  \x0D\x0A|\x0D|\x0A \x0D\x0A s/\x0D\x0A|[\x0D\x0A]/<BR>/g; 3
s/\x0D\x0A/<BR>/g;
s/\x0D/<BR>/g;
s/\x0A/<BR>/g;







tr/\x0D\x0A//d;


 <BR> s/\x0D\x0A|\x0D|\x0A//g; <BR> \x0D\x0A  2 s/[\x0D\x0A]//g;  tr/\x0D\x0A//d;  s/\x0D\x0A|\x0D|\x0A//g;  s/[\x0D\x0A]//g; tr 使
1  chomp 使 chomp  Windows(DOS)  MacUNIX \x0D\x0A  UNIX  perl  chomp  \x0D 
s/\x0D?\x0A?$//;





CSV



# CSV形式の $line から値を取り出して @values に入れる

{
  my $tmp = $line;
  $tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/;
  @values = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_}
                ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
}


CSV(Comma Separated Value)  CSV  Excel  CSVExcel  CSV Excel  調

(一)
(二)    
(三) "" 

 $line  $tmp   $line   $line   $line  ,,,  ,
 ,,,   g  g ()
,  "",  2"",  split /,/, $tmp  ($tmp =~ /([^,]*),/g)   2    "" 
,    /([^,]*),/  "",  "" /("[^"]*"),/ CSV 3 "" [^"]  ""  /("(?:[^"]|"")*"),/  2 ($tmp =~ /("(?:[^"]|"")*"|[^,]*),/g)     ""   Jeffrey E. F. Friedl  
 ""   "" ""  "   map   CSV
 Text::CSV 使 ASCII 使  Text::CSV_XS 使


 CSV



# 値に改行コードを含む CSV形式を扱う

while (my $line = <DATA>) {
  $line .= <DATA> while ($line =~ tr/"// % 2 and !eof(DATA));

  $line =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/;
  @values = map {/^"(.*)"$/s ? scalar($_ = $1, s/""/"/g, $_) : $_}
                ($line =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);

  # @values を処理する
}


 CSV CSV  Excel  CSV

(一)  
(二)      
(三) "" 

CSV 1 ""   "" CSV 1  1 CSV
tr/"//   CSV 1 CSV map   s  s


 CSV



# 値の配列 @values から CSV形式の行 $line に変換する

$line = join ',', map {(s/"/""/g or /[\r\n,]/) ? qq("$_") : $_} @values;


CSV CSV CSV
""  ""  map 
s///   s/"/""/g   "" 1 "" 
 $line
print $line, "\n";






1 113
@data = ('A,7,緑',
         'C,6,青',
         'B,4,赤',
         'A,9,紫',
         'A,2,黄緑',
         'B,10,黄',
         'C,3,青紫');


# 第2項でソートする

@data = map {$_->[0]}
            sort {$a->[2] <=> $b->[2]}
                 map {[$_, split /,/]} @data;
ソート後のデータ
@data = ('A,2,黄緑',
         'C,3,青紫',
         'B,4,赤',
         'C,6,青',
         'A,7,緑',
         'A,9,紫',
         'B,10,黄');


 Schwartzian Transform 2 sort 使 1
@data = sort {
  my ($alpha_a, $num_a, $color_a) = split(/,/, $a);
  my ($alpha_b, $num_b, $color_b) = split(/,/, $b);
  $num_a <=> $num_b;
} @data;


Schwartzian Transform 
 3  1 3 2 1 3 1 1 [$_, split /,/]  (, 1, 2, 3)  2 $a->[2]  $b->[2] 3 2 2   1
Schwartzian Transform  使 使
# 第2項でソートする(作業用配列を使った高速版)

@tmp = map {(split /,/)[1]} @data;
@data = @data[sort {$tmp[$a] <=> $tmp[$b]} 0 .. $#tmp];


 @tmp 2 0 .. $#tmp  @data 




1 113
@data = ('A,7,緑',
         'C,6,青',
         'B,4,赤',
         'A,9,紫',
         'A,2,黄緑',
         'B,10,黄',
         'C,3,青紫');


# 第1項でソートし,さらに第2項で降順ソートする

@data = map {$_->[0]}
            sort {$a->[1] cmp $b->[1] or $b->[2] <=> $a->[2]}
                 map {[$_, split /,/]} @data;
ソート後のデータ
@data = ('A,9,紫',
         'A,7,緑',
         'A,2,黄緑',
         'B,10,黄',
         'B,4,赤',
         'C,6,青',
         'C,3,青紫');


1 2  2 sort  or 使 1 cmp 使 2 <=> 使 2  $a  $b
使
# 第1項でソートし,さらに第2項で降順ソートする(作業用配列を使った高速版)

@tmp1 = @tmp2 = ();
foreach (@data) {
  my ($first, $second) = split /,/;
  push(@tmp1, $first);
  push(@tmp2, $second);
}
@data = @data[sort {$tmp1[$a] cmp $tmp1[$b] or
   $tmp2[$b] <=> $tmp2[$a]} 0 .. $#tmp1];


 Schwartzian Transform 
perl5.005  expr foreach ()  foreach () {expr}  perl5.005  perl5 
# 任意の項目数のデータを昇順で多重ソートする

@data = map {$_->[0]}
            sort {$x = ($a->[$_] <=> $b->[$_] or $a->[$_] cmp $b->[$_])
      and return $x foreach (1 .. $#$a); -1}
                 map {[$_, split /,/]} @data;






1 113
@data = ('A,7,緑',
         'C,6,青',
         'B,4,赤',
         'A,9,紫',
         'A,2,黄緑',
         'B,10,黄',
         'C,3,青紫');


# 第3項が自分で決めた順番になるようにソートする

$i = 0;
undef(%color);
foreach $name ('赤', '黄赤', '黄', '黄緑', '緑', '青緑', '青', '青紫',
               '紫', '赤紫') {
  $color{$name} = $i++;
}
@data = map {$_->[0]}
            sort {$color{$a->[3]} <=> $color{$b->[3]}}
                 map {[$_, split /,/]} @data;
ソート後のデータ
@data = ('B,4,赤',
         'B,10,黄',
         'A,2,黄緑',
         'A,7,緑',
         'C,6,青',
         'C,3,青紫',
         'A,9,紫');


使 3  09 




# $year年 $mon月 $mday日の曜日を求める

use Time::Local;

$time = timelocal(0, 0, 0, $mday, $mon - 1, $year - 1900);
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
  localtime($time);
$wday_string = (qw(日 月 火 水 木 金 土))[$wday];


timelocal 使 197011000000(MacPerl  190411000000)  - 1900 - 1 localtime 使 $wday (06)
 19702037 (Zellar)使 使
# $year年 $mon月 $mday日の曜日を求める

$wday = getwday($year, $mon, $mday);
$wday_string = (qw(日 月 火 水 木 金 土))[$wday];

sub getwday {
  my($year, $mon, $mday) = @_;

  if ($mon == 1 or $mon == 2) {
    $year--;
    $mon += 12;
  }
  int($year + int($year / 4) - int($year / 100) + int($year / 400)
      + int((13 * $mon + 8) / 5) + $mday) % 7;
}






# 一週間前の年月日($year年 $mon月 $mday日)を求める

use Time::Local;

# 一週間前の時間を求める
$time = time() - 60 * 60 * 24 * 7;

($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
    localtime($time);
$year += 1900;
$mon++;


 time   60 * 60 * 24 * 7   localtime 使 localtime 西 1900 011 + 1900 + 1 time 
 Date::Calc 使




# $year年 $mon月の末日 $lastday を求める

$lastday = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$mon - 1]
           + ($mon == 2 and $year % 4 == 0 and
       ($year % 400 == 0 or $year % 100 != 0));


 1 12  $mon - 1  23 4 100400 4 and ( 400 or 100 )   2 23
Perl and  or ==  !=  1 23 21 +   0 


N W



# $year年 $mon月の第$n $wday(0-6)曜日が何日か求める
# getwday() は別途参照

$wday1 = getwday($year, $mon, 1);
$mday = 1 + ($wday - $wday1) % 7 + 7 * ($n - 1);
print $mday, "\n";


 1 $wday1
 1  $wday  1 $wday1  1$wday  if7() 1$wday  $n $wday  7 * ($n - 1) 

 Date::Calc 使


3



1 while s/^([-+]?\d+)(\d\d\d)/$1,$2/;


 while  1 while  1  1 1  while 
^([-+]?\d+)  (\d\d\d)  3 $1   3 $2  3 1  3 3
while 使
s/(\d{1,3})(?=(?:\d\d\d)+(?!\d))/$1,/g;


g 使 while 使
(?=regex)  regex   0 0  ^  $  \b (?=regex)  foo(?=bar) foo  foohoge  foo  bar foobar  foo (?=bar)  bar   bar   foobar  foo(?=bar) (?!regex)  (?=regex) 
(?=(?:\d\d\d)+(?!\d)) 3 1 $  3 (?=regex)  3 (\d{1,3})   1  g g   1

s/\G((?:^[-+])?\d{1,3})(?=(?:\d\d\d)+(?!\d))/$1,/g;


\G \G 
調 2 3 1 3 6 35  1

# $num を3桁ごとにコンマで区切る(高速版)

$num = reverse(join(',', reverse($num) =~ /((?:^\d+\.)?\d{1,3}[-+]?)/g))
  if $num =~ /^[-+]?\d\d\d\d/;


3 if   3 if  1000

# $num を3桁ごとにコンマで区切る(最速版)

if ($num =~ /^[-+]?\d\d\d\d+/g) {
  for ($i = pos($num) - 3, $j = $num =~ /^[-+]/; $i > $j; $i -= 3) {
    substr($num, $i, 0) = ',';
  }
}


 if + 
 g  g  g pos  使
 3  substr 3 0  3 $j  1  0




# $num を四捨五入して小数点以下 $decimals桁にする

sub round {
  my ($num, $decimals) = @_;
  my ($format, $magic);
  $format = '%.' . $decimals . 'f';
  $magic = ($num > 0) ? 0.5 : -0.5;
  sprintf($format, int(($num * (10 ** $decimals)) + $magic) /
                   (10 ** $decimals));
}
# 計算例
$number = 1.2345;
$number_0 = round($number, 0);  # 四捨五入して整数にする
$number_2 = round($number, 2);  # 小数点以下2桁まで求める
$number_3 = round($number, 3);  # 小数点以下3桁まで求める
print $number_0, "\n";          # --> 1
print $number_2, "\n";          # --> 1.23
print $number_3, "\n";          # --> 1.235


0.5  int   n10 (n - 1)  1 10 -(n - 1)  0.5 
sprintf 使
sprintf  %f 使
# $num を小数点以下 $decimals桁までに丸める(完全な四捨五入ではない)

$num = sprintf("%.${decimals}f", $num);


 0.15 21 0.2  printf("%.1f\n", 0.15);   0.2  0.1 




# $num を切り上げて小数点以下 $decimals桁にする

sub ceil {
  my ($num, $decimals) = @_;
  my ($format, $tmp1, $tmp2);
  $format = '%.' . $decimals . 'f';
  $tmp1 = $num * (10 ** $decimals);
  $tmp2 += $tmp1 <=> ($tmp2 = int($tmp1));
  sprintf($format, $tmp2 / (10 ** $decimals));
}
# 計算例
$number = 1.2345;
$number_0 = ceil($number, 0);  # 切り上げて整数にする
$number_2 = ceil($number, 2);  # 小数点以下2桁まで求める
$number_3 = ceil($number, 3);  # 小数点以下3桁まで求める
print $number_0, "\n";          # --> 2
print $number_2, "\n";          # --> 1.24
print $number_3, "\n";          # --> 1.235


sprintf 
  $tmp2 = int($tmp1) + ($tmp1 <=> int($tmp1));



 POSIX  ceil 使




# 配列 @array から重複した要素を取り除く

{
  my %count;
  @array = grep(!$count{$_}++, @array);
}


 %count   0 1 ++   !0  1
 %count 
# 配列 @array から重複した要素を取り除く
# 後から出現回数を利用したい

undef(%count);
@array = grep(!$count{$_}++, @array);






# 配列 @array をランダムに並び替える

srand;
for (my $i = @array; --$i; ) {
  my $j = int rand ($i + 1);
  next if $i == $j;
  @array[$i, $j] = @array[$j, $i];
}


srand  rand 使 srand  perl5.004  rand 使 srand  $i = @array  @array  for  ( - 1)for   3 使$i  ( + 1)   --$i  - 1 for   () 

# 配列 @old をランダムに並び替えた配列 @new を作る

srand;
@new = ();
foreach (@old) {
  my $r = int rand (@new + 1);
  push(@new, $new[$r]);
  $new[$r] = $_;
}


 1



うずら メール

Copyright (C) 1999-2019 OHZAKI Hiroki. All rights reserved.