Преобразование URL национального домена в punycode

При использовании компонента WinHTTPrequest для выполнения запроса к сайту,
требуется предварительно преобразовать URL национальных доменов с использованием метода Punycode.

PS: если вы загружаете исходный код вебстраницы с использованием WinAPI функции URLDownloadToFile, - подобное преобразование не обязательно

Sub ПримерИспользования_ConvertURLtoPunycode()
    Dim host$, newURL$
 
    ' исходная ссылка
    host$ = "http://государство.президент.рф/советы"
 
    ' результат преобразования: "http://xn--80aebe3cdmfdkg.xn--d1abbgf6aiiy.xn--p1ai/%D1%81%D0%BE%D0%B2%D0%B5%D1%82%D1%8B"
    newURL$ = ConvertURLtoPunycode(host$)
    MsgBox newURL$
End Sub

Автор функции преобразования: Achim Neubauer
Источник: www.herber.de/forum/archiv/1192to1196/1192164_Punycode_Unicode.html

Для использования функции, добавьте в проект стандартный модуль, и в него вставьте следующий код:

'****************************************
'* Converts Domainnames from Unicode to Punycode and vice versa             *
'* Programmed: Achim Neubauer           Last Change: 22.03.2004 18:39       *
'****************************************
' source: http: //www.herber.de/forum/archiv/1192to1196/1192164_Punycode_Unicode.html
' Edited 25.01.2015 by ExcelVBA.ru    http: //excelvba.ru/code/punycode

Option Explicit
 
'Punycode constants
Private Const BASE As Long = 36, TMIN As Long = 1, TMAX As Long = 26, SKEW As Long = 38, DAMP As Long = 700
Private Const INITIAL_BIAS As Long = 72, INITIAL_N As Long = 128
'********************************************************************************

Function ConvertURLtoPunycode(ByVal URL$) As String
    ' преобразует URL национальных доменов в Punycode
    ' например, ссылка http: //государство.президент.рф/советы
    ' преобразуется в http: //xn--80aebe3cdmfdkg.xn--d1abbgf6aiiy.xn--p1ai/%D1%81%D0%BE%D0%B2%D0%B5%D1%82%D1%8B
    ' если ссылка ведет на обычный (международный домен), приеобразование не выполняется
    ' дополнительно выполняется преобразование ссылки в URLencode

    On Error Resume Next
    Dim arr, host$, i&, l$, t$, txt$
    arr = Split(URL$, "/")
    arr(2) = EncodeHost(arr(2))        ' Punycode
    txt$ = Join(arr, "/")
 
    For i = 1 To Len(txt)
        l = Mid(txt, i, 1)
        Select Case AscW(l)
            Case Is > 256
                t = "%" & Hex(AscW(l) \ 64 + 192) & "%" & Hex(8 * 16 + AscW(l) Mod 64)
            Case 32: t = "+"
            Case Else: t = l
        End Select
        ConvertURLtoPunycode = ConvertURLtoPunycode & t
    Next
End Function
Public Function EncodeHost(ByVal Name As String) As String
    If Len(Name) = 0 Then Exit Function
    Dim arrLevels() As String, t As Long
 
    arrLevels = Split(Name, ".")
    For t = 0 To UBound(arrLevels)
        arrLevels(t) = Replace(arrLevels(t), "?", "ss")
        arrLevels(t) = Encode(arrLevels(t))
    Next t
    EncodeHost = Join(arrLevels, ".")
End Function
 
Public Function DecodeHost(Name As String) As String
    If Len(Name) = 0 Then Exit Function
    Dim arrLevels() As String, t As Long
    arrLevels = Split(Name, ".")
    For t = 0 To UBound(arrLevels)
        If Left$(LCase$(arrLevels(t)), 4) = "xn--" Then
            arrLevels(t) = Decode(Mid$(arrLevels(t), 5))
        End If
    Next t
    DecodeHost = Join(arrLevels, ".")
End Function
'********************************************************************************
Private Function Encode(text As String) As String
    On Error GoTo Ende
    Dim n&, delta&, bias&, b&, output$, l&, c$, h&, q&, m&, k&, t&
    bias = INITIAL_BIAS: n = INITIAL_N
    For l = 1 To Len(text)
        c = Mid$(text, l, 1)
        If IsBasic(c, INITIAL_N) Then output = output & c: b = b + 1
    Next l
    If Len(output) < Len(text) Then
        If Len(output) > 0 Then output = output & "-"
        output = "xn--" & output
    End If
 
    h = b
    While h < Len(text)
        m = GetMinCodePoint(n, text)
        delta = delta + UInt(m - n) * (h + 1)
        n = m
        For l = 1 To Len(text)
            c = Mid$(text, l, 1)
            If IsBasic(c, n) Then
                delta = delta + 1
            ElseIf UInt(AscW(c)) = n Then
                q = delta
                For k = BASE To &H7FFFFFFF Step BASE
                    If k <= bias + TMIN Then
                        t = TMIN
                    ElseIf k >= bias + TMAX Then
                        t = TMAX
                    Else
                        t = k - bias
                    End If
                    If q < t Then Exit For
                    output = output & Chr(Digit2Codepoint(t + ((q - t) Mod (BASE - t))))
                    Let q = (q - t) \ (BASE - t)
                Next k
                output = output & Chr(Digit2Codepoint(q))
                bias = Adapt(delta, h + 1, (h = b))
                delta = 0: h = h + 1
            End If
        Next l
        delta = delta + 1: n = n + 1
    Wend
 
Ende:
    Encode = output
End Function
 
Private Function Decode(text As String) As String
    On Error GoTo Ende
    Dim n&, i&, bias&, output$, l&, pos&, c$, oldi&, w&, k&, digit As Byte, t&
    n = INITIAL_N: bias = INITIAL_BIAS
    pos = InStrRev(text, "-")
    If pos > 0 Then
        For l = 1 To pos - 1
            c = Mid$(text, l, 1)
            If IsBasic(c, INITIAL_N) Then output = output & c Else Exit Function
        Next l
    End If
 
    pos = pos + 1
    Do While (pos <= Len(text))
        oldi = i: w = 1
        For k = BASE To &H7FFFFFFF Step BASE
            If pos > Len(text) Then Exit For        'out of code points
            c = Mid$(text, pos, 1)
            pos = pos + 1
            digit = Codepoint2Digit(Asc(c))
            If digit = 255 Then Exit Function        'bad code point
            i = i + digit * w
            If k <= bias Then
                t = TMIN
            ElseIf k >= bias + TMAX Then
                t = TMAX
            Else
                t = k - bias
            End If
            If digit < t Then Exit For
            w = w * (BASE - t)
        Next k
        bias = Adapt(i - oldi, Len(output) + 1, (oldi = 0))
        n = n + i \ (Len(output) + 1)
        i = i Mod (Len(output) + 1)
        If IsBasic(ChrW(n), INITIAL_N) Then Exit Function        'shouldn't be a basic code point
        output = Left$(output, i) & ChrW(n) & Mid$(output, i + 1)
        i = i + 1
    Loop
Ende:
    Decode = output
End Function
Private Function GetMinCodePoint(ByVal n As Long, ByVal data As String) As Long
    Dim t&, a&, result&
    result = &H7FFFFFFF
    For t = 1 To Len(data)
        a = UInt(AscW(Mid$(data, t, 1)))
        If (a >= n) And (a < result) Then result = a
    Next t
    GetMinCodePoint = result
End Function
 
Private Function IsBasic(c As String, ByVal n As Long) As Boolean
    IsBasic = (UInt(AscW(c)) < n)
End Function
 
Private Function Adapt(ByVal delta As Long, ByVal numpoints As Long, ByVal firsttime As Boolean) As Long
    Dim k As Long
    If (firsttime) Then delta = delta \ DAMP Else delta = delta \ 2
    delta = delta + (delta \ numpoints)
    k = 0
    While (delta > ((BASE - TMIN) * TMAX) \ 2)
        delta = delta \ (BASE - TMIN)
        k = k + BASE
    Wend
    Adapt = k + (((BASE - TMIN + 1) * delta) \ (delta + SKEW))
End Function
 
Private Function Digit2Codepoint(ByVal d As Long) As Long
    If (d < 26) Then
        Digit2Codepoint = d + &H61        'a'
    ElseIf (d < 36) Then
        Digit2Codepoint = d - 26 + &H30        '0'
    Else
        Debug.Print "Error in Function Digit2Codepoint"
    End If
End Function
 
Private Function Codepoint2Digit(ByVal c As Long) As Long
    If (c - &H30 < 10) Then        '0'
        Codepoint2Digit = 26 + c - &H30        '0'
    ElseIf (c - &H41 < 26) Then        'a'
        Codepoint2Digit = c - &H41        'a'
    ElseIf (c - &H61 < 26) Then        'A'
        Codepoint2Digit = c - &H61        'A'
    Else
        Codepoint2Digit = 255
        Debug.Print "Error in Function Codepoint2Digit"
    End If
End Function
 
Private Function UInt(i As Integer) As Long
    If i < 0 Then UInt = 2 ^ 16 + i Else UInt = i
End Function

Комментарии

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

Работает. Спасибо. Для себя, пришлось дописать функцию разбивки URL на части, чтобы кидать Host в функции EncodeHost и DecodeHost.

Здравствуйте, Спасибо большое!
Отличное работает.
А обратного конвертера нет случайно?

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
CAPTCHA
Подтвердите, пожалуйста, что вы - человек:
              _  __         __        __              ____  
_ __ ___ | |/ / __ _ \ \ / / _ __ ___ | __ )
| '_ ` _ \ | ' / / _` | \ \ /\ / / | '_ ` _ \ | _ \
| | | | | | | . \ | (_| | \ V V / | | | | | | | |_) |
|_| |_| |_| |_|\_\ \__, | \_/\_/ |_| |_| |_| |____/
|_|
Введите код, изображенный в стиле ASCII-арт.

Не получается применить макрос? Не удаётся изменить код под свои нужды?

Оформите заказ у нас на сайте, не забыв прикрепить примеры файлов, и описать, что и как должно работать.