LoginSignup
3

More than 5 years have passed since last update.

C++(Arduino)で簡単なURLエンコード/デコードを実装する

Last updated at Posted at 2019-02-20

Arduinoでネットワーク通信する際に必要だったのですが、どれも凝った実装ばかりで、ブラックボックスなままコピペするのも癪だったので、勉強も兼ねて簡単な実装をしてみました。

日本語には対応してません、悪しからず。

テーブル

エンコード/デコードを行う際の、文字コード識別テーブルを用意します。

エンコード用

UrlEncodeTable
const char encTable[16] = {
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};

エンコード対象識別用

UrlSafeTable
const char safeTable[128] = {
    //      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
    /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    /* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    /* 3 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
    /* 4 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    /* 5 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
    /* 6 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    /* 7 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
};

デコード用

UrlDecodeTable
const char decTable[128] = {
    //       0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    /* 0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    /* 1 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    /* 2 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    /* 3 */  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
    /* 4 */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    /* 5 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    /* 6 */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    /* 7 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};

エンコーダ/デコーダ

エンコーダ

UrlEncoder
String urlEncode(const char* msg){
    String encoded = "";

    while(*msg != '\0'){
        if(safeTable[*msg]){
            encoded += *msg;
        }
        else{
            encoded += '%';
            encoded += encTable[*msg >> 0x04];
            encoded += encTable[*msg & 0x0F];
        }
        msg++;
    }

    return encoded;
}

[0-9a-zA-Z]ならば変換は不要なので、そのままencodedへ追加します。
それ以外の文字は、文字コード値の上位4bitと下位4bitをそれぞれテーブルに掛け、16進表記へ変換しています。

例: '#' (0x23)の場合
1. safeTable[0x23] == 0の為、変換対象
2. 0x020x03に分割
3. encTable[0x02] == '2'
4. encTable[0x03] == '3'
5. 出力は%23となる

デコーダ

UrlDecoder
String urlDecode(const char* msg){
    String decoded = "";

    while(*msg != '\0'){
        if(*msg == '%'){
            char dec1 = decTable[*(msg + 1)];
            char dec2 = decTable[*(msg + 2)];

            if(dec1 != -1 && dec2 != -1){
                decoded += (dec1 << 4) + dec2;
            }
            msg += 3;
        }
        else{
            decoded += *msg;
            msg++;
        }
    }

    return decoded;
}

文字が%でなければ、そのままdecodedへ追加し次の文字へ移ります。

%がヒットしたら、1個先と2個先の文字コード値をそれぞれテーブルに掛け、1個先の値を4ビット左シフト後に2個先の値を足して出力します。

%1zなど、%の先に16進値以外の文字列が来た場合は、何も出力せずスキップします。

例: '#' ("%23")の場合
1. %の1個先は'2'
2. decTable['2'] == 2 ('2' == 0x32)
3. 2を4ビット左シフトすると0x20
4. %の2個先は'3'
5. decTable['3'] == 3 ('3' == 0x33)
6. 0x20 + 3 == 0x23となる

まとめ

普段は便利なライブラリに頼りがちですが、こうして自力で実装してみると、こんな短いコードでも勉強になりました。

余談: '\0'とは
char*型の配列は、自身の配列終了を識別する為、暗黙的に配列末尾へ\0が付与されます。
ポインタを増やして行き、ポインタが指すアドレスの中身が\0ならば、そこで配列は終了(文字列終了)と判別可能なのです。

SpecialThanks!

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
3