読者です 読者をやめる 読者になる 読者になる

正規表現がよく使えない子のsprintfの実装

sprintfをJavaScriptで利用できるように頑張って移植してたとき正規表現を一杯つかって実装していたわけですが…僕の頭では正規表現で実装できなくて別の方法をとった覚えがあります。そのときのソースコードの展開。sprintfの修飾子(?)(%からはじまる奴)で数字関係の処理がよく分かっていなくてバグがかなりひそんでいるハズです…1文字変数多用しています。

sprintf('%02d', 1); // 01
sprintf("%'#5.3s", 'Hello World!!') // ##Hel

sprintfのソース

function sprintf(s)
{
     var i, a, c, v, m, p, t, n, f, k = 1, b = s.split('');

     for(i=0,f=b.length;i<=f;i++)
     {
          if(b[i] == '%')
          {
               v = sprintf.arguments[k++];
               m = n = c = '', a = 0, p = ' ';

               switch(typeof(v))
               {
                    case 'object':
                         if(v == null){ v = ''; };
                         break;
                    case 'undefined':
                         throw new Error('too few arguments');
                         break;
               };

               do
               {
                    t = b[i], b[i++] = '';
                    switch(t)
                    {
                         case '$':
                              v = sprintf.arguments[n];
                              n = '';
                              break;
                         case '.':
                              if(n == ''){ n = 0; };
                              c = b[i], b[i++] = '';
                              break;
                         case '%':
                              t = b[i];
                              break;
                         case '+':
                              if(m == ''){ m = '+'; };
                              break;
                         case '-':
                              a = 1;
                              break;
                         case "'":
                              p = b[i], b[i++] = '';
                              break;
                         case '0':
                              if(n == ''){ p = t; break; };
                              if(c == ''){ n+= t; } else { c+= t; };
                              break;
                         case '1':
                         case '2':
                         case '3':
                         case '4':
                         case '5':
                         case '6':
                         case '7':
                         case '8':
                         case '9':
                              if(c == ''){ n += t; } else { c += t; };
                              break;
                         default:
                              i--;
                              break;
                    };
               }
               while('#' < t && t < ':');

               n = (n == '') ? 0 : Number(n);
               if(!isNaN(Number(v)))
               {
                    if(v < 0){ m = ''; };
                    if(c != ''){ v = v.toFixed(c); };
               };

               switch(t)
               {
                    case '%': k--; b[i] = t; continue;
                    case 'F': v = v.toFixed(6); break;
                    case 'f': v = v.toFixed(6); break;
                    case 'o': v = v.toString(8); break;
                    case 'u': v = v.toString(10); break;
                    case 'd': v = v.toString(10); break;
                    case 'c': v = String.fromCharCode(v); break;
                    case 'b': v = parseInt(v).toString(2); break;
                    case 'x': v = v.toString(16).toLowerCase(); break;
                    case 'X': v = v.toString(16).toUpperCase(); break;
                    case 'e': v = parseInt(v).toExponential(c || 5); break;
                    case 's': m = ''; if(c != '') v = v.slice(0, c); break;
               };

               if(v.length < n)
               {
                    if(a) v = v + Array(n - v.length - m.length + 1).join(p);
                    else  v = Array(n - v.length - m.length + 1).join(p) + v;
               };

               b[i] = m + v;
          };
     };

     return b.join('');
};