更新于 2024-01-11
Punycode编码/解码的实现。
Console.WriteLine(PunyCode.Encode("用法")); //nwwn1p
Console.WriteLine(PunyCode.Decode("nwwn1p")); //用法
Console.WriteLine(PunyCode.IDN2Punycode("用法.中国")); //xn--nwwn1p.xn--fiqs8s
Console.WriteLine(PunyCode.Punycode2IDN("xn--nwwn1p.xn--fiqs8s")); //用法.中国
public class PunyCode
{
private static readonly string[] spliter = ["."];
public static string IDN2Punycode(string input)
{
string[] inputArray = input.Split(spliter, StringSplitOptions.RemoveEmptyEntries);
string retstr = "";
for (int i = 0; i < inputArray.Length; i++)
{
Regex myreg = new Regex("^[0-9a-zA-Z\\-]+$");
if (myreg.IsMatch(inputArray[i]))
retstr += inputArray[i] + ".";
else
retstr += "xn--" + Encode(inputArray[i]) + ".";
}
return retstr.TrimEnd('.');
}
public static string Punycode2IDN(string input)
{
string[] inputArray = input.ToLower().Split(spliter, StringSplitOptions.RemoveEmptyEntries);
string retstr = "";
for (int i = 0; i < inputArray.Length; i++)
{
string tmp = inputArray[i];
if (tmp.StartsWith("xn--"))
retstr += Decode(tmp.Substring(4)) + ".";
else
retstr += tmp + ".";
}
return retstr.TrimEnd('.');
}
public static string Encode(string input)
{
int n = 0x80;
int delta = 0;
int bias = 72;
StringBuilder output = new StringBuilder();
int b = 0;
for (int i = 0; i < input.Length; i++)
{
char c = input[i];
if (c < 0x80)
{
output.Append(c);
b++;
}
}
if (b > 0) output.Append('-');
int h = b;
while (h < input.Length)
{
int m = int.MaxValue;
for (int i = 0; i < input.Length; i++)
{
int c = input[i];
if (c >= n && c < m) m = c;
}
if (m - n > (int.MaxValue - delta) / (h + 1)) throw new Exception();
delta += (m - n) * (h + 1);
n = m;
for (int j = 0; j < input.Length; j++)
{
int c = input[j];
if (c < n)
{
delta++;
if (0 == delta) throw new Exception();
}
if (c == n)
{
int q = delta;
for (int k = 36; ; k += 36)
{
int t;
if (k <= bias) t = 1;
else if (k >= bias + 26) t = 26;
else t = k - bias;
if (q < t) break;
output.Append((char)Digit2Codepoint(t + (q - t) % (36 - t)));
q = (q - t) / (36 - t);
}
output.Append((char)Digit2Codepoint(q));
bias = Adapt(delta, h + 1, h == b);
delta = 0;
h++;
}
}
delta++;
n++;
}
return output.ToString();
}
public static string Decode(string input)
{
int n = 0x80;
int i = 0;
int bias = 72;
StringBuilder output = new StringBuilder();
int d = input.LastIndexOf('-');
if (d > 0)
{
for (int j = 0; j < d; j++)
{
char c = input[j];
if (c >= 0x80) throw new Exception();
output.Append(c);
}
d++;
}
else d = 0;
while (d < input.Length)
{
int oldi = i;
int w = 1;
for (int k = 36; ; k += 36)
{
if (d == input.Length) throw new Exception();
int c = input[d++];
int digit = Codepoint2Digit(c);
if (digit > (int.MaxValue - i) / w) throw new Exception();
i += digit * w;
int t;
if (k <= bias) t = 1;
else if (k >= bias + 26) t = 26;
else t = k - bias;
if (digit < t) break;
w *= 36 - t;
}
bias = Adapt(i - oldi, output.Length + 1, oldi == 0);
if (i / (output.Length + 1) > int.MaxValue - n) throw new Exception();
n += i / (output.Length + 1);
i %= output.Length + 1;
output.Insert(i, (char)n);
i++;
}
return output.ToString();
}
private static int Adapt(int delta, int numpoints, bool first)
{
delta /= first ? 700 : 2;
delta += delta / numpoints;
int k = 0;
while (delta > 455)
{
delta /= 35;
k += 36;
}
return k + (36 * delta) / (delta + 38);
}
private static int Digit2Codepoint(int d)
{
if (d < 26) return d + 97;
if (d < 36) return d + 22;
throw new Exception();
}
private static int Codepoint2Digit(int c)
{
if (c < 58) return c - 22;
if (c < 123) return c - 97;
throw new Exception();
}
}