2013年11月20日水曜日

文字の先頭をゼロ埋めして桁数を揃える。



他のプログラム言語とかでもよく出てくる「ゼロパディング」といわれる問題。


例えば5桁に揃えたい場合は、
 「123」→「00123」
 「AB」 →「000AB」
みたいな感じに加工する方法。



ゼロ埋め対象が数値変数の場合

data DT1;
   length V1 $5.;
   V0 = 123;
   V1 = put(V0, z5.); 
run;

「Zw.dフォーマット」というのが用意されてて「z桁数」と指定するだけでok。




ゼロ埋め対象が文字変数の場合

data DT2;
   length V0 $3. V1-V3 $5.;
   V0 = "ABC";
   V1 = reverse(substr(reverse(cats("00000",V0)),1,5));  * 方法① ; 
   V2 = cats(repeat("0",4-lengthn(V0)),V0);                    * 方法② ; 
   V3 = tranwrd(put(V0, 5.-R),' ','0');                             * 方法③ ; 
run;


だいたい上記3つのやり方が思い浮かぶ。

方法①
ごり押し感あるやり方。
「cats関数」で「00000ABC」という文字にする。
「reverse関数」で「CBA00000」と逆にしてから、
「substr関数」で先頭5文字を抽出「CBA00」
また「reverse関数」でもとの順に戻せば完成。
非効率だけどきらいじゃない。

追記
上記と同じ原理、かつもっといい方法を教えてもらいました。

  V1 = put(put('00000'||V0 ,$revers8.),$revers5.);

put関数にも文字を逆さまにする「$revers」というformatがある。
うまいのが「$revers5」で、5文字に切りつつ、もとの順に戻してるところ。




方法②
変数「V0」から不足してる文字の長さ分だけ「repeat関数」を使ってゼロを埋めてる。


方法③
個人的には使ってる関数も少なくて一番スマートだと思う。
数値変数の場合でも適用可。

ポイントは「put関数」で5文字の長さにして「-R」を使って文字を右詰して「  ABC」としてるところ
その後、左側に出来た半角スペースを「tranwrd関数」でゼロに置換えてる。

「put関数」の「-R」については PUT関数の小技 を参照。




6 件のコメント:

  1. 何回もコメントしてすみません。全然無視していただいて結構です。
    面白い面白くないで言うならば、方法①が一番面白いです!そして一番好きです!
    reverse関数を実用的に使用して、うまく活きている例を初めてみました。

    返信削除
  2. いっぱいコメント貰えると嬉しいです!
    方法①、いいですよね!
    むかし実務でゼロ埋めする機会があって、
    初めてやったのが①の方法なので、思い出深いです笑。

    返信削除
  3. 度々、すみません、方法①と似た考えで使用する関数の数を減らしたバージョンを
    考えてみました。といっても、なんかこのやり方、どっかで見た気がするんで、パクリかも
    しれません。
    フォーマット名が長すぎて、すっきり感がでないので、やっぱり③ですかね

    data DT2;
    length V0 $3. V1_ $5.;
    V0 = "ABC";
    V1_=put(put('00000'||V0 ,$revers8.),$revers5.);
    run;

    返信削除
  4. これはうまい!
    reverseがこんなに活かされてるの初めて見たかもしません。
    非常にスマートなやり方です!

    ありがとうございます!

    返信削除
  5. 欠損値のときと 先頭がスペース埋めのときなどに上手くいかなかったパターンがあったので、魔改造 + 新しい方法も考えてみました。



    /* テストデータ */
    data TEST ;
    length V0 $5. ;
    V0 = "A" ; output ;
    V0 = "AB" ; output ;
    V0 = "ABC" ; output ;
    V0 = "ABCD" ; output ;
    V0 = "ABCDE" ; output ;
    V0 = "" ; output ; /* 欠損値 */
    V0 = "12345" ; output ;
    V0 = "1 3 5" ; output ; /* 間にスペースあり */
    V0 = "12 45" ; output ; /* 間にスペースあり */
    V0 = "1 3" ; output ; /* 間にスペースあり */
    V0 = " 234" ; output ; /* 先頭にスペースあり */
    run ;


    data TEST ;
    set TEST ;
    length V2 V4 V5 $5. ;

    /* 1. 先頭がスペースのとき、先頭スペースが残ったままにならないように、left関数を追加 */
    /* 2. 欠損値のとき、ゼロが1つ少なかったので、cmiss関数を追加して対策 */
    /* 3. ゼロ埋めするスペースがない = 5桁のとき、 */
    /* repeat関数で、NOTE: ~引数が無効です。メッセージが出るので、 */
    /* substr関数を追加し、repeat関数の 4 → 5 に変更 */
    V2 = substr(cats(repeat("0", 5 - length(left(V0)) + cmiss(V0)), V0), 2, 5) ;

    /* 1. 先頭がスペースのとき、先頭スペースが残ったままにならないように、left関数を追加 */
    /* (catsでも可) */
    /* 2. 入力文字が何桁でも対応できるように、$5. の2倍の値を設定 : $revers8. → $revers10. */
    V4 = put(put('00000' || left(V0), $revers10.), $revers5.) ;

    /* NEW ! */
    V5 = substr(cats('00000', V0), length(left(V0)) - cmiss(V0) + 1, 5) ;
    run ;





    返信削除
    返信
    1. yoshinoさん、コメントありがとうございます!

      そうか、欠損値の時はlength関数って「1」が返されちゃうんでしたね。
      なので欠損値の時に、cmiss関数で「-1」して調整してるのか、、なるほどうまいですね!!

      新しいアイディアも私の発想から抜けてたもので勉強になります。
      ありがとうございます!

      削除