То что перекодируется именно этот кусок - это понятно, а вот как его перекодировать?
Строка была приведена полностью для более полного представления о чем идет реч.
О! Как раз на днях по этой теме объяснял следующее:
Формат кодирования заголовков выглядит примерно таким образом
(на примере темы данного треда):
=?KOI8-R?Q?=CB=CF=C4=C9=D2=CF=D7=CB=C1=3F?=
=? - начало строки
KOI8-R - кодировка заголовка (может быть, например, WIN-1251)
? - разделитель
Q - тип кодирования.
Я знаю 2 варианта:
Q - Quoted-printable. Это то, про что Вы спрашивали. То есть ряд
символов (английские буквы, цифры, некоторые другие знаки) пишутся как
есть, остальное кодируется в шестнадцатеричном виде с добавлением в
начале знака "="
B - Base64. Я обычно им пользуюсь
? - разделитель
=CB=CF=C4=C9=D2=CF=D7=CB=C1=3F - собственно сам текст заголовка
?= - конец строки
Где-то валялся у меня класс для декодинга таких вещей на VB6... Если
надо, поищу. На .Net тоже есть, только выдрать из DLL будет трудновато
А вот дальше идет как раз класс...
Павел, когда ждать цитирование кода и прикрепления мелких файлов? Ты опять расслабляешься
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
 ataBindingBehavior = 0 'vbNone
 ataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "Base64"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'*************************************************************************************
'Base 64 Encoding class
'
'Author: Wil Johnson
' Wil.Johnson@att.net
'
'Version: 1.1
'
'Date: 3/21/2000
'
'Notes:
' This code is for example purposes only, and is provided as-is. While it has
' worked well under limited testing, the current error handling is minimal and
' should be expanded upon before release into a production environment. Please
' report all bugs found to the author for correction, even if you have already
' corrected them yourself.
'
' Again, this code is a rough draft. Feel free to use it, but do so at your own
' risk. These release notes must also remain intact.
'
'*************************************************************************************
Option Explicit
Private m_bytIndex(0 To 63) As Byte
Private m_bytReverseIndex(0 To 255) As Byte
Private Const k_bytEqualSign As Byte = 61
Private Const k_bytMask1 As Byte = 3 '00000011
Private Const k_bytMask2 As Byte = 15 '00001111
Private Const k_bytMask3 As Byte = 63 '00111111
Private Const k_bytMask4 As Byte = 192 '11000000
Private Const k_bytMask5 As Byte = 240 '11110000
Private Const k_bytMask6 As Byte = 252 '11111100
Private Const k_bytShift2 As Byte = 4
Private Const k_bytShift4 As Byte = 16
Private Const k_bytShift6 As Byte = 64
Private Const k_lMaxBytesPerLine As Long = 152
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)
Public Function Encode(ByRef sInput As String) As String
If sInput = "" Then Exit Function
 im bytTemp() As Byte
bytTemp = StrConv(sInput, vbFromUnicode)
Encode = EncodeArr(bytTemp)
End Function
Public Function EncodeFromFile(sFileName As String) As String
On Error GoTo ErrorHandler:
 im bytFile() As Byte
 im iFile As Integer
'get new file handle
iFile = FreeFile
Open sFileName For Input As #iFile
'size the array to the size of the file
ReDim bytFile(0 To VBA.LOF(iFile) - 1) As Byte
'get everything in the file
Input #iFile, bytFile
Close #iFile
'encode it
EncodeFromFile = EncodeArr(bytFile)
GoTo Done:
ErrorHandler:
EncodeFromFile = ""
Resume Done:
Done:
On Error Resume Next
Close #iFile
End Function
Public Function EncodeArr(ByRef bytInput() As Byte) As String
On Error GoTo ErrorHandler:
 im bytWorkspace() As Byte 'array for the "rough draft" of the encoded data
 im bytResult() As Byte 'array for the "final draft"
 im bytCrLf(0 To 3) As Byte 'array that will contain vbCrLf, for CopyMemory purposes
 im lCounter As Long 'counter used to iterate through input bytes
 im lWorkspaceCounter As Long 'counter used to iterate through workspace bytes
 im lLineCounter As Long 'counter used when inserting CrLfs
 im lCompleteLines As Long 'used for calculations when inserting CrLfs
 im lBytesRemaining As Long 'used to determine how much work is left after coming out of a loop
