Swift で SemiEncoder, SemiDecoder を作る

沖縄では蝉が鳴く季節がやってきましたね！友人も鳴いてました！！
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ...ﾐｰﾝﾐﾝﾐﾝﾐｰﾝ</p>&mdash; あるマス (@walkingmask) <a href="https://twitter.com/walkingmask/status/738941421491621889">2016年6月4日</a></blockquote>

6月とは思えない暑さですもんね！
では本題です.
##SemiEncoder
SemiEncoder には以下のルールがあります.

1. 文字列からバイナリ文字列を作成
2. "0" を "ﾐﾝ", "1" を "ﾐｰﾝ" へ変換
3. "ﾐｰﾝﾐﾝﾐﾝ" の後ろに "…" が続く(ﾐｰﾝﾐﾝﾐﾝ…)

簡単ですね！

##SemiDecoder
SemiDecoder に関しては SemiEncode の手順を全て逆順で行えばできますね！

##実装
最初の extension は 上からバイナリ文字列へ変換, バイナリ文字列から元の文字列への変換, バイナリ文字列のパディング, 正規表現でマッチした部分の文字列の Array を作成, 正規表現でマッチした文字列を置換するためのメソッドを書いてます.

少し長いですが, メインは一番下の extension の部分です.

```swift
import Foundation

extension String {
    
    var binary: String {
        let chars = self.characters.map { String($0).unicodeScalars.first!.value }
        return chars.map({ String($0, radix: 2).pad(24) }).joinWithSeparator("")
    }
    
    func bintostr() -> String {
        let binaries = self.match("[01]{1,24}")
        var orig = ""
        for b in binaries {
            orig += String(UnicodeScalar(Int(strtoul(b, nil, 2))))
        }
        return orig
    }
    
    func pad(toSize: Int) -> String {
        var padded = self
        for _ in 0..<toSize - self.characters.count {
            padded = "0" + padded
        }
        return padded
    }
    
    func match(pattern: String) -> [String] {
        let regex = try! NSRegularExpression(pattern: pattern, options: [])
        let range = NSMakeRange(0, self.characters.count)
        let matches = regex.matchesInString(self, options: [], range: range)
        
        var matchedTokens = [String]()
        for result in matches {
            for r in [result] {
                if r.range.location <= self.characters.count && r.range.length > 0 {
                    matchedTokens.append((self as NSString).substringWithRange(r.range))
                }
                break
            }
        }
        
        return matchedTokens
    }
    
    func replace(pattern: String, _ withString: String) -> String {
        let regex = try! NSRegularExpression(pattern: pattern, options: [])
        let range = NSMakeRange(0, self.characters.count)
        return regex.stringByReplacingMatchesInString(self, options: [], range: range, withTemplate: withString)
    }
}

// SemiEncode, SemiDecode
extension String {
    func SemiEncode() -> String {
        return self.binary.replace("0", "ﾐﾝ").replace("1", "ﾐｰﾝ").replace("(ﾐｰﾝﾐﾝﾐﾝ)", "$0…")
    }
    
    func SemiDecode() -> String {
        return self.replace("…", "").replace("ﾐﾝ", "0").replace("ﾐｰﾝ", "1").bintostr()
    }
}
```

他のメソッドが長い長いwww
バイナリ文字列の桁数を 24 桁にしているのは Unicode の数が $16^6 = 2^{24}$ 未満だからです！！（自信無い）

##実行
こんな感じのコードを書くと実行できるはず…!!

```swift
let str = "お前のカーチャン🍣ｨｨｨｨｨ〜www"
let crows = str.SemiEncode() // ﾐﾝﾐﾝﾐﾝﾐﾝﾐﾝﾐﾝﾐﾝﾐﾝﾐﾝﾐﾝﾐｰﾝﾐｰﾝﾐﾝﾐﾝ…
crows.SemiDecode() // お前のカーチャン🍣ｨｨｨｨｨ〜www
```

##おまけ
Perlだとこんなに簡単に書けるのに...

```pl
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use Encode qw/encode_utf8/;
use v5.10;

sub SemiEncode {
	my $input = shift;
	my $bit = unpack 'B*', $input;
	$bit =~ s/0/ﾐﾝ/g;
	$bit =~ s/1/ﾐｰﾝ/g;
	$bit =~ s/(ﾐｰﾝﾐﾝﾐﾝ)/$1…/g;
	return $bit;
}

sub SemiDecode {
	my $meen = shift;
	$meen =~ s/…//g;
	$meen =~ s/ﾐﾝ/0/g;
	$meen =~ s/ﾐｰﾝ/1/g;
	return pack 'B*', $meen;
}

my $input = encode_utf8("お前のカーチャン🍣ｨｨｨｨｨ〜www");
my $m = SemiEncode($input);

say encode_utf8($m);
say SemiDecode($m);
```