プリント基板設計・シミュレーション

TOP > アポロレポート > コラム > 第99回 プログラミングについて
コラム
2025/02/28

第99回 プログラミングについて

アポロレポート
今回はバーコードについて考えてみましょう。バーコードと言えば今では日常当たり前のように目にする白黒のしましまです。

 私が最初にバーコードを気にするようになったのは大学4年の頃だったと思います。当時卒論を一緒にやっていたM氏(彼は富士通に就職しました)が、『最近バーコードに興味があるんだ』ということを言っていたことが妙に頭に残っていました。当時私はコンピュータなどには興味がなかったので、バーコードにはなにか暗号のようなものが書かれているんだろうなと漠然と感じた程度でした。
 就職後何年かが過ぎ、私のいた部署の課長が仕事の管理をバーコードを使ってやりたいと言い出したことがありました。当時の私の部署はプリント基板CADを扱ってプリント基板製造用のデータを作ることが仕事でした。設計の部署からデータ作成の依頼を受けてから最終データにするまでの工程がどのプリント基板についても同じでしたので、この工程の管理をコンピュータで行なっていました。コンピュータで行なうと言っても各工程の進み具合を人間が入力していたのです(このソフトは私が作りました)。これをなんとかバーコードを使う事で簡素化しようという魂胆なのです。最終的にはこの計画は諸々の事情があって実現はしなかったのですが、このときにバーコードの勉強をしたことをお話しします。

 バーコードと言えば誰でもが知っている一般化したものですが、いざこの内容がどうなっているのかまでを知っている人はあまり多くはありません。このバーコードに書かれている内容は特別な暗号でもなんでもなく数値や文字列なのです。書き込みたいデータの内容や用途でバーコードに使うコード体系もいろいろあります。

 バーコードは黒の線と空間とで成り立っています。ここでは空間のことを白の線と考えましょう。いま0~9までの10種類の数値を表現するとします。10種類の値が表現できればいいのであれば、コンピュータでは4ビットあればおつりがきます。この数値をビットで表現すると、

   下位  →  上位
 0 0 0 0 0
 1 1 0 0 0
 2 0 1 0 0
 3 1 1 0 0
 4 0 0 1 0
 5 1 0 1 0
 6 0 1 1 0
 7 1 1 1 0
 8 0 0 0 1
 9 1 0 0 1

というようになります。簡単に言えばこれをバーコードにしたいとすれば、1つの数値に使用する黒い線を4本と決めておき、上の図で0のビットは細い線、1のビットは太い線で表現すればいいということになります。もしれをバーコードにしてみると、

* * * * ** * * * * ** * * ** ** * * * * ** * ** * ** * * ** ** * ** ** ** *
* * * * ** * * * * ** * * ** ** * * * * ** * ** * ** * * ** ** * ** ** ** *
* * * * ** * * * * ** * * ** ** * * * * ** * ** * ** * * ** ** * ** ** ** *
* * * * ** * * * * ** * * ** ** * * * * ** * ** * ** * * ** ** * ** ** ** *
* * * * ** * * * * ** * * ** ** * * * * ** * ** * ** * * ** ** * ** ** ** *
* * * * ** * * * * ** * * ** ** * * * * ** * ** * ** * * ** ** * ** ** ** *
0    1    2    3     4    5     6     7

というようなものになるはずです。

 これをプログラムにしてみると、

 #include <stdio.h>

 main()
 {
  char *data="0123456789";
  int i,j,k,v;

  for(k=0;k<5;k++){
   for(i=0;data[i];i++){
    v=data[i]-'0';

    for(j=0;j<4;j++){
     if(v & (1<<j))printf("** ");
     else     printf("* " );
    }
   }

   printf("\n");
  }
 }

となります。

 実際にはこのような単純な形式のものはありませんが、おおよその理屈はこれで理解できると思います。それでは実際に0~9までの値を扱うバーコードをお話ししましょう。先程もお話ししたように数値だけの場合は4ビットあれば値は表現できますが、実際にはこれにパリティビットを1つ付加し、全体で5ビットにします。そしてビットがオン(黒の線が太くなる)になる数を2つにするバーコードの体系が2of5のコード系で、『ツーオブファイブ』と呼びます。
 ビットがオンの数が2ということは、上の図では7の値が表現できず、またパリティビットも含みますので、数値のビットを単純にバーにする訳にはいきません。そこで2of5系では、

   下位 → 上位 パリティ
 0 0 0 1 1 0
 1 1 0 0 0 1
 2 0 1 0 0 1
 3 1 1 0 0 0
 4 0 0 1 0 1
 5 1 0 1 0 0
 6 0 1 1 0 0
 7 0 0 0 1 1
 8 1 0 0 1 0
 9 0 1 0 1 0