'pointers
 im lpWorkSpace As Long 'pointer to bytWorkspace. it's offset will change as bytes are copied out of the array
 im lpResult As Long 'pointer to bytResult. it's offset will also change
 im lpCrLf As Long 'pointer to bytCrLf. it is not offset and will not change
'create a workspace larger than we need
'this is to prevent VB from having to allocate memory constantly
If UBound(bytInput) < 1024 Then
ReDim bytWorkspace(LBound(bytInput) To (LBound(bytInput) + 4096)) As Byte
Else
ReDim bytWorkspace(LBound(bytInput) To (UBound(bytInput) * 4)) As Byte
End If
lWorkspaceCounter = LBound(bytWorkspace)
'step through in 3 byte increments
For lCounter = LBound(bytInput) To (UBound(bytInput) - ((UBound(bytInput) Mod 3) + 3)) Step 3
'result set byte 1 = 6 most significant bits of first byte of input set
'bits are right shifted by 2
bytWorkspace(lWorkspaceCounter) = m_bytIndex((bytInput(lCounter) \ k_bytShift2))
'result set byte 2 = 2 least significant bits of first byte and 4 most significant bits of second byte of input set
'bits from first byte are left shifted by 4
'bits from second byte are right shifted by 4
bytWorkspace(lWorkspaceCounter + 2) = m_bytIndex(((bytInput(lCounter) And k_bytMask1) * k_bytShift4) + ((bytInput(lCounter + 1)) \ k_bytShift4))
'result set byte 3 = 4 least significant bits of second byte and 2 most significant bits of third byte of input set
'bits from second byte are left shifted by 2
'bits from third byte are right shifted by 6
bytWorkspace(lWorkspaceCounter + 4) = m_bytIndex(((bytInput(lCounter + 1) And k_bytMask2) * k_bytShift2) + (bytInput(lCounter + 2) \ k_bytShift6))
'result set byte 4 = 6 least significant bits of third byte of input set
'bits from third byte are not shifted at all
bytWorkspace(lWorkspaceCounter + 6) = m_bytIndex(bytInput(lCounter + 2) And k_bytMask3)
lWorkspaceCounter = lWorkspaceCounter + 8
Next lCounter
Select Case (UBound(bytInput) Mod 3):
'for information on how bits are masked and shifted, see above
Case 0:
bytWorkspace(lWorkspaceCounter) = m_bytIndex((bytInput(lCounter) \ k_bytShift2))
bytWorkspace(lWorkspaceCounter + 2) = m_bytIndex((bytInput(lCounter) And k_bytMask1) * k_bytShift4)
bytWorkspace(lWorkspaceCounter + 4) = k_bytEqualSign
bytWorkspace(lWorkspaceCounter + 6) = k_bytEqualSign
'base64 encoding allows no more than 76 characters per line,
'which translates to 152 bytes since the string is unicode
If lWorkspaceCounter <= k_lMaxBytesPerLine Then
'no need to line wrap.
EncodeArr = Left$(bytWorkspace, InStr(1, bytWorkspace, Chr$(0)) - 1)
'EncodeArr = bytWorkspace
Else
'must wrap lines
'first, populate the CrLf byte array
bytCrLf(0) = 13
bytCrLf(1) = 0
bytCrLf(2) = 10
bytCrLf(3) = 0
'size the end result array
ReDim bytResult(LBound(bytWorkspace) To UBound(bytWorkspace))
'get pointers to the various arrays
lpWorkSpace = VarPtr(bytWorkspace(LBound(bytWorkspace)))
lpResult = VarPtr(bytResult(LBound(bytResult)))
lpCrLf = VarPtr(bytCrLf(LBound(bytCrLf)))
'get count of complete lines
lCompleteLines = Fix(lWorkspaceCounter / k_lMaxBytesPerLine)
For lLineCounter = 0 To lCompleteLines
'copy first line
CopyMemory lpResult, lpWorkSpace, k_lMaxBytesPerLine
'offset the workspace and result pointers by k_lMaxBytesPerLine
lpWorkSpace = lpWorkSpace + k_lMaxBytesPerLine
lpResult = lpResult + k_lMaxBytesPerLine
'copy CrLf to result
CopyMemory lpResult, lpCrLf, 4&
'offset result pointer by another 4 bytes to account for the CrLf
lpResult = lpResult + 4&
Next lLineCounter
'check if there are any remaining bytes in an incomplete line to be copied
lBytesRemaining = lWorkspaceCounter - (lCompleteLines * k_lMaxBytesPerLine)
If lBytesRemaining > 0 Then
'copy remaining bytes to result
CopyMemory lpResult, lpWorkSpace, lBytesRemaining
End If
'no need to resize the result before passing it back to a string,
'since the empty space is made up of null chars that will terminate the
'string automatically.
'CopyMemory StrPtr(EncodeArr), VarPtr(bytResult(LBound(bytResult))), lpResult + lBytesRemaining
EncodeArr = Left$(bytResult, InStr(1, bytResult, Chr$(0)) - 1)
End If
Exit Function
ErrorHandler:
'on error just return an empty array
Erase bytResult
EncodeArr = bytResult
End Function
Public Function Decode(sInput As String) As String
If sInput = "" Then Exit Function
 ecode = StrConv(DecodeArr(sInput), vbUnicode)
