LoginSignup
2
3

More than 5 years have passed since last update.

Swift で SemiEncoder, SemiDecoder を作る

Posted at

沖縄では蝉が鳴く季節がやってきましたね!友人も鳴いてました!!

6月とは思えない暑さですもんね!
では本題です.

SemiEncoder

SemiEncoder には以下のルールがあります.

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

簡単ですね!

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);
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3