libidn库下载、编译、示例:实现UTF-8转Punycode、Punycode转UTF-8
1 Punycode 编码
1.1 Punycode 编码
一种用于将Unicode字符转换为ASCII字符的编码方式,主要用于实现国际化域名(IDN)。由于域名系统最初只能支持ASCII字符,Punycode允许非ASCII字符的域名通过特定编码方式转换为有效的ASCII域名。
1.2 ASCII、Unicode、UTF-8、UTF-16、UTF-32、GBK和 Punycode编码区别
ASCII、Unicode、UTF-8、UTF-16、UTF-32、GBK和Punycode都是用于字符编码的系统,它们的目的是将字符映射为计算机可以处理的数字。
1.2.1 ASCII编码
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是最早的字符编码系统之一,它使用7位二进制数来表示128个不同的字符,包括大小写英文字母、数字、标点符号以及一些控制字符。
1.2.2 Unicode编码
Unicode是一种旨在为世界上所有字符提供唯一编号的字符编码标准。它包含了几乎所有书写系统中的符号和标点,支持从古代文字到现代字符的广泛字符集。
1.2.3 UTF-8、UTF-16和UTF-32编码
UTF-8、UTF-16和UTF-32是Unicode字符集的编码方式。
1、UTF-8是一种变长的编码方式,使用1到4个字节来表示一个字符,兼容ASCII编码,对英文字符使用单字节表示,而对其他字符则使用更多的字节。
2、UTF-16使用2个或4个字节表示一个字符,对于基本多文种平面(BMP)内的字符使用2个字节,对于辅助平面内的字符使用4个字节。
3、UTF-32使用固定长度的4个字节表示每个字符。
1.2.4 GBK
GBK(Chinese Internal Code Specification)是一种扩展的双字节编码,主要用于简体中文,它是对GB2312编码的扩展,包含了更多汉字和字符。
2 libidn库的下载、编译、转换实例
2.1 ibidn库的下载
libidn下载地址
当前最新的稳定版本为:libidn2-2.3.7.tar.gz
2.2 ibidn库的编译
2.2.1 libidn库的当前主机编译安装
(1) ./configure
(2) make
(3) make install (注:如果不指定prefix,默认安装在/usr/local/下)
2.2.2 libidn库的交叉编译
3 使用libidn库实现utf-8与Punycode相互转换的实例
#include <stdio.h>
#include <stdlib.h>
#include <idn2.h>
#include <string.h>#define is_split_symbol(c) ((c) == '.' )int countCharInString(char *str, char ch) { int count = 0; // 初始化计数器 char *p = str;while (*p) { // 遍历字符串直到遇到字符串结束符'\0' if (*p == ch) { // 如果当前字符是目标字符 count++; // 计数器加1 } p++; // 移动到字符串的下一个字符 } return count; // 返回计数结果
}static char para[128][64];
static int npara;
static int domain_para_parse(char *str)
{char *cur;int argcc = 0, i = 0;if (str == NULL) return 0;memset(para, 0, sizeof(para));npara = 0;cur = str;while(is_split_symbol(*cur)) cur++;while (1) {if (*cur == 0) break;if (!is_split_symbol(*cur)){para[npara][argcc] = *cur;argcc ++; cur ++;if (argcc == 64) return 0;//域名关键字超出限制} else {while (is_split_symbol(*cur)) cur++;if (argcc == 64) return 0;//域名关键字超出限制para[npara][argcc] = 0;npara ++; argcc = 0;}if (argcc >= 64) return 0; //域名关键字超出限制//Begin li_xiaofei@topsec.com.cn 20061027if (npara>128) return 0;//域名深度超出限制//End li_xiaofei@topsec.com.cn 20061027 }if (!is_split_symbol(str[strlen(str)-1])) npara ++;//Begin li_xiaofei@topsec.com.cn 20061027if (npara > 128) return 0;//End li_xiaofei@topsec.com.cn 20070127return 1;
}int punycode_encode(char *in_str, char **out_str) {char *ascii_domain = NULL, *p = NULL; // 用于存储转换后的 ASCII 域名 int ret = 0, i = 0;int flags = 0; // 转换标志,当前设置为 0 printf("[%s:%d] in_str %s\n", __func__, __LINE__, in_str);ret = domain_para_parse(in_str);if (!ret) {return -1;}*out_str = p = malloc(npara * 64 * 4 + 1);for ( i = 0; i < npara; i++) {printf("[%s:%d] para[%d] %s\n", __func__, __LINE__, i, para[i]);if (i) {p += sprintf(p, ".");}ret = idn2_to_ascii_8z(para[i], &ascii_domain, flags); if (ret != IDN2_OK) { // 如果转换失败,则打印错误信息 fprintf(stderr, "Error converting UTF-8 to ASCII: %d\n", ret); return -1; } p += sprintf(p, "%s", ascii_domain);if (ascii_domain) free(ascii_domain);printf("[%s:%d] out_str %s\n", __func__, __LINE__, *out_str);}printf("[%s:%d] out_str %s\n", __func__, __LINE__, *out_str);return 0;
}int punycode_decode(char * in_str, char **out_str) {char *unicode_domain = NULL, *p = NULL; // 用于存储转换后的 unicode 域名 int ret = 0, i = 0;int flags = 0; // 转换标志,当前设置为 0 printf("[%s:%d] in_str %s\n", __func__, __LINE__, in_str);ret = domain_para_parse(in_str);if (!ret) {return -1;}*out_str = p = malloc(npara * 64 * 4 + 1);for ( i = 0; i < npara; i++) {printf("[%s:%d] para[%d] %s\n", __func__, __LINE__, i, para[i]);if (i) {p += sprintf(p, ".");}ret = idn2_to_unicode_8z8z(para[i], &unicode_domain, flags);if (ret != IDN2_OK) { // 如果转换失败,则打印错误信息 fprintf(stderr, "Error converting UTF-8 to ASCII: %d\n", ret); return -1; } p += sprintf(p, "%s", unicode_domain);if (unicode_domain) free(unicode_domain);printf("[%s:%d] out_str %s\n", __func__, __LINE__, *out_str);}printf("[%s:%d] out_str %s\n", __func__, __LINE__, *out_str);
}int main() { //char utf8_domain[1024] = {0}; // UTF-8 编码的国际化域名 char *utf8_domain = "你好.com.你好.cn"; // UTF-8 编码的国际化域名 char *ascii_domain = NULL; // 用于存储转换后的 ASCII 域名 char *unicode_domain = NULL; // 用于存储转换后的 unicode 域名 int ret = 0;ret = punycode_encode(utf8_domain, &ascii_domain);if (ret < 0) {printf("[%s:%d] %s punycode encode failed!\n", __func__, __LINE__, utf8_domain);goto out;}printf("[%s:%d] ascii_domain %s\n", __func__, __LINE__, ascii_domain);ret = punycode_decode(ascii_domain, &unicode_domain);if (ret < 0) {printf("[%s:%d] %s punycode_decode failed!\n", __func__, __LINE__, ascii_domain);goto out;}printf("[%s:%d] unicode_domain %s\n", __func__, __LINE__, unicode_domain);out:// 释放由 idn2_to_ascii_8z 分配的内存 if (ascii_domain) free(ascii_domain); if (unicode_domain) free(unicode_domain); return ret;
}