End Function
Public Sub DecodeToFile(sInput As String, sFileName As String)
On Error GoTo ErrorHandler:
 im iFile As Integer
'do not overwrite existing files
If Dir(sFileName) <> "" Then
Err.Raise vbObjectError + 1000, "Base64.DecodeToFile", "File already exists."
GoTo Done:
End If
iFile = FreeFile
Open sFileName For Binary As #iFile
Put #iFile, , DecodeArr(sInput)
Close #iFile
Public Function DecodeArr(sInput As String) As Byte()
'returns a SBCS byte array
 im bytInput() As Byte 'base64 encoded string to work with
 im bytWorkspace() As Byte 'byte array to use as workspace
 im bytResult() As Byte 'array that result will be copied to
 im lInputCounter As Long 'iteration counter for input array
 im lWorkspaceCounter As Long 'iteration counter for workspace array
'get rid of CrLfs, and "="s since they're not required for decoding,
'and place the input in the byte array
bytInput = Replace(Replace(sInput, vbCrLf, "", "=", ""
'size the workspace
ReDim bytWorkspace(LBound(bytInput) To (UBound(bytInput) * 2)) As Byte
lWorkspaceCounter = LBound(bytWorkspace)
'pass bytes back through index to get original values
For lInputCounter = LBound(bytInput) To UBound(bytInput)
bytInput(lInputCounter) = m_bytReverseIndex(bytInput(lInputCounter))
Next lInputCounter
For lInputCounter = LBound(bytInput) To (UBound(bytInput) - ((UBound(bytInput) Mod 8) + 8)) Step 8
'left shift first input byte by 2 and right shift second input byte by 4
bytWorkspace(lWorkspaceCounter) = (bytInput(lInputCounter) * k_bytShift2) + (bytInput(lInputCounter + 2) \ k_bytShift4)
'mask bits 5-8 of second byte, left shift it by 4
'right shift third byte by 2, add it to result of second byte
bytWorkspace(lWorkspaceCounter + 1) = ((bytInput(lInputCounter + 2) And k_bytMask2) * k_bytShift4) + _
 bytInput(lInputCounter + 4) \ k_bytShift2)
'mask bits 3-8 of third byte, left shift it by 6, add it to fourth byte
bytWorkspace(lWorkspaceCounter + 2) = ((bytInput(lInputCounter + 4) And k_bytMask1) * k_bytShift6) + _
bytInput(lInputCounter + 6)
lWorkspaceCounter = lWorkspaceCounter + 3
Next lInputCounter
'decode any remaining bytes that are not part of a full 4 byte block
Select Case (UBound(bytInput) Mod 8):
Case 3:
'left shift first input byte by 2 and right shift second input byte by 4
bytWorkspace(lWorkspaceCounter) = (bytInput(lInputCounter) * k_bytShift2) + (bytInput(lInputCounter + 2) \ k_bytShift4)
Case 5:
'left shift first input byte by 2 and right shift second input byte by 4
bytWorkspace(lWorkspaceCounter) = (bytInput(lInputCounter) * k_bytShift2) + (bytInput(lInputCounter + 2) \ k_bytShift4)
'mask bits 5-8 of second byte, left shift it by 4
'right shift third byte by 2, add it to result of second byte
bytWorkspace(lWorkspaceCounter + 1) = ((bytInput(lInputCounter + 2) And k_bytMask2) * k_bytShift4) + _
 bytInput(lInputCounter + 4) \ k_bytShift2)
