如何在 C 中使用 unicode 正确解码 url

How to properly decode url with unicode in C

从我的引荐来源日志中,我正在尝试解码引荐来源,但看起来 %81%8A 不是有效的百分比编码,所以我得到 ri�0�9o

我需要通过 websocket 发送解码后的字符串,现在我在浏览器端得到 Could not decode a text frame as UTF-8.

这些甚至是有效的百分比编码吗?我怎么知道它们是否有效?

#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>

void urldecode2(char *dst, const char *src) {
    char a, b;
    while(*src) {
        if((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) {
            if(a >= 'a')
                a -= 'a'-'A';
            if(a >= 'A')
                a -= ('A' - 10);
            else
                a -= '0';
            if(b >= 'a')
                b -= 'a'-'A';
            if(b >= 'A')
                b -= ('A' - 10);
            else
                b -= '0';
            *dst++ = 16*a+b;
            src+=3;
        } else if(*src == '+') {
            *dst++ = ' ';
            src++;
        } else {
            *dst++ = *src++;
        }
    }
    *dst++ = '[=11=]';
}

int main () {
    const char *in = "http://www.google.co.in/search?q=cari%810%8A9o";
    char out[100];

    urldecode2(out, in);
    printf("%s\n", out);

    return 0;
}

%81%8A 是完全有效的 %-escapes,但结果不是 UTF-8 字符串。 URL不要求必须是 UTF-8 字符串,但如今它们通常是。

在我看来,似乎发生了一些非常奇怪的双重编码。我不知道哪个约定使用 three-digit %-编码,但这就是你在 URL 中的样子。假设意图是编码西班牙语单词 "cariño"(care,affection,fondness),它应该是 UTF-8 中的 cari%C3%B1o,或者 [=29= 中的 cari%F1o ](通常意外出现在 URL 秒)。

有效 UTF-8 序列的规则非常简单,您可以使用正则表达式检查有效序列。并非所有有效序列都映射到字符,其中 66 个被显式映射为 "not characters",但所有有效序列都应被符合标准的解码器接受,即使它后来拒绝解码的字符在语义上不正确。

UTF-8 序列是 one-to-four 字节序列,对应于以下模式之一:(取自 Unicode 标准,table 3.7)

    Byte 1      Byte 2      Byte 3      Byte 4
    ------      ------      ------      ------
    00..7F        --          --          --
    C2..DF      80..BF        --          --
    E0          A0..BF      80..BF        --
    E1..EC      80..BF      80..BF        --
    ED          80..9F      80..BF        --
    EE..EF      80..BF      80..BF        --
    F0          90..BF      80..BF      80..BF
    F1..F3      80..BF      80..BF      80..BF
    F4          80..8F      80..BF      80..BF

任何其他都是非法的。 (因此代码 C0、C1 和 F5 到 FF 根本不会出现。)特别是,十六进制代码 81 和 8A 永远不能开始 UTF-8 序列。

由于没有好的方法可以知道无效序列可能意味着什么,最简单的方法就是将它们去掉。