WSH JScriptで文字列中の半角カタカナを全角カタカナに変換する

# 極端に少ないWSH JScriptの公開記事
世間にはWSH(Windows Script Host) の VBScript(VBS) の記事は比較的多いです。VBA(Visual Basic for Application) が Excel とともに数多く日常事務作業の効率化で使われています。この VBA から移行しやすい VBScript は比較的なじみやすいからでしょう

なにしろ巷のシステムインテグレータさまでは、「ほぼほぼ」仕様書・設計書などのドキュメントを Excel で作成しており、いわゆる Excelマクロ を開発するプログラミング言語として VBA の人気は根強いというのが私の印象です。「SIerなら**みんな大好きExel君**」なのですから。入社数年でコードを書かなくなる、書かせてもらえない SIer ですら VBA は唯一書かれているプログラミング言語かもしれません

しかし、Windows版 JavaScript ともいえる JScript の公開記事の少なさには現場で日々 JScript でインフラ・システム管理業務の自動化を行っている私としては大変困っています。Webブラウザをプラットフォームとする Web系フロントエンド としての JavaScript の人気は絶大なものがあります

しかし、Windows をプラットフォームとする JScript はフロントエンドエンジニアからもそっぽを向かれたままです。フロントエンドエンジニアの Mac 好きという背景はあるにしても、あまりにも冷遇されすぎです。JScript にも、もう少し光が当たってほしいと思っている今日この頃です

# 半角カタカナを全角カタカナに変換する
そんな私が最近遭遇した最たるものが表題の「半角カタカナを全角カタカナに変換する」というものです

このソリューションは JavaScript での実装例は大量にインターネット上に公開されています。しかし、JScript の実装例は英語でググってもあまりお目にかかりません。特に極東アジアの小国ニッポンの「カタカナ」を取り扱う JScript は検索しても実装例はあまり出てきません。JavaScript の記事を頑張って JScript に移植するしかないのが実情ではないでしょうか?

私が移植したのは Qiita の記事「**[JavaScriptで正規表現（文字列置換え編）](https://qiita.com/hrdaya/items/291276a5a20971592216)**」で紹介してくださっている関数「hankana2zenkana()」です

以下が移植した結果のコードです。

```JavaScript:convertKatakanaHalfToFullWidth.js
/*--------------------------------------------------------------
 * 文字列内の半角カタカナを全角カタカナに変換します
 * @param {String} targetString 変換したい文字列
 *     下記のQiitaの記事のJavaScript版を参考にさせていただいて
 *     WSH(Windows Scripting Host) JScript版を作ってみました
 *         JavaScriptで正規表現（文字列置換え編）
 *         https://qiita.com/hrdaya/items/291276a5a20971592216
 *         関数：hankana2zenkana()
 --------------------------------------------------------------*/
function UtilityClass(){
  this.KatakanaMap = {
    'ｶﾞ':  'ガ', 'ｷﾞ':  'ギ', 'ｸﾞ':  'グ', 'ｹﾞ':  'ゲ', 'ｺﾞ':  'ゴ',
    'ｻﾞ':  'ザ', 'ｼﾞ':  'ジ', 'ｽﾞ':  'ズ', 'ｾﾞ':  'ゼ', 'ｿﾞ':  'ゾ',
    'ﾀﾞ':  'ダ', 'ﾁﾞ':  'ヂ', 'ﾂﾞ':  'ヅ', 'ﾃﾞ':  'デ', 'ﾄﾞ':  'ド',
    'ﾊﾞ':  'バ', 'ﾋﾞ':  'ビ', 'ﾌﾞ':  'ブ', 'ﾍﾞ':  'ベ', 'ﾎﾞ':  'ボ',
    'ﾊﾟ':  'パ', 'ﾋﾟ':  'ピ', 'ﾌﾟ':  'プ', 'ﾍﾟ':  'ペ', 'ﾎﾟ':  'ポ',
    'ｳﾞ':  'ヴ', 'ﾜﾞ':  '?', 'ｦﾞ':  '?',
    'ｱ':  'ア', 'ｲ':  'イ', 'ｳ':  'ウ', 'ｴ':  'エ', 'ｵ':  'オ',
    'ｶ':  'カ', 'ｷ':  'キ', 'ｸ':  'ク', 'ｹ':  'ケ', 'ｺ':  'コ',
    'ｻ':  'サ', 'ｼ':  'シ', 'ｽ':  'ス', 'ｾ':  'セ', 'ｿ':  'ソ',
    'ﾀ':  'タ', 'ﾁ':  'チ', 'ﾂ':  'ツ', 'ﾃ':  'テ', 'ﾄ':  'ト',
    'ﾅ':  'ナ', 'ﾆ':  'ニ', 'ﾇ':  'ヌ', 'ﾈ':  'ネ', 'ﾉ':  'ノ',
    'ﾊ':  'ハ', 'ﾋ':  'ヒ', 'ﾌ':  'フ', 'ﾍ':  'ヘ', 'ﾎ':  'ホ',
    'ﾏ':  'マ', 'ﾐ':  'ミ', 'ﾑ':  'ム', 'ﾒ':  'メ', 'ﾓ':  'モ',
    'ﾔ':  'ヤ', 'ﾕ':  'ユ', 'ﾖ':  'ヨ',
    'ﾗ':  'ラ', 'ﾘ':  'リ', 'ﾙ':  'ル', 'ﾚ':  'レ', 'ﾛ':  'ロ',
    'ﾜ':  'ワ', 'ｦ':  'ヲ', 'ﾝ':  'ン',
    'ｧ':  'ァ', 'ｨ':  'ィ', 'ｩ':  'ゥ', 'ｪ':  'ェ', 'ｫ':  'ォ',
    'ｯ':  'ッ', 'ｬ':  'ャ', 'ｭ':  'ュ', 'ｮ':  'ョ',
    '｡':  '。', '､':  '、', 'ｰ':  'ー', '｢':  '「', '｣':  '」', '･':  '・'
  };

  this.convertKatakanaHalfToFullWidth = function ( targetString ) {
  
    var convertedString = ""; var targetChar = ''; var convertedChar = '';
    for( var i = 0; i < targetString.length; i++ ){
      targetChar = targetString.substr( i, 1 );
      convertedChar = this.KatakanaMap[ targetChar ];

      if( convertedChar == undefined ){
        convertedString += targetChar;
      } else {
        convertedString += convertedChar;
      }
    }
  
    return( convertedString );
  };
}
```
私が試行錯誤した箇所に限って以下に述べたいと思います。上記の記事は正規表現を使ってありますが、以下に述べる理由で私はスマートな正規表現を使わずに地味にダサく実装いたしました

# ShiftJISエンコードでコードを保管する

オブジェクト「KatakanaMap 」にはカタカナが含まれています。これを UTF-8 エンコードで保管するとオブジェクト「KatakanaMap 」の定義で実行時エラーが出ました。おそらくですが、濁点・半濁点のカタカナが UTF-8 エンコードのオブジェクトの定義では何かがうまくいっていないのかも知れません

### 追記
Qiita ID "nukie_53"さまからコメント欄でご指摘をいただきました。WSH JScriptで使えるエンコードは**ShiftJIS**と**UTF16LE**（Unicode）のようです

# オブジェクトのキーから値を取り出す

JavaScriptだとカタカナのオブジェクト「KatakanaMap」からキーリストを取り出すのは「**Object.keys( KatakanaMap )**」です。しかし、これは JScript では動作しませんでした。かなり試行錯誤したのですがビクとも動きません

そこで、この「keys()」メソッドを使うのを早々にあきらめました。つまり、これが使えないと正規表現を使ったスマートな実装はあきらめざるを得ないことになります

いろいろ試行錯誤した結果、JScript でオブジェクトのキーから対応する値を取り出すのは以下の方法を使うしかないのではないかということになりました

```JavaScript:JScriptでオブジェクトのキーから対応する値を取り出す
convertedChar = this.KatakanaMap[ targetChar ];
```

「targetChar」がオブジェクトのキーにあたるもので、ここでは、半角カタカナを指定します。指定したキーに一致する値が見つかった場合には、その値が返されます。もしも、キーに一致する値が見つからない場合、すなわち「targetChar」が半角カタカナでない場合には、「**undefined**」が返ってきます。これで、半角カタカナとそれ以外の文字を判別できるのです

### オブジェクトのキーから値を取り出す(改良版)

QiitaID"culage"さまからコメントで「**Object.keys()**」メソッドの代わりになるWSH JScript用のメソッド（関数）を教えていただきました。それが、以下の「**getObject()**」メソッドです。引数で受け取ったJSON形式のオブジェクトからキーを取り出してリストに「push()」メソッドで格納しています

```JavaScript:getObject()メソッド-QiitaID"culage"さまご指南
  this.getObjectKeys = function ( obj ) {
    var list = [];
    for( var key in obj ) { list.push( key ); }
    return list;
  };
```
このメソッドを使った改良版がこちらです。この改良版でも問題なく半角カタカナを全角カタカナに変換してくれました。こちらは正規表現を使ってパターンマッチングを行っているので、私のダサダサなコードに比べてかなりスマートです

```JavaScript:convertKatakanaHalfToFullWidth.js(改良版)
function UtilityClass(){
  this.convertKatakanaHalfToFullWidth = function ( targetString ) {
    var KatakanaMap = {
      'ｶﾞ':  'ガ', 'ｷﾞ':  'ギ', 'ｸﾞ':  'グ', 'ｹﾞ':  'ゲ', 'ｺﾞ':  'ゴ',
      'ｻﾞ':  'ザ', 'ｼﾞ':  'ジ', 'ｽﾞ':  'ズ', 'ｾﾞ':  'ゼ', 'ｿﾞ':  'ゾ',
      'ﾀﾞ':  'ダ', 'ﾁﾞ':  'ヂ', 'ﾂﾞ':  'ヅ', 'ﾃﾞ':  'デ', 'ﾄﾞ':  'ド',
      'ﾊﾞ':  'バ', 'ﾋﾞ':  'ビ', 'ﾌﾞ':  'ブ', 'ﾍﾞ':  'ベ', 'ﾎﾞ':  'ボ',
      'ﾊﾟ':  'パ', 'ﾋﾟ':  'ピ', 'ﾌﾟ':  'プ', 'ﾍﾟ':  'ペ', 'ﾎﾟ':  'ポ',
      'ｳﾞ':  'ヴ', 'ﾜﾞ':  '?', 'ｦﾞ':  '?',
      'ｱ':  'ア', 'ｲ':  'イ', 'ｳ':  'ウ', 'ｴ':  'エ', 'ｵ':  'オ',
      'ｶ':  'カ', 'ｷ':  'キ', 'ｸ':  'ク', 'ｹ':  'ケ', 'ｺ':  'コ',
      'ｻ':  'サ', 'ｼ':  'シ', 'ｽ':  'ス', 'ｾ':  'セ', 'ｿ':  'ソ',
      'ﾀ':  'タ', 'ﾁ':  'チ', 'ﾂ':  'ツ', 'ﾃ':  'テ', 'ﾄ':  'ト',
      'ﾅ':  'ナ', 'ﾆ':  'ニ', 'ﾇ':  'ヌ', 'ﾈ':  'ネ', 'ﾉ':  'ノ',
      'ﾊ':  'ハ', 'ﾋ':  'ヒ', 'ﾌ':  'フ', 'ﾍ':  'ヘ', 'ﾎ':  'ホ',
      'ﾏ':  'マ', 'ﾐ':  'ミ', 'ﾑ':  'ム', 'ﾒ':  'メ', 'ﾓ':  'モ',
      'ﾔ':  'ヤ', 'ﾕ':  'ユ', 'ﾖ':  'ヨ',
      'ﾗ':  'ラ', 'ﾘ':  'リ', 'ﾙ':  'ル', 'ﾚ':  'レ', 'ﾛ':  'ロ',
      'ﾜ':  'ワ', 'ｦ':  'ヲ', 'ﾝ':  'ン',
      'ｧ':  'ァ', 'ｨ':  'ィ', 'ｩ':  'ゥ', 'ｪ':  'ェ', 'ｫ':  'ォ',
      'ｯ':  'ッ', 'ｬ':  'ャ', 'ｭ':  'ュ', 'ｮ':  'ョ',
      '｡':  '。', '､':  '、', 'ｰ':  'ー', '｢':  '「', '｣':  '」', '･':  '・'
    };
    var reg = new RegExp(
                '('
                  + this.getObjectKeys( KatakanaMap ).join( '|' )
                  + ')'
                  , 'g'
              );
    return targetString
              .replace( reg
                        , function ( match ) {
                            return KatakanaMap[ match ];
                          }
              )
              .replace( /ﾞ/g, '゛' )
              .replace( /ﾟ/g, '゜' );
  };
  // Object.keys() メソッドのWSH JScript用メソッド
  // QiitaID "culage"さまご指南
  this.getObjectKeys = function ( obj ) {
    var list = [];
    for( var key in obj ) { list.push(key); }
    return list;
  };
}
```

# 「for ~ in コレクション」はJScriptでは使えない

蛇足ではありますが、JScript では「for ~ in コレクション」は使えません。これも JScript では初心者がはまりやすいワナです。代わりに「[Enumerator オブジェクト](https://msdn.microsoft.com/ja-jp/library/cc427724.aspx)」を使います。「[Enumerator オブジェクト](https://msdn.microsoft.com/ja-jp/library/cc427724.aspx)」はコレクションにしか使えません。上記の実装例のようなオブジェクトのキーや値を順番に取り出すことは出来ませんので注意が必要です

慣れないうちはダサダサでも普通の「for( 初期値; 判断; カウントアップ )」形式の for 文を使った方が仕事が早い場合もあります 

# 実行結果
テスト実行用の JScript コードを以下のように実装してみました。

```JavaScript:convertKatakanaHalfToFullWidth.js実行用コード
var targetString = "ﾍﾟﾌﾟｼｺｰﾗとｺｶｺｰﾗはどっちが美味しいのか？";
var utility = new UtilityClass();
WScript.Echo(
    "・変換前：" + targetString + "\n"
  + "・変換後：" + utility.convertKatakanaHalfToFullWidth( targetString )
);
```

実行すると、以下のようにダイアログが表示されます。きちんと半角カタカナが全角カタカナに変換されているのがおわかりいただけると思います
![convertKatakanaHalfToFullWidth.PNG](https://qiita-image-store.s3.amazonaws.com/0/124810/55a7036d-b4f9-ed9b-d536-d08e2c41cdb5.png)

# 最後に
まさにキモは上記で述べた部分だけであり、こんなにもアホみたいに簡単なこともググってもまったく実装例が出てきませんでした

ということで、もしも困っている人がいらっしゃって、私がここに公開したこの記事をググって発見して、仕事の少しでも助けになれば幸いです