lWorkspaceCounter = lWorkspaceCounter + 1
Case 7:
'left shift first input byte by 2 and right shift second input byte by 4
bytWorkspace(lWorkspaceCounter) = (bytInput(lInputCounter) * k_bytShift2) + (bytInput(lInputCounter + 2) \ k_bytShift4)
'mask bits 5-8 of second byte, left shift it by 4
'right shift third byte by 2, add it to result of second byte
bytWorkspace(lWorkspaceCounter + 1) = ((bytInput(lInputCounter + 2) And k_bytMask2) * k_bytShift4) + _
 bytInput(lInputCounter + 4) \ k_bytShift2)
'mask bits 3-8 of third byte, left shift it by 6, add it to fourth byte
bytWorkspace(lWorkspaceCounter + 2) = ((bytInput(lInputCounter + 4) And k_bytMask1) * k_bytShift6) + _
bytInput(lInputCounter + 6)
lWorkspaceCounter = lWorkspaceCounter + 2
End Select
'size the result array
ReDim bytResult(LBound(bytWorkspace) To lWorkspaceCounter) As Byte
'if option base is set to 1 then don't increment this value
If LBound(bytWorkspace) = 0 Then
lWorkspaceCounter = lWorkspaceCounter + 1
End If
'move decoded data to a properly sized array
CopyMemory VarPtr(bytResult(LBound(bytResult))), VarPtr(bytWorkspace(LBound(bytWorkspace))), lWorkspaceCounter
'return
 ecodeArr = bytResult
