Noël Café : Blog
Noëlの公開備忘録とひとりごと。コメント・トラックバックご自由に

Google Apps Scriptでbase64をbase10へ変換しかも1E+17以上

短縮URL等で使われるbase64(64進法)表記。
base10(10進法)へ戻したいことがたまにあります。
ただ、戻そうとしたら大きな数字(17桁以上)で、
計算に失敗するという。
かなり?苦戦したのでメモとして残したいと思います。
Google Apps Script (GAS)になります。

やりたいこと

  • GASでbase64をbase10に変換
  • base10にした時に17桁以上(2^54以上)でも対応

POINT

  1. 1文字ずつ変換
  2. 64進法から10進法に変換したらすぐに.ToFixed(0).ToString()
  3. 10進法の数字は下から10桁くらいずつ分けて処理、最後に結合

GAS スクリプト

引数はst_base64(64進法で書かれた文字列)、
戻り値は st_base10(10進法に換算して文字列にしたもの)
になります。

POINTの項にほとんど肝は書いてしまったので、
スクリプトを一気書きして終了にします。

function fn_base64url2base10str(st_base64){

  // base64urlをbase10(文字列)へ変換
  // Reference : https://ja.loveblade.org/361152-where-do-i-find-the-JVZRBG
  var base64url_chars = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_’;
  var split_digit = 10;     // 計算時に大きな数字を避けるためこの桁数ごとに配列ar_base10に格納して計算させる(<16)
  var st_base10;       // 最終出力 10進法にした文字列
  var ar_base10 = [];  // split_digit桁ごと分けて格納する(桁数対応)。最後にjoinで繋げる↑
  var st_base10_i;     // base64のi文字目→base10に変換 * 64^桁目
  var st_base10_i_j;   // 上の文字の下からsplit_digit桁ずつの数字

  for (var i = 0; i < st_base64.length ; i++){
    // 上から1桁ずつ変換。桁も考慮して一気にbase10へ *POINT1*, *POINT2*
    st_base10_i = (base64url_chars.indexOf(st_base64[i]) * Math.pow(64, (st_base64.length – i – 1))).toFixed(0).toString();

    // 下から10桁ずつar_base10に格納 *POINT3*
    for (var j = 1; j <= Math.ceil(st_base10_i.length / split_digit); j++ ){ 
      if (j > ar_base10.length){
        // ar_base10の要素数が足りない場合は頭に0を入れる
        ar_base10.unshift(0);
      }
      if (j == Math.ceil(st_base10_i.length / split_digit) && st_base10_i.length % split_digit > 0){
        // 頭の桁で且つst_base10_iの桁数がsplit_digitで割り切れない場合
        // 先頭から必要な桁を取り出す
        st_base10_i_j = st_base10_i.substr(0, st_base10_i.length % split_digit);
      }else{
        // 途中のsplit_digit桁を取り出す
        st_base10_i_j = st_base10_i.substr(st_base10_i.length – j * split_digit, split_digit);
      }
      
      // 同じ桁同士を足す
      ar_base10[ar_base10.length – j] += Number(st_base10_i_j);

      // 足し算をした結果10^split_digit以上の場合は繰り上げ処理する(一個手前の要素に足す)
      if (ar_base10[ar_base10.length – j] >= Math.pow(10,split_digit)){
        if (j + 1 > ar_base10.length){
          // ar_base10の要素数が足りない場合は頭に0を入れる
          ar_base10.unshift(0);
        }
        ar_base10[ar_base10.length – j – 1] += Math.floor(ar_base10[ar_base10.length – j]/Math.pow(10,split_digit));
        ar_base10[ar_base10.length – j] = ar_base10[ar_base10.length – j] % Math.pow(10,split_digit);
      }

    }

  }

  // ar_base10[0]は文字列に、他はsplit_digit桁の文字列に直す。  
  // (ただ繋げると桁が足りない時NGなので)
  for(i = 0; i < ar_base10.length; i++){ // 文字列に直す。 ar_base10[i] = ar_base10[i].toString(); // i=0でない場合は10桁にする if (i > 0){
      ar_base10[i] = (“00000000000000000000” + ar_base10[i]).slice(-1*split_digit);
    }
  }

  // ar_base10の配列を繋げて完成
  st_base10 = ar_base10.join(“”);

//  Logger.log(st_base10);
  return st_base10;

}

Reference

余談

これ、Instagramのmedia-idをURLから取得するために取り掛かりました。
しかし、目的のjsonがエラーできて取得できない事件(笑)!
とりあえず、成果物としてUPします。

Leave a Reply

*


CAPTCHA



Trackback URL