というように決めています。0のときには何もビットがオンにならないので、他の数値では使っていない値にし、7のところでビット数が3以上になってしまうので、この値以上のときには1を加えて調整しているようです。この図から判断すると偶数パリティですね。偶数パリティというのは以前にもお話ししましたように、すべてのビット数が偶数になるようにビットを付加するものです。

 実際にバーコードにするときには、バーコードを逆さまに読んでしまうことがありますので、どっちが先頭か末尾かを示すコードもバーコードの先頭と末尾に付加します。
もう1つチェックサムキャラクタというものがあり、総文字数を偶数にするか奇数にするかで意味のない0を1文字付加することもありますがここでは省略して、データそのものの部分について考えていきます。

これをプログラムにしてみると、

 #include <stdio.h>

 main()
 {
  char *data="0123456789";
  int i,j,k,n,v;

  for(k=0;k<5;k++){
   for(i=0;data[i];i++){
    v=data[i]-'0';

       if(v==0)v=12;
    else if(v>=7)v++;

    n=0;

    for(j=0;j<4;j++){
     if(v & (1<<j)){
      printf("##.");
      n++;
     }
     else{
      printf("#." );
     }
    }

    if(n%2==1)printf("##.");
    else   printf("#." );
   }

   printf("\n");
  }
 }

 となります。この例は各値を上の表に合う様に操作し、パリティビットも各値のビット数を数えて立てるようにしていますが、それぞれの値をパリティビットも含めてテーブルにしておく方がプログラムは単純になるかも知れません。このプログラムの出力は次のようになります(6以降は省略してあります)。

#.#.##.##.#.##.#.#.#.##.#.##.#.#.##.##.##.#.#.#.#.#.##.#.##.##.#.##.#.#
#.#.##.##.#.##.#.#.#.##.#.##.#.#.##.##.##.#.#.#.#.#.##.#.##.##.#.##.#.#
#.#.##.##.#.##.#.#.#.##.#.##.#.#.##.##.##.#.#.#.#.#.##.#.##.##.#.##.#.#
#.#.##.##.#.##.#.#.#.##.#.##.#.#.##.##.##.#.#.#.#.#.##.#.##.##.#.##.#.#
#.#.##.##.#.##.#.#.#.##.#.##.#.#.##.##.##.#.#.#.#.#.##.#.##.##.#.##.#.#
0      1      2      3      4      5     

 ここまでは理解できたと思います。このバーコードの形式は Industrial 2 of 5 コードと呼ばれているものなのです。
 さて、ここまでのバーコードを見てみると10文字の数値のみなのにやけに長いように思いませんか。そうです本当に長いのです。そこで昔の人は考えたのでした。これまでは、黒の線だけを太いものと細いものにしていましたが、白の線は一定幅としていました。それならば、この単なる区切りにしか使用していなかった白線にも情報を付けてしまおうということになったのです。

 いま Industrial 2 of 5 コードの 0 は、

 0 0 1 1 0
 | | | | |
 #.#.##.##.#.
 #.#.##.##.#.
 #.#.##.##.#.
 #.#.##.##.#.
 #.#.##.##.#.

となっていました。これを、

 001 1 0
 ||| | |
 #.##..#.
 #.##..#.
 #.##..#.
 #.##..#.
 #.##..#.

と、白線にも太いものと細いものを使用すればバーコードの密度が高くなるという理屈です。このような方式でも5本の線の中の2本が太いので 2 of 5 コードです。
これをプログラムにしてみると、

 #include <stdio.h>

 main()
 {
  char *data="0123456789";
  int i,j,k,n,v;

  for(k=0;k<5;k++){
   for(i=0;data[i];i++){
    v=data[i]-'0';
       if(v==0)v=12;
    else if(v>=7)v++;

    n=0;

    for(j=0;j<4;j++){
     if(v & (1<<j)){
      if(j%2==0)printf("##");
      else   printf("..");
      n++;
     }
     else{
      if(j%2==0)printf("#");
      else   printf(".");
     }
    }

    if(n%2==1)printf("##.");
    else   printf("#." );
   }

   printf("\n");
  }
 }

となります。もっと簡単になると思いますので試してみて下さい。このプログラムの実行結果は、

#.##..#.##.#.##.#..#.##.##..#.#.#.##.##.##.##.#.#..##.#.#.#..##.##.#..#.#..#..#.
#.##..#.##.#.##.#..#.##.##..#.#.#.##.##.##.##.#.#..##.#.#.#..##.##.#..#.#..#..#.
#.##..#.##.#.##.#..#.##.##..#.#.#.##.##.##.##.#.#..##.#.#.#..##.##.#..#.#..#..#.
#.##..#.##.#.##.#..#.##.##..#.#.#.##.##.##.##.#.#..##.#.#.#..##.##.#..#.#..#..#.
#.##..#.##.#.##.#..#.##.##..#.#.#.##.##.##.##.#.#..##.#.#.#..##.##.#..#.#..#..#.
0    1    2    3    4    5    6    7    8    9

となり、なんとか1行に納まりました。かなりコードが圧縮されているのが分かりますね。このバーコードの方式は Matrix 2 of 5 と呼ばれています。

 これで大体満足なのですが、昔の人はもっと密度が高くならないかと考えたのでした。
一体どうすればもっと密度が高くなるのかというと、上のバーコードでは数値と数値の間の空白が単なる区切りとしか使用されていないので、これをなくしてしまおうというのです。これには私もなるほどと感心させられました。

 2 of 5 コードの 0 と 1 のビットパターンは次の通りです。

 000110 110001
 0   1

この2つの数値を1つの組として、最初のものを黒線、次のものを白線にしてしまえばその次の数値の組の間に区切りとしての白線が必要なくなるのです。そこで、この場合は、

 0 0 0 1 1 0 --- 0
 | | | | | |
 #..#..#.##.##.#..
  | | | | | |
  1 1 0 0 0 1 --- 1

というようにするのです。

 これをプログラムにしてみると、

 #include <stdio.h>

 main()
 {
  char *data="0123456789";
  int i,j,k,l,v[2],n[2];

  for(k=0;k<5;k++){
   for(i=0;data[i];i+=2){
    for(l=0;l<=1;l++){
     v[l]=data[i+l]-'0';
        if(v[l]==0)v[l]=12;
     else if(v[l]>=7)v[l]++;

     n[l]=0;
    }

    for(j=0;j<4;j++){
     for(l=0;l<=1;l++){
      if(v[l] & (1<<j)){
       if(l==0)printf("##");
       else  printf("..");
       n[l]++;
      }
      else{
       if(l==0)printf("#");
       else  printf(".");
      }
     }
    }

    for(l=0;l<=1;l++){
     if(n[l]%2==1){
      if(l==0)printf("##");
      else  printf("..");
     }
     else{
      if(l==0)printf("#");
      else  printf(".");
     }
    }
   }

   printf("\n");
  }
 }

となり、この結果は、

0       2       4       6       8
#..#.##.##.#..#..##..#.#.##.#..#.##..#.##.#.##.##.#..#..##.#..#.##..#.
#..#.##.##.#..#..##..#.#.##.#..#.##..#.##.#.##.##.#..#..##.#..#.##..#.
#..#.##.##.#..#..##..#.#.##.#..#.##..#.##.#.##.##.#..#..##.#..#.##..#.
#..#.##.##.#..#..##..#.#.##.#..#.##..#.##.#.##.##.#..#..##.#..#.##..#.
#..#.##.##.#..#..##..#.#.##.#..#.##..#.##.#.##.##.#..#..##.#..#.##..#.
1       3       5       7       9

となります。ちょっと分かりづらいかも知れませんが、確かにバーコードが短くなっています。この方式を Interleaved 2 of 5 と呼びます。この方式では数字の数が奇数個では表現ができないので、奇数個のときには最初の1つ目にダミーとして0を入れ偶数個にします。

 このようにバーコードは短いパターンの中にどれだけの情報を詰め込めるかの工夫がなされているのです。今回は 2 of 5 コードしか紹介しませんでしたが、この他に2 of 7、3 of 9、JAN などのコード系があり、表現できる文字の種類に違いがあります。私が普通買い物をしている商品に付いているバーコードは、JAN コードが殆どのようです。興味のある方はバーコードのコード体系を調べてみると面白いと思います。また買い物をしているときに、『ははあ、これはXXコードだな』と眺めるのもちょっとだけ買い物が楽しくなるかも知れません。

 上のプログラムを予めテーブルにビットパターンを定義しておくものに置き換えてみましたので参考にして下さい。だいぶ簡潔になっていますので比較してみるのも面白いと思います。

 #include <stdio.h>

 main()
 {
  char tbl[10][5]={
      0,0,1,1,0, 1,0,0,0,1,
      0,1,0,0,1, 1,1,0,0,0,
      0,0,1,0,1, 1,0,1,0,0,
      0,1,1,0,0, 0,0,0,1,1,
      1,0,0,1,0, 0,1,0,1,0};

  char *data="0123456789";

  int i,j,k,l,v[2];
  char c;

  for(k=0;k<5;k++){
   for(i=0;data[i];i+=2){
    v[0]=data[i ]-'0';
    v[1]=data[i+1]-'0';

    for(j=0;j<5;j++){
     c='#';
     for(l=0;l<=1;l++){
      if(tbl[v[l]][j])printf("%c%c",c,c);
      else      printf("%c" ,c );

      c='.';
     }
    }
   }

   printf("\n");
  }
 }

 最近はもっとデータの密度の高い2次元コードが考案されていてテレビのニュースでやっていましたが、まだ実用化はされていないようです。このコードの資料がないのでお話しできないのが残念です。

 それではまた次回。

そのお悩み、
アポロ技研に話してみませんか?

アポロ技研でワンランク上の物創りへ!
そのお悩み、アポロ技研に話してみませんか?