PHPで郵便バーコードの作成

郵便バーコードは結構大変でした。
作ってビックリ、今までどこぞのサイトで作成したバーコードは郵便番号だけを変換した物で実際の変換とは異なっていました。!

このサンプルでの動作画面はこちらで確認できます。(応用例です。)

郵便局のバーコード バーコードマニュアル の掲載サイト
   https://www.post.japanpost.jp/zipcode/zipmanual/index.html

ここを参考して、検証用の事例が載っている箇所があります。
「千葉県鎌ケ谷市右京塚 東3丁目-20-5 郵便・A&bコーポB604号」など、これを正確にバーコードにする必要があります。

郵便サイトに行くと本物がありますので参考にしてください。コード体系は数字の1~0までとA~Z、CC1~CC8とスタートとストップコードで、当サイトではCC1~CC8を小文字のa~hとしてスタート、ストップを〔と〕で全て半角の1文字で扱います。この方が印刷時の変換が便利です。

当サイトでは文字を分解して判定するので、印刷時の判定では負荷が大きくなる恐れがあり、登録時に変換してデータとして登録しています。印刷時にはPXDoc得意のLINEで表現します。

間違いや、事業所コード、悪意のあるデータはうまく変換できませんので、予めPHP側の変換前処理をしていおていください。

フローチャートや変換方法を何度も読み返し、行ったり来たりのスクリプトなので綺麗ではありませんが、課題はクリアーしています。実際にスクリプトを見てもらうとわかりますが、複雑ですべてに大丈夫とは言いません。

漢字数値で「二十一」と「二一」は同じですよね。百の位は考えてません。

実際に実験して使えたら使ってください。お願いはしませんので!

計算側

/*/---------------------
	郵便バーコード計算
----------------------/*/
function bcr($yubin,$jiyushyo1,$jiyushyo2){

   //$yubin="1000013"; //100-0013でもOK
   //$jiyushyo1="東京都千代田区霞が関1丁目3番2号";
   //$jiyushyo2="郵便プラザ503室";

   //---郵便番号の前処理---
   $a1=mb_convert_kana($yubin,"as");      //郵便番号の半角変換
   $a1=preg_replace('/[^0-9]/', '', $a1); //郵便番号の数値だけ
   //---住所の前処理---
   $a2=mb_convert_kana($jiyushyo1.$jiyushyo2,"as");   //住所の英数時を半角に
   $a2=htmlspecialchars_decode($a2);                  //& などHTMLのデータがあるので戻す
   $a2=strtoupper($a2); //半角の英字小文字を大文字に
   //
   $b=preg_split("//u", $a2, -1, PREG_SPLIT_NO_EMPTY);
   $n=count($b);
   //
   $kan=array("一"=>"1","二"=>"2","三"=>"3","四"=>4,"五"=>5,"六"=>"6","七"=>"7","八"=>"8","九"=>"9","十"=>"1","百"=>"1","千"=>"1");
   $chi=array("丁","番","号","線","の","ノ");
   $h=0;
   $h1=0;
   $h2=0;
   $a3="";
   for ($i=$n;$i>=0;$i--){       //最後の文字から判別しているので注意
      $m=$b[$i];
      //print "i-$i->$m";
      if (strlen($m)==1) {       //半角
         if ($m=="&" OR $m=="/" OR $m=="." OR $m==" " ) continue;
         if (is_numeric($m)) {   //数値
            $a3=$m.$a3;
            $h=1;                //数値判定
         }else{                  //文字
            if ($m=="F") {       //前が数値の場合は無視
               if (is_numeric($b[($i-1)])) {
                  if ($h==1) {   //前も数値
                     $m="-";
                  }else{
                     $m="";
                  }
               }
            }
            if ($m=="-") {       //次が全角でも-にしない1
               $h=0;
               if ($b[($i+1)]=="-") $m="";   //続けない
            }
            $a3=$m.$a3;
         }
         $h1=0;   //半角を挟んだのでリセット
         $h2=0;
      }else{                  //全角
         if ($h==1) {         //直前が半角数値がある1
            $a3="-".$a3;
            $h=0;
         }
         if ($h2==1) {  //下の判定の次回なので行が上下している2
            if (empty($kan[$m])) {
               $h1=0;   //数字ではないのでリセット
               $h2=0;
               $a3="-".$a3;
            }else{      //漢数字  二十三=>23 となる 十三=>13
               if ($m=="十") {
                  if (empty($kan[$b[($i-1)]])) {   //次が漢数字なら
                     $a3=$kan[$m].$a3;
                  }
               }else{
                  $a3=$kan[$m].$a3;
               }
            }
         }   
         if ($m=="割") {      //2文字判定「割地」
            $h1=1;
         }
         if ($h1==1 AND $m=="地") {
            $h1=0;
            $h2=1;
         }
         foreach ($chi as $v){
            if (mb_strpos($m,$v)!==FALSE) {  //array $chiに一致するなら前は数値 この先の判定はこの上の行へ2
               $h2=1;
               break;
            }
         }
      }
   }
   //---2文字以上の連続した半角英文字---
   preg_match_all("/[A-Z]{2,}/",$a3,$ar, PREG_PATTERN_ORDER );
   $ar=$ar[0];
   $ar=array_reverse($ar); //3文字以上の場合 2文字以上、3文字以上となるので配列を逆にする。
   //---該当文字を削除--
   if (!empty($ar)) {
      $a3=str_replace($ar,"",$a3);
   } 
   // 先頭に - 残っていれば
   if (substr($a3,0,1)=="-") {
      $a3=substr($a3,1);
   }
   //---アルファベット前後の(-)文字は取除く---
   $b=str_split($a3);
   $n=count($b);
   $a4="";
   for ($i=0;$i<$n;$i++){
      $m=$b[$i];
      if ($b[$i]=="-" AND ctype_alpha($b[($i+1)])) { //いま-で次がアルファベット
         $m="";
      }
      if ($i>0 AND $b[$i]=="-") {
         if (ctype_alpha($b[($i-1)])) {               //いま-で前がアルファベット
            $m="";
         }
      }
      $a4.=$m;
   }
   /*/---------------------------
   A=CC1+0  B=CC1+1 ~J=CC1+9
   K=CC2+0  L=CC2+1   T=CC2+9
   Z=CC3+5
   仮にCC1=a CC2=b ~ CC8=hと扱う
   A=ASC(65)  a=ASC(97)
   アルファベットを2文字に変換
   -----------------------------*/
   $b=str_split($a4);
   $n=count($b);
   $a5="";
   for ($i=0;$i<$n;$i++){
      $m=$b[$i];
      if (ctype_alpha($b[$i])) { //英字/アルファベット
         $cc=chr(intval((ord($b[$i])-65)/10)+97);
         $ccx=((ord($b[$i])-65)%10);
         $m=$cc.$ccx;
      }
      $a5.=$m;
   }
   $a5=$a1.$a5.str_repeat("d", 20);  //郵便番号を足しcc4(d)で埋める
   $a5=substr($a5,0,20);             //20文字で切る
   //----チェックデジットの計算----
   $b=str_split($a5);
   $n=count($b);
   $c_di=0;
   for ($i=0;$i<$n;$i++){
      $m=$b[$i];
      if (is_numeric($m)) { //数値
         $c_di+=(int)$m;
      }else{    //数値以外 
         if ($m=="-") {
            $c_di+=10;    
         }else{
            $c_di+=ord($b[$i])-86;  //ASC(a)=97  97-86=11  
         }
      }
   }
   $c_dix=(ceil($c_di/19)*19)-$c_di;
   $v_di=array("0","1","2","3","4","5","6","7","8","9","-","a","b","c","d","e","f","g","h");
   $a5.=$v_di[$c_dix];  //計算結果
   //----できた!-- スタートコードとストップコードを付加
   return "[".$a5."]";
}

PXDoc側です。この部分は印刷可能と思いますが、プリントしかしません。最初は図形処理でつくったのですが、粗かったのでLINEの組み合わせです。

1つのコードが縦3本の4種類なので数字1を114として・・バーコード表の通りですが

/*/---------------------
	PXDOC印刷
----------------------/*/
function prt($bd_data){

   //--A4横--
   $attach_data='<?xml version="1.0" encoding="UTF-8" ?>
   <pxd paper-type="A4" orientation="landscape" title="BCR" auto-print="false" xmlns:xlink="http://www.w3.org/1999/xlink" name="Pxd" >';
   //------頁-----
      $attach_data.= '<page>
      <svg x="0" y="0" width="297mm" height="210mm" viewBox="0 0 2970 2100">'; 
      //---変換デーブル---
       $ary=array("1"=>"114","2"=>"132","3"=>"312","4"=>"123","5"=>"141","6"=>"321","7"=>"213","8"=>"231","9"=>"411","0"=>"144",
                     "-"=>"414","a"=>"324","b"=>"342","c"=>"234","d"=>"432","e"=>"243","f"=>"423","g"=>"441","h"=>"111","A"=>"324144",
                     "B"=>"324114","C"=>"324132","D"=>"324312","E"=>"324123","F"=>"324141","G"=>"324321","H"=>"324213","I"=>"324231",
                     "J"=>"324411","K"=>"342144","L"=>"342114","M"=>"342132","N"=>"342312","O"=>"342123","P"=>"342141","Q"=>"342321",
                     "R"=>"342213","S"=>"342231","T"=>"342411","U"=>"234144","V"=>"234114","W"=>"234132","X"=>"234312","Y"=>"234123",
                     "Z"=>"234141","["=>"013","]"=>"310");

         //---- バーコード----- 
         $t_var=str_split($bd_data);
         $n=count($t_var);
         $bcr="";
         $y=510;
         $x=290;
         for ($i=0;$i<$n;$i++){    
            $b_var=str_split($ary[$t_var[$i]]); //324321などをさらに分解
            foreach ($b_var as $v){
               $data[0]='';
               $data[1]='<line x1="'.$x.'" y1="'.$y.'" x2="'.$x.'" y2="'.($y+36).'" stroke="black" stroke-width="7"/>';
               $data[2]='<line x1="'.$x.'" y1="'.$y.'" x2="'.$x.'" y2="'.($y+24).'" stroke="black" stroke-width="7"/>';
               $data[3]='<line x1="'.$x.'" y1="'.($y+12).'" x2="'.$x.'" y2="'.($y+36).'" stroke="black" stroke-width="7"/>';
               $data[4]='<line x1="'.$x.'" y1="'.($y+12).'" x2="'.$x.'" y2="'.($y+24).'" stroke="black" stroke-width="7"/>';
               $bcr.=$data[$v];
               $x=$x+12;
            }
         }  
         $attach_data .=$bcr;
         $attach_data .='</svg>
         </page>
        </pxd>';
   print $attach_data;
}

チェックデジットも計算します。

全体はこんな感じ

<?php

/*/---------------------
	郵便バーコード計算
----------------------/*/
function bcr($yubin,$jiyushyo1,$jiyushyo2){

   //$yubin="1000013"; //100-0013でもOK
   //$jiyushyo1="東京都千代田区霞が関1丁目3番2号";
   //$jiyushyo2="郵便プラザ503室";

   //---郵便番号の前処理---
   $a1=mb_convert_kana($yubin,"as");      //郵便番号の半角変換
   $a1=preg_replace('/[^0-9]/', '', $a1); //郵便番号の数値だけ
   //---住所の前処理---
   $a2=mb_convert_kana($jiyushyo1.$jiyushyo2,"as");   //住所の英数時を半角に
   $a2=htmlspecialchars_decode($a2);                  //&amp; などHTMLのデータがあるので戻す
   $a2=strtoupper($a2); //半角の英字小文字を大文字に
   //print "--a2-->$a2<br>";
   //
   //print "--c-->";
   $b=preg_split("//u", $a2, -1, PREG_SPLIT_NO_EMPTY);
   $n=count($b);
   //
   $kan=array("一"=>"1","二"=>"2","三"=>"3","四"=>4,"五"=>5,"六"=>"6","七"=>"7","八"=>"8","九"=>"9","十"=>"1","百"=>"1","千"=>"1");
   $chi=array("丁","番","号","線","の","ノ");
   $h=0;
   $h1=0;
   $h2=0;
   $a3="";
   for ($i=$n;$i>=0;$i--){       //最後の文字から判別しているので注意
      $m=$b[$i];
      //print "i-$i->$m";
      if (strlen($m)==1) {       //半角
         if ($m=="&" OR $m=="/" OR $m=="." OR $m==" " ) continue;
         if (is_numeric($m)) {   //数値
            $a3=$m.$a3;
            $h=1;                //数値判定
         }else{                  //文字
            if ($m=="F") {       //前が数値の場合は無視
               if (is_numeric($b[($i-1)])) {
                  if ($h==1) {   //前も数値
                     $m="-";
                  }else{
                     $m="";
                  }
               }
            }
            if ($m=="-") {       //次が全角でも-にしない1
               $h=0;
               if ($b[($i+1)]=="-") $m="";   //続けない
            }
            $a3=$m.$a3;
         }
         $h1=0;   //半角を挟んだのでリセット
         $h2=0;
      }else{                  //全角
         if ($h==1) {         //直前が半角数値がある1に
            $a3="-".$a3;
            $h=0;
         }
         if ($h2==1) {  //下の判定の次回なので行が上下している2
            if (empty($kan[$m])) {
               $h1=0;   //数字ではないのでリセット
               $h2=0;
               $a3="-".$a3;
            }else{      //漢数字  二十三=>23 となる 十三=>13
               if ($m=="十") {
                  if (empty($kan[$b[($i-1)]])) {   //次が漢数字なら
                     $a3=$kan[$m].$a3;
                  }
               }else{
                  $a3=$kan[$m].$a3;
               }
            }
         }   
         if ($m=="割") {      //2文字判定「割地」
            $h1=1;
         }
         if ($h1==1 AND $m=="地") {
            $h1=0;
            $h2=1;
         }
         foreach ($chi as $v){
            if (mb_strpos($m,$v)!==FALSE) {  //array $chiに一致するなら前は数値 この先の判定はこの上の行へ2
               $h2=1;
               break;
            }
         }
      }
   }
   //---2文字以上の連続した半角英文字---
   preg_match_all("/[A-Z]{2,}/",$a3,$ar, PREG_PATTERN_ORDER );
   $ar=$ar[0];
   $ar=array_reverse($ar); //3文字以上の場合 2文字以上、3文字以上となるので配列を逆にする。
   //---該当文字を削除--
   if (!empty($ar)) {
      $a3=str_replace($ar,"",$a3);
   } 
   // 先頭に - 残っていれば
   if (substr($a3,0,1)=="-") {
      $a3=substr($a3,1);
   }
   //---アルファベット前後の(-)文字は取除く---
   $b=str_split($a3);
   $n=count($b);
   $a4="";
   for ($i=0;$i<$n;$i++){
      $m=$b[$i];
      if ($b[$i]=="-" AND ctype_alpha($b[($i+1)])) { //いま-で次がアルファベット
         $m="";
      }
      if ($i>0 AND $b[$i]=="-") {
         if (ctype_alpha($b[($i-1)])) {               //いま-で前がアルファベット
            $m="";
         }
      }
      $a4.=$m;
   }
   /*/---------------------------
   A=CC1+0  B=CC1+1 ~J=CC1+9
   K=CC2+0  L=CC2+1   T=CC2+9
   Z=CC3+5
   仮にCC1=a CC2=b ~ CC8=hと扱う
   A=ASC(65)  a=ASC(97)
   アルファベットを2文字に変換
   -----------------------------*/
   $b=str_split($a4);
   $n=count($b);
   $a5="";
   for ($i=0;$i<$n;$i++){
      $m=$b[$i];
      if (ctype_alpha($b[$i])) { //英字/アルファベット
         $cc=chr(intval((ord($b[$i])-65)/10)+97);
         $ccx=((ord($b[$i])-65)%10);
         $m=$cc.$ccx;
      }
      $a5.=$m;
   }
   $a5=$a1.$a5.str_repeat("d", 20);  //郵便番号を足しcc4(d)で埋める
   $a5=substr($a5,0,20);             //20文字で切る
   //----チェックデジットの計算----
   $b=str_split($a5);
   $n=count($b);
   $c_di=0;
   for ($i=0;$i<$n;$i++){
      $m=$b[$i];
      if (is_numeric($m)) { //数値
         $c_di+=(int)$m;
      }else{    //数値以外 
         if ($m=="-") {
            $c_di+=10;    
         }else{
            $c_di+=ord($b[$i])-86;  //ASC(a)=97  97-86=11  
         }
      }
   }
   $c_dix=(ceil($c_di/19)*19)-$c_di;
   $v_di=array("0","1","2","3","4","5","6","7","8","9","-","a","b","c","d","e","f","g","h");
   $a5.=$v_di[$c_dix];  //計算結果
   //----できた!-- スタートコードとストップコードを付加
   return "[".$a5."]";
}
/*/---------------------
	PXDOC印刷
----------------------/*/
function prt($bd_data){

   //--A4横--
   $attach_data='<?xml version="1.0" encoding="UTF-8" ?>
   <pxd paper-type="A4" orientation="landscape" title="BCR" auto-print="false" xmlns:xlink="http://www.w3.org/1999/xlink" name="Pxd" >';
   //------頁-----
      $attach_data.= '<page>
      <svg x="0" y="0" width="297mm" height="210mm" viewBox="0 0 2970 2100">'; 
      //---変換デーブル---
       $ary=array("1"=>"114","2"=>"132","3"=>"312","4"=>"123","5"=>"141","6"=>"321","7"=>"213","8"=>"231","9"=>"411","0"=>"144",
                     "-"=>"414","a"=>"324","b"=>"342","c"=>"234","d"=>"432","e"=>"243","f"=>"423","g"=>"441","h"=>"111","A"=>"324144",
                     "B"=>"324114","C"=>"324132","D"=>"324312","E"=>"324123","F"=>"324141","G"=>"324321","H"=>"324213","I"=>"324231",
                     "J"=>"324411","K"=>"342144","L"=>"342114","M"=>"342132","N"=>"342312","O"=>"342123","P"=>"342141","Q"=>"342321",
                     "R"=>"342213","S"=>"342231","T"=>"342411","U"=>"234144","V"=>"234114","W"=>"234132","X"=>"234312","Y"=>"234123",
                     "Z"=>"234141","["=>"013","]"=>"310");

         //---- バーコード----- 
         $t_var=str_split($bd_data);
         $n=count($t_var);
         $bcr="";
         $y=510;
         $x=290;
         for ($i=0;$i<$n;$i++){    
            $b_var=str_split($ary[$t_var[$i]]); //324321などをさらに分解
            foreach ($b_var as $v){
               $data[0]='';
               $data[1]='<line x1="'.$x.'" y1="'.$y.'" x2="'.$x.'" y2="'.($y+36).'" stroke="black" stroke-width="7"/>';
               $data[2]='<line x1="'.$x.'" y1="'.$y.'" x2="'.$x.'" y2="'.($y+24).'" stroke="black" stroke-width="7"/>';
               $data[3]='<line x1="'.$x.'" y1="'.($y+12).'" x2="'.$x.'" y2="'.($y+36).'" stroke="black" stroke-width="7"/>';
               $data[4]='<line x1="'.$x.'" y1="'.($y+12).'" x2="'.$x.'" y2="'.($y+24).'" stroke="black" stroke-width="7"/>';
               $bcr.=$data[$v];
               $x=$x+12;
            }
         }  
         $attach_data .=$bcr;
         $attach_data .='</svg>
         </page>
        </pxd>';
   print $attach_data;
}

//----main-----
$a="1980036"; //198-0036でもOK
$b="東京都青梅市河辺町十一丁目六番地一号";
$c="郵便タワー601";

//---計算---
$bcr=bcr($a,$b,$c);
print $bcr;
/* [198003611-6-1-601dddh] と表示されるはず。
   郵便局の例題の答えと同じ
   STC 198003611-6-1-601 CC4 CC4 CC4 CC8 SPC
*/   
//----印刷----
//画面にプリントします。
prt($bcr);


?>

このような表示になります。

お役に立てました?