沖縄では蝉が鳴く季節がやってきましたね!友人も鳴いてました!!
ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン...ミーンミンミンミーン
— あるマス (@walkingmask) 2016年6月4日
6月とは思えない暑さですもんね!
では本題です.
SemiEncoder
SemiEncoder には以下のルールがあります.
- 文字列からバイナリ文字列を作成
- "0" を "ミン", "1" を "ミーン" へ変換
- "ミーンミンミン" の後ろに "…" が続く(ミーンミンミン…)
簡単ですね!
SemiDecoder
SemiDecoder に関しては SemiEncode の手順を全て逆順で行えばできますね!
実装
最初の extension は 上からバイナリ文字列へ変換, バイナリ文字列から元の文字列への変換, バイナリ文字列のパディング, 正規表現でマッチした部分の文字列の Array を作成, 正規表現でマッチした文字列を置換するためのメソッドを書いてます.
少し長いですが, メインは一番下の extension の部分です.
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}$ 未満だからです!!(自信無い)
実行
こんな感じのコードを書くと実行できるはず…!!
let str = "お前のカーチャン🍣ィィィィィ〜www"
let crows = str.SemiEncode() // ミンミンミンミンミンミンミンミンミンミンミーンミーンミンミン…
crows.SemiDecode() // お前のカーチャン🍣ィィィィィ〜www
おまけ
Perlだとこんなに簡単に書けるのに...
#!/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);