End Function
Павел, сработает ли код приведенный SNE в Вашем примере? Я попробовал результат не впечетляет, или, возможно требуется дополнительная перекодировка полученного после декодирования текста из koi8 в windows-1251?
Попробую сам, но пока это вопрос.
Я попробовал перекодировать Ваш текст (koi8-r?Q?) у меня ничего не получилось. Хотя (koi8-r?B?) декодировался нормально. Если можете, подскажите, что делать c Q-типом.
Q тип нужно:
1. разбить в массив - strArray = Split(str, "="
2. создать Byte массив размерности такой же, как и получившийся массив
3. Циклом преобразовать все значения, в строку - str = str & Chr(Val$(strAray(i)))
ИМХО, чушь какая-то... Для раскодирования Quoted-Printable нужно
пройтись циклом по строке, пропускать все символы, а при встрече "="
получить следующие за ним 2 символа и, приняв их за шестнадцатеричное
предстанвление кода символа, преобразовать в символ.
Вообще, вот код моего модуля на VB .NET для полного разбора таких
заголовоков.
Public Module mdlMIME
Public Enum EncType As Integer
None = 0
Base64 = 1
QuotedPrintable = 2
End Enum
Public Function MIMEDecode(ByVal Source As String) As String
Dim enc As String
Dim typeenc As String
Dim pr As String
Dim pp As Integer
Dim np As Integer
Dim Result As String
Dim Header As String
Dim Footer As String
pp = np
np = Source.IndexOf("?=", pp + 1)
If np = 0 Then
np = Source.Length
Footer = ""
Else
'np = np
If np <> Source.Length - 1 Then
Footer = Source.Substring(np + 2).Trim
End If
End If
pr = Source.Substring(pp + 1, np - pp - 1)
Select Case typeenc.Substring(0, 1)
Case "B"
Result = B64Decode(pr)
Case "Q"
Result = QPDecode(pr)
Case Else
Result = Source
End Select
Catch e As Exception
MsgBox(e.Message)
Finally
Dim temp As String
Dim encoding1 As Text.Encoding = Text.Encoding.GetEncoding(1251) 'Здесь по идее нужно поставить дефолтную кодировку
Dim encoding2 As Text.Encoding = Text.Encoding.GetEncoding(1251)
'Пока в этом смысла нет (пока я убрал дефолтную кодировку)
'temp = encoding1.GetString(encoding2.GetBytes(Header))
temp = Header
encoding1 = Text.Encoding.GetEncoding(EncFromString(enc))
temp &= encoding1.GetString(encoding2.GetBytes(Result))
If Footer <> "" Then
temp &= MIMEDecode(Footer)
End If
MIMEDecode = temp
End Try
End Function
Private Function EncFromString(ByVal Source As String) As Integer
Select Case Source.ToLower
Case "win-1251", "windows-1251"
Return 1251
Case "koi8-r"
Return 20866
End Select
End Function
Public Function QPDecode(ByVal Source As String) As String
Dim result As String
Dim i As Integer
Dim l As Integer
Dim C As Char
l = Source.Length
i = 1
Do While i <= l
C = Convert.ToChar(Mid(Source, i, 1))
If C = "=" Then
Try
result = result & Chr(CInt("&H0" & Mid(Source, i + 1, 1) & Mid(Source, i + 2, 1)))
Catch ex As Exception
'MsgBox("Ошибка в Utils.mdlMIMEDecode.QPDecode" & vbCrLf & ex.Message)
End Try
i = i + 3
Else
result = result & C
i = i + 1
End If
Loop
Return result
End Function
Public Function B64Decode(ByVal Source As String) As String
Dim pr As String
Dim i As Integer
'Удаление символов, не соответствующих base64-алфавиту
For i = 1 To Source.Length
If InStr(1, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", Mid(Source, i, 1)) <> 0 Then
pr = pr & Mid(Source, i, 1)
End If
Next i
If (pr.Length Mod 4) <> 0 Then
pr &= StrDup(4 - (pr.Length Mod 4), "=")
End If
Dim encoding As System.Text.Encoding = System.Text.Encoding.GetEncoding(1251)
Try
pr = encoding.GetString(Convert.FromBase64String(pr))
Catch ex As Exception
MsgBox("Ошибка в Utils.mdlMIMEDecode.B64Decode" & vbCrLf & ex.Message)
End Try
Return pr
End Function
Public Function MIMEEncode(ByVal Source As String, ByVal EncodeType As EncType, ByVal Encoding As String) As String
Dim temp As String
Dim EType As String
Select Case EncodeType
Case EncType.Base64
EType = "B"
Case EncType.QuotedPrintable
EType = "Q"
End Select
Dim enc As Text.Encoding = Text.Encoding.GetEncoding(Encoding)
temp = Convert.ToBase64String(enc.GetBytes(Source))
Return "=?" & Encoding & "?" & EType & "?" & temp & "?="
End Function
End Module
Предложенный Вами вариант для .NET я переделал под VB6
Пример:
' str = QPDecode("CB=CF=C4=C9=D2=CF=D7=CB=C1=3F="
'
'После чего содержимое переменной str
'перекодируем в Windows-1251
'Как результат: "кодировка?"
'
Public Function QPDecode(ByVal Source As String) As String
Dim result As String
Dim i As Integer
Dim l As Integer
Dim v As Variant
'конвертируем строку в массив
v = Split(Source, "="
l = UBound(v)
i = 0
'проверяем действительно ли получен массив
If IsArray(v) And l >= 0 Then
'собираем строку отчета
 o While i <= l
result = result & Chr(CInt("&H0" & CStr(v(i))))
i = i + 1
Loop
Erase v
Else
QPDecode = Source
End If
QPDecode = result
result = vbNullString
End Function