Excel vba кодировка файла

Смена кодировки строки с UTF-8 на ANSI (Windows-1251) и преобразование кодировки текста ANSI (Windows-1251) в UTF-8.

Перекодировка строки с UTF-8 в ANSI (Windows-1251) может понадобиться в VBA, например, при загрузке данных из CSV-файла с кодировкой UTF-8 на рабочий лист книги Excel.

Изменение кодировки текста UTF-8 на ANSI (Windows-1251) для 32-разрядных платформ:

Private Declare Function MultiByteToWideChar Lib «kernel32.dll» (ByVal CodePage As Long, ByVal dwFlags As Long, ByVal lpMultiByteStr As String, ByVal cchMultiByte As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long) As Long

Function FromUTF8(ByVal sText As String) As String

Dim nRet As Long, strRet As String

    strRet = String(Len(sText), vbNullChar)

    nRet = MultiByteToWideChar(65001, &H0, sText, Len(sText), StrPtr(strRet), Len(strRet))

FromUTF8 = Left(strRet, nRet)

End Function

Пример перекодировки строки с UTF-8 в ANSI (Windows-1251):

Sub Primer()

Dim num1 As Integer, a1 As String, str1 As String

    ‘Выбираем файл CSV с кодировкой UTF-8

    a1 = Application.GetOpenFilename(«Текст с разделителями,*.csv», , «Выбор файла»)

        If Right(a1, 4) <> «.csv» Then Exit Sub

    ‘Открываем файл и считываем текст в переменную

    num1 = FreeFile

        Open a1 For Input As num1

            str1 = Input(LOF(num1), num1)

        Close num1

    ‘Меняем кодировку с UTF-8 на Windows-1251

    str1 = FromUTF8(str1)

    ‘Работаем с текстом и вставляем нужные значения на рабочий лист

End Sub

Преобразование кодировки ANSI в UTF-8

Изменение кодировки текста ANSI (Windows-1251) на UTF-8 для 32-разрядных платформ:

Private Declare Function WideCharToMultiByte Lib «kernel32.dll» (ByVal CodePage As Long, ByVal dwFlags As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long, ByVal lpMultiByteStr As Long, ByVal cchMultiByte As Long, ByVal lpDefaultChar As Long, ByVal lpUsedDefaultChar As Long) As Long

Function ToUTF8(ByVal sText As String) As String

Dim nRet As Long, strRet As String

    strRet = String(Len(sText) * 2, vbNullChar)

    nRet = WideCharToMultiByte(65001, &H0, StrPtr(sText), Len(sText), StrPtr(strRet), Len(sText) * 2, 0&, 0&)

    ToUTF8 = Left(StrConv(strRet, vbUnicode), nRet)

End Function

Пример перекодировки строки с ANSI (Windows-1251) в UTF-8:

Изменение кодировки в 64-разрядных системах

Если у вас 64-разрядная версия VBA Excel, добавьте ключевое слово PtrSafe после оператора Declare и замените тип данных Long на LongPtr:

Private Declare PtrSafe Function MultiByteToWideChar Lib «kernel32.dll» (ByVal CodePage As LongPtr, ByVal dwFlags As LongPtr, ByVal lpMultiByteStr As String, ByVal cchMultiByte As LongPtr, ByVal lpWideCharStr As LongPtr, ByVal cchWideChar As LongPtr) As LongPtr

Private Declare PtrSafe Function WideCharToMultiByte Lib «kernel32.dll» (ByVal CodePage As LongPtr, ByVal dwFlags As LongPtr, ByVal lpWideCharStr As LongPtr, ByVal cchWideChar As LongPtr, ByVal lpMultiByteStr As LongPtr, ByVal cchMultiByte As LongPtr, ByVal lpDefaultChar As LongPtr, ByVal lpUsedDefaultChar As LongPtr) As LongPtr

В среде разработки VBA 7 тип данных LongPtr на 32-разрядных платформах интерпретируется как Long, а в 64-разрядных — как LongLong.


Функции ChangeFileCharset и ChangeTextCharset предназначены для изменения кодировки символов в текстовых файлах и строках.

Исходную и конечную (желаемую) кодировку можно задать в параметрах вызова функций.

ВНИМАНИЕ: Функции чтения и сохранения текста в файл в заданной кодировке

Список доступных на вашем компьютере кодировок можно найти в реестре Windows в ветке
HKEY_CLASSES_ROOTMIMEDatabaseCharset

Среди доступных кодировок есть koi8-r, ascii, utf-7, utf-8, Windows-1250, Windows-1251, Windows-1252, и т.д. и т.п.

Определить исходную и конечную кодировку можно, воспользовавшись онлайн-декодером:
http://www.artlebedev.ru/tools/decoder/advanced/
(после преобразования снизу будет написано, из какой кодировки в какую переведён текст)

Sub ПримерИспользования_ChangeTextCharset()
 
    ИсходнаяСтрока = "бНОПНЯ"
    ' вызываем функцию ChangeTextCharset с указанием кодировок
    ' (меняем кодировку с KOI8-R на Windows-1251)
    ПерекодированнаяСтрока = ChangeTextCharset(ИсходнаяСтрока, "Windows-1251", "KOI8-R")
 
    MsgBox "Результат перекодировки: """ & ПерекодированнаяСтрока & """", _
           vbInformation, "Исходная строка: """ & ИсходнаяСтрока & """"
 
End Sub
Function ChangeFileCharset(ByVal filename$, ByVal DestCharset$, _
                           Optional ByVal SourceCharset$) As Boolean
    ' функция перекодировки (смены кодировки) текстового файла
    ' В качестве параметров функция получает путь filename$ к текстовому файлу,
    ' и название кодировки DestCharset$ (в которую будет переведён файл)
    ' Функция возвращает TRUE, если перекодировка прошла успешно
    On Error Resume Next: Err.Clear
    With CreateObject("ADODB.Stream")
        .Type = 2
        If Len(SourceCharset$) Then .Charset = SourceCharset$    ' указываем исходную кодировку
        .Open
        .LoadFromFile filename$    ' загружаем данные из файла
        FileContent$ = .ReadText   ' считываем текст файла в переменную FileContent$
        .Close
        .Charset = DestCharset$    ' назначаем новую кодировку
        .Open
        .WriteText FileContent$
        .SaveToFile filename$, 2   ' сохраняем файл уже в новой кодировке
        .Close
    End With
    ChangeFileCharset = Err = 0
End Function
Function ChangeTextCharset(ByVal txt$, ByVal DestCharset$, _
                           Optional ByVal SourceCharset$) As String
    ' функция перекодировки (смены кодировки) текстовоq строки
    ' В качестве параметров функция получает текстовую строку txt$,
    ' и название кодировки DestCharset$ (в которую будет переведён текст)
    ' Функция возвращает текст в новой кодировке
    On Error Resume Next: Err.Clear
    With CreateObject("ADODB.Stream")
        .Type = 2: .Mode = 3
        If Len(SourceCharset$) Then .Charset = SourceCharset$    ' указываем исходную кодировку
        .Open
        .WriteText txt$
        .Position = 0
        .Charset = DestCharset$    ' назначаем новую кодировку
        ChangeTextCharset = .ReadText
        .Close
    End With
End Function

‘ Функция для перекодировки файла в UTF-8 без BOM (то же самое, что и UTF-8, только без первых 3 байтов)

Function ChangeFileCharset_UTF8noBOM(ByVal filename$, Optional ByVal SourceCharset$) As Boolean
    ' функция перекодировки (смены кодировки) текстового файла
    ' В качестве параметров функция получает путь filename$ к текстовому файлу,
    ' Функция возвращает TRUE, если перекодировка прошла успешно
    On Error Resume Next: Err.Clear
    DestCharset$ = "utf-8"
    With CreateObject("ADODB.Stream")
        .Type = 2
        If Len(SourceCharset$) Then .Charset = SourceCharset$        ' указываем исходную кодировку
        .Open
        .LoadFromFile filename$        ' загружаем данные из файла
        FileContent$ = .ReadText        ' считываем текст файла в переменную FileContent$
        .Close
        .Charset = DestCharset$        ' назначаем новую кодировку "utf-8"
        .Open
        .WriteText FileContent$
 
        'Write your data into the stream.

        Dim binaryStream As Object
        Set binaryStream = CreateObject("ADODB.Stream")
        binaryStream.Type = 1
        binaryStream.Mode = 3
        binaryStream.Open
        'Skip BOM bytes
        .Position = 3
        .CopyTo binaryStream
        .Flush
        .Close
        binaryStream.SaveToFile filename$, 2
        binaryStream.Close
    End With
    ChangeFileCharset_UTF8noBOM = Err = 0
End Function

Функция перекодировки текста в UTF-8 без BOM

Function EncodeUTF8noBOM(ByVal txt As String) As String
    For i = 1 To Len(txt)
        l = Mid(txt, i, 1)
        Select Case AscW(l)
            Case Is > 4095: t = Chr(AscW(l)  64  64 + 224) & Chr(AscW(l)  64) & Chr(8 * 16 + AscW(l) Mod 64)
            Case Is > 127: t = Chr(AscW(l)  64 + 192) & Chr(8 * 16 + AscW(l) Mod 64)
            Case Else: t = l
        End Select
        EncodeUTF8noBOM = EncodeUTF8noBOM & t
    Next
End Function
  • 144335 просмотров

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

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

Sub CallKML(control As IRibbonControl)
Dim i As Integer
Dim fn As Long
Dim npg As Integer
If ActiveSheet.Name = "Вуличні ПГ" Then wnet = "Вуличні ПГ"
If ActiveSheet.Name = "Об'єктові ПГ" Then wnet = "Об'єктові ПГ"
fn = FreeFile
Open ThisWorkbook.Path & "ResultExcel.kml" For Output As fn
Print #fn, "<?xml version='1.0' encoding='UTF-8'?>"
Print #fn, "<kml xmlns='http://www.opengis.net/kml/2.2'>"
Print #fn, "<Document>"
Print #fn, "<Style id=""placemark-blue"">"
Print #fn, "<IconStyle>"
Print #fn, "<Icon>"
Print #fn, "<href>images/1.png</href>"
Print #fn, "</Icon>"
Print #fn, "<hotSpot x='0.5' y='0.5' xunits='fraction' yunits='fraction'/>"
Print #fn, "</IconStyle>"
Print #fn, "<LabelStyle><color>ff000000</color><scale>0.5</scale><face>Arial</face><visible>1</visible><style>00000000</style></LabelStyle>"
Print #fn, "</Style>"
Print #fn, "<Style id=""placemark-red"">"
Print #fn, "<IconStyle>"
Print #fn, "<Icon>"
Print #fn, "<href>images/2.png</href>"
Print #fn, "</Icon>"
Print #fn, "<hotSpot x='0.5' y='0.5' xunits='fraction' yunits='fraction'/>"
Print #fn, "</IconStyle>"
Print #fn, "<LabelStyle><color>ff000000</color><scale>0.5</scale><face>Arial</face><visible>1</visible><style>00000000</style></LabelStyle>"
Print #fn, "</Style>"
Print #fn, "<Style id=""placemark-orange"">"
Print #fn, "<IconStyle>"
Print #fn, "<Icon>"
Print #fn, "<href>images/3.png</href>"
Print #fn, "</Icon>"
Print #fn, "<hotSpot x='0.5' y='0.5' xunits='fraction' yunits='fraction'/>"
Print #fn, "</IconStyle>"
Print #fn, "<LabelStyle><color>ff000000</color><scale>0.5</scale><face>Arial</face><visible>1</visible><style>00000000</style></LabelStyle>"
Print #fn, "</Style>"
        
npg = 0
For i = 2 To 1001
If ActiveSheet.Cells(i, 2) <> "" Then
npg = npg + 1

Print #fn, "<Placemark>"
If wnet = "Вуличні ПГ" Then Print #fn, "<description>" & "Вуличні ПГ" & "</description>"
If wnet = "Об'єктові ПГ" Then Print #fn, "<description>" & "Об'єктові ПГ" & "</description>"
Print #fn, "<name>" & ActiveSheet.Cells(i, 2) & "</name>"

      If ActiveSheet.Cells(i, 3) = "Справний" Then Print #fn, "<styleUrl>#placemark-blue</styleUrl>"
      If ActiveSheet.Cells(i, 3) = "Несправний" Then Print #fn, "<styleUrl>#placemark-red</styleUrl>"
           
Print #fn, "<ExtendedData> "
Print #fn, "<Data name='Вулиця'> <value>" & ActiveSheet.Cells(i, 1) & "</value> </Data>"
Print #fn, "<Data name='Технічний стан'> <value>" & ActiveSheet.Cells(i, 3) & "</value> </Data>"
Print #fn, "<Data name='Характер несправності'> <value>" & ActiveSheet.Cells(i, 4) & "</value> </Data>"
Print #fn, "<Data name='Належність'> <value>" & ActiveSheet.Cells(i, 5) & "</value> </Data>"
Print #fn, "<Data name='Примітка'> <value>" & ActiveSheet.Cells(i, 8) & "</value> </Data>"

If ActiveSheet.Cells(i, 9) <> "" Then Print #fn, "<Data name='gx_media_links'> <value>" & ActiveSheet.Cells(i, 9) & "</value> </Data>"
Print #fn, "</ExtendedData> "
Print #fn, "<Point> <coordinates>" & ActiveSheet.Cells(i, 7); "," & ActiveSheet.Cells(i, 6) & ",0.0</coordinates> </Point>"
Print #fn, "</Placemark>"
   End If
Next i
 
Print #fn, "</Document>"
Print #fn, "</kml>"

Close fn

ChangeFileCharset Filename$, "utf-8"

    MsgBox "Експорт таблиці в kml завершено"
End Sub
Function ChangeFileCharset(ByVal Filename$, ByVal DestCharset$, _
                           Optional ByVal SourceCharset$) As Boolean
      On Error Resume Next: Err.Clear
    With CreateObject("ADODB.Stream")
        .Type = 2
        If Len(SourceCharset$) Then .Charset = "Windows-1251"
        .Open
        .LoadFromFile "ResultExcel.kml"
        FileContent$ = .ReadText
        .Close
        .Charset = "utf-8"
        .Open
        .WriteText FileContent$
        .SaveToFile "ResultExcel.kml", 2
        .Close
    End With
    ChangeFileCharset = Err = 0
End Function

Eskander88

12 / 17 / 2

Регистрация: 02.11.2015

Сообщений: 222

1

09.03.2017, 14:34. Показов 23501. Ответов 15

Метки excel, кодировка, макрос (Все метки)


Студворк — интернет-сервис помощи студентам

Уважаемые форумчане!

Делаю скрипт для чтения и записи txt-файлов из Excel-вского файла по средствам VBA макросов.
Но всё упёрлось в проблему с кодировкой. Мне по умолчанию нужна UTF-8, так как она удобнее и файлы с которыми будет работать скрипт будут именно в ней.
Взял за основу для чтения и записи такой скрипт:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
Sub Test()
    ' Запись в файл
    Open "D:tmpfile.txt" For Output As #1
    Print #1, "My name is Петя!"
    Close #1
  
    ' Чтение из файла
    Open "D:tmpfile.txt" For Input As #1
    Dim s As String
    Input #1, s
    MsgBox s
    Close #1
End Sub

Проблема в том, что записывать (если понадобится) или считыватт прийдётся txt-шники с «charset = Utf-8», а Excel по-умолчанию работает с ANSI.

Из решений просмотрел в net-е. И думал нашёл функцию StrConv(s, vbUnicode) и StrConv(s, vbFromUnicode)
Но увы… получается, что она не выполняет конвертацию в кодировку юникод и обратно.

Существуют ли какие-нибудь функции для решения этой проблемы?



0



smeckoi77

61 / 60 / 16

Регистрация: 13.02.2017

Сообщений: 172

09.03.2017, 15:48

2

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Function ChangeFileCharset(ByVal filename$, ByVal DestCharset$, _
                           Optional ByVal SourceCharset$) As Boolean
    ' функция перекодировки (смены кодировки) текстового файла
   ' В качестве параметров функция получает путь filename$ к текстовому файлу,
   ' и название кодировки DestCharset$ (в которую будет переведён файл)
   ' Функция возвращает TRUE, если перекодировка прошла успешно
   On Error Resume Next: Err.Clear
    With CreateObject("ADODB.Stream")
        .Type = 2
        If Len(SourceCharset$) Then .Charset = SourceCharset$    ' указываем исходную кодировку
       .Open
        .LoadFromFile filename$    ' загружаем данные из файла
       FileContent$ = .ReadText   ' считываем текст файла в переменную FileContent$
       .Close
        .Charset = DestCharset$    ' назначаем новую кодировку
       .Open
        .WriteText FileContent$
        .SaveToFile filename$, 2   ' сохраняем файл уже в новой кодировке
       .Close
    End With
    ChangeFileCharset = Err = 0
End Function

Добавлено через 2 минуты

Visual Basic
1
ChangeFileCharset "C:/file.txt",  "Windows-1251", "UTF-8"



1



Eskander88

12 / 17 / 2

Регистрация: 02.11.2015

Сообщений: 222

09.03.2017, 21:37

 [ТС]

3

спасибо за помощь, к сожалению при испытании этого кода, ничего не получилось:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Function ChangeFileCharset(ByVal filename$, ByVal DestCharset$, Optional ByVal SourceCharset$) As Boolean
   ' функция перекодировки (смены кодировки) текстового файла
   ' В качестве параметров функция получает путь filename$ к текстовому файлу,
   ' и название кодировки DestCharset$ (в которую будет переведён файл)
   ' Функция возвращает TRUE, если перекодировка прошла успешно
   On Error Resume Next: Err.Clear
    With CreateObject("ADODB.Stream")
        .Type = 2
        If Len(SourceCharset$) Then .Charset = SourceCharset$    ' указываем исходную кодировку
       .Open
        .LoadFromFile filename$   ' загружаем данные из файла
       FileContent$ = .ReadText   ' считываем текст файла в переменную FileContent$
       .Close
        .Charset = DestCharset$   ' назначаем новую кодировку
       .Open
        .WriteText FileContent$
        .SaveToFile filename$, 2  ' сохраняем файл уже в новой кодировке
       .Close
    End With
    ChangeFileCharset = Err = 0
End Function
 
    
Sub TEST()
ChangeFileCharset "E:TMP1.txt", "Windows-1251", "UTF-8"
 
End Sub

Файл пересохраняется, но обработка происходит некорретно. В исходный файл 1.txt записывал строку «My name is Петя». Сам файл сохранил в 1251 (и в UTF 8) — возвращает что то вроде «My name is ????» или «My name is Ïåòÿ» и т.п (в зависимости от вариации. Но никакая не позволила определить, что к чему.

Я вот ещё думаю… Сам редактор VBA не может настраиваться, как скажем другие текстовые редакторы скажем в Notepad++ можно же кодировку менять.. Хотя конечно врятли… Просто заметил, что даже когда просто копируешь из VBA-редактора текст в буфер и вставляешь в блокнот, то сразу вылезают кракозяблы.?!



0



smeckoi77

61 / 60 / 16

Регистрация: 13.02.2017

Сообщений: 172

09.03.2017, 21:52

4

Попробуйте поменять местами

Visual Basic
1
ChangeFileCharset "C:/file.txt",  "UTF-8", "Windows-1251"



0



12 / 17 / 2

Регистрация: 02.11.2015

Сообщений: 222

09.03.2017, 22:23

 [ТС]

5

пробовал местами менять, и тоже в разных вариантах исходный текст писал (UTF8 и 1251), но при каждом включении макроса бьются русские символы, а иногда и английские… попробую наверное ещё с iconv() функцией скомбинировать код, она тоже как то на кодировке влияет, правда коряво и не так.. токо синтаксис с объектами для меня сложноват..



0



es geht mir gut

11264 / 4746 / 1183

Регистрация: 27.07.2011

Сообщений: 11,437

10.03.2017, 07:14

6

Eskander88, если файл в кодировке UTF-8, то функция ChangeFileCharset работает хорошо, я проверил.
Менять параметры местами не нужно.
Естественно, что применять эту функцию к файлу нужно только один раз.



0



185 / 183 / 31

Регистрация: 11.10.2016

Сообщений: 599

10.03.2017, 09:19

7

Цитата
Сообщение от Eskander88
Посмотреть сообщение

Просто заметил, что даже когда просто копируешь из VBA-редактора текст в буфер и вставляешь в блокнот, то сразу вылезают кракозяблы

чтобы не было кракозябров, нужно перед копированием всего лишь перейти на русскую раскладку клавиатуры



0



Eskander88

12 / 17 / 2

Регистрация: 02.11.2015

Сообщений: 222

10.03.2017, 17:20

 [ТС]

8

.. ещё раз протестил. Разобрался в чём дело. Проверил логику приложения, оказывается допустил банальную ошибку.

Visual Basic
1
ChangeFileCharset "D:TMP1.txt", "UTF-8", "UTF-8"

иначе конечно код считывает «UTF-8» а записывает в «Windows-1251», и опять такую же процедуры не проделаешь, а программа Блокнот так быстро не реагирует на смену кодировки.

Единственное какой для меня недостаток, то что для меня данная синтаксическая конструкция малость гоняет в ступор.
Так как считывание и запись происходит в теле функции, при этом работа происходит с объектом. Это конечно хоть, что-то, чтение и запись происходит, но от этого легче не становится.
Нужно же получить данные в переменную а потом обработать с помощью имеющихся процедур, прийдётся огород городить или в справочник погрузится.



0



es geht mir gut

11264 / 4746 / 1183

Регистрация: 27.07.2011

Сообщений: 11,437

10.03.2017, 17:27

9

Тогда можно предложить менять кодировку «на лету», а кодировку файла не трогать, пуст так и будет UTF-8.



0



Eskander88

12 / 17 / 2

Регистрация: 02.11.2015

Сообщений: 222

10.03.2017, 17:34

 [ТС]

10

я в общем то и полагал так делать, вроде

Visual Basic
1
2
3
4
5
6
7
8
    ' Чтение из файла
    Open "D:tmpfile.txt" For Input As #1
    Dim s As String
    Input #1, s
    ' Функция д/правильного перекодировки «s»   
    ' Произвольный код д/обработки «s»
    MsgBox s
    Close #1

.. и потом таким же макаром запись в *.txt файл, с перекодированием — если понадобится.
Но готовой функции как таковой нет, пока имеем такой предложенный вариант



0



SoftIce

es geht mir gut

11264 / 4746 / 1183

Регистрация: 27.07.2011

Сообщений: 11,437

10.03.2017, 17:41

11

Цитата
Сообщение от Eskander88
Посмотреть сообщение

Но готовой функции как таковой нет

На вскидку могу предложить что-то подобное.
Но с первым байтом нужно что-то делать.

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Sub Test()
    ' Запись в файл
    ' Open "C:tmpfile.txt" For Output As #1
    ' Print #1, "My name is Петя!"
    ' Close #1
  
    ' Чтение из файла
    Open "C:tmpfile.txt" For Input As #1
      Dim s As String
      Input #1, s
    Close #1
    MsgBox UTF8ToANSI(s)
End Sub
 
Public Function UTF8ToANSI(ByVal UTF8 As String) As String
    Dim UTF8Chars() As String, i As Long
    UTF8Chars = UTF8Chr
    For i = 128 To 255
         UTF8 = Replace(UTF8, UTF8Chars(i - 128), Chr(i))
    Next i
    UTF8ToANSI = UTF8
End Function
Public Function UTF8Chr()
    UTF8Chr = Split("Р‚&&Рѓ&&‚&&С“&&„&&…&&вЂ*&&‡&&€&&‰&&Р‰&&‹&&РЉ&&РЊ&&Р‹&&РЏ&&С’&&вЂЛњ&&’&&“&&”&&•&&–&&—&&ВЛњ&&в„ў&&С™&&›&&Сљ&&Сњ&&С›&&Сџ&&В*&&РЋ&&Сћ&&Р€&&В¤&&Тђ&&В¦&&В§&&РЃ&&В©&&Р„&&В«&&В¬&&В*&&В®&&Р‡&&В°&&В±&&Р†&&С–&&Т‘&&Вµ&&В¶&&В·&&С‘&&в„–&&С”&&В»&&СЛњ&&Р…&&С•&&С—&&Рђ&&Р‘&&Р’&&Р“&&Р”&&Р•&&Р–&&Р—&&РЛњ&&Р™&&Рљ&&Р›&&Рњ&&Рќ&&Рћ&&Рџ&&Р*&&РЎ&&Рў&&РЈ&&Р¤&&РҐ&&Р¦&&Р§&&РЁ&&Р©&&РЄ&&Р«&&Р¬&&Р*&&Р®&&РЇ&&Р°&&Р±&&РІ&&Рі&&Рґ&&Рµ&&Р¶&&Р·&&Рё&&Р№&&Рє&&Р»&&Рј&&РЅ&&Рѕ&&Рї&&СЂ&&СЃ&&С‚&&Сѓ&&С„&&С…&&С†&&С‡&&С€&&С‰&&СЉ&&С‹&&СЊ&&СЌ&&СЋ&&СЏ", "&&")
End Function



1



12 / 17 / 2

Регистрация: 02.11.2015

Сообщений: 222

10.03.2017, 17:48

 [ТС]

12

.. а если использовать вариант, что предложил smeckoi77? Только добавить возможность возврата свойства объекта — имею ввиду переменную с записанными в неё данными из файла. У меня к сожалению навыков по VBA маловато, не могу вернуть значение из функции от объекта, как я понял там должен быть специальный метод д/реализации. Использовать функции getFile и putFile на базе ChangeFileCharset



0



smeckoi77

61 / 60 / 16

Регистрация: 13.02.2017

Сообщений: 172

10.03.2017, 17:52

13

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Function ChangeTextCharset(ByVal txt$, ByVal DestCharset$, _
                           Optional ByVal SourceCharset$) As String
    ' функция перекодировки (смены кодировки) текстовой строки
   ' В качестве параметров функция получает текстовую строку txt$,
   ' и название кодировки DestCharset$ (в которую будет переведён текст)
   ' Функция возвращает текст в новой кодировке
   On Error Resume Next: Err.Clear
    With CreateObject("ADODB.Stream")
        .Type = 2: .Mode = 3
        If Len(SourceCharset$) Then .Charset = SourceCharset$    ' указываем исходную кодировку
       .Open
        .WriteText txt$
        .Position = 0
        .Charset = DestCharset$    ' назначаем новую кодировку
       ChangeTextCharset = .ReadText
        .Close
    End With
End Function



0



Homarty

141 / 119 / 29

Регистрация: 12.02.2017

Сообщений: 308

11.03.2017, 14:16

14

Кривой вариант немного

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Sub Test()
    Dim s As String
    Dim x() As Byte, xx() As Byte
    Dim i As Long, y As Long
    Dim ii As Long
    Open "d:tmpfile.txt" For Binary As #1
        s = "My name is Петя!"
        x = StrConv(s, vbUnicode)
        y = UBound(x)
        ReDim xx(0 To y  2)
        ii = 0
        For i = 0 To y Step 2
            xx(ii) = x(i)
            ii = ii + 1
        Next i
        Put #1, , xx
    Close #1
    y = FileLen("d:tmpfile.txt")
    Open "d:tmpfile.txt" For Binary As #1
        ReDim xx(0 To y - 1)
        ReDim x(0 To 2 * (y - 1))
        Get #1, , xx
        ii = 0
        For i = 0 To 2 * (y - 1) Step 2
            x(i) = xx(ii)
            ii = ii + 1
        Next i
        s = StrConv(x, vbFromUnicode)
        MsgBox s
    Close #1
End Sub

Добавлено через 16 часов 16 минут
Этот вариант более эстетичный

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
Sub Test()
    Dim  xx() As Byte
    xx = "My name is Петя!"
    Open "d:tmpfile.txt" For Binary As #1
        Put #1, , xx
    Close #1
    Open "d:tmpfile.txt" For Binary As #1
        Get #1, , xx
    Close #1
    MsgBox xx
End Sub



0



Eskander88

12 / 17 / 2

Регистрация: 02.11.2015

Сообщений: 222

12.03.2017, 15:57

 [ТС]

15

воспользовавшись примером, получилось нечто такое..

Функция для обмена данными с файлом

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Function exChangeContent(ByVal str$, ByVal filename$, ByVal DestCharset$, Optional ByVal SourceCharset$) As String
   ' функция перекодировки (смены кодировки) текстового файла
   ' В качестве параметров функция получает путь filename$ к текстовому файлу,
   ' и название кодировки DestCharset$ (в которую будет переведён файл)
   ' Функция возвращает TRUE, если перекодировка прошла успешно
   On Error Resume Next: Err.Clear
    With CreateObject("ADODB.Stream")
        .Type = 2
        If Len(SourceCharset$) Then .Charset = SourceCharset$    ' указываем исходную кодировку
       .Open
        .LoadFromFile filename$   ' загружаем данные из файла
       FileContent$ = .ReadText   ' считываем текст файла в переменную FileContent$
       .Close
        .Charset = DestCharset$   ' назначаем новую кодировку
       .Open
        .WriteText str$
        .SaveToFile filename$, 2  ' сохраняем файл уже в новой кодировке
       .Close
    End With
'   exChangeContent = FileContent$     ' обменяет знач.ячейки с содержимым файла
    exChangeContent = ActiveCell.Value  ' вернёт текущее значение ячейки
End Function

и такая процедура, для запуска

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Sub TEST()
 
Dim str As String  'Текст для записи в файл.
Dim pth As String  'Путь к файлу + имя.расширение
Dim chI As String  'Кодировка входящего текста
Dim chO As String  'Кодировка исходящего текста
 
' Значения по умолчанию
str = "Текстовый файлик"
pth = "E:TMP1.txt"
chI = "UTF-8"
chO = "UTF-8"
 
 
' Берём путь к файлу из соседней ячейки справа от выделенной
  If Len(ActiveCell.Offset(0, 1).Value) Then pth = ActiveCell.Offset(0, 1).Value
  
  
  str = ActiveCell.Text
  ActiveCell.Value = exChangeContent(str, pth, chI, chO)
 
End Sub

скрипт рабочий. В принципе необходимые задачи решает. Только получается что «On Error Resume Next: Err.Clear» вроде как в холостую, хотя по идеи надо как то обработать ошибку..



0



Eskander88

12 / 17 / 2

Регистрация: 02.11.2015

Сообщений: 222

20.04.2017, 18:40

 [ТС]

16

Переписал макрос. Сделал пример только для считывания.
Выглядит следующим образом:

Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Function exChangeContent(ByVal str$, ByVal pth$, ByVal chI$, Optional ByVal chO$) As String
   ' функция перекодировки (смены кодировки) текстового файла
   ' pth$ - путь к текстовому файлу с названием файла.расширение,
   ' chI$ кодировка в которой перевести для принятия файла
   ' ? Функция возвращает TRUE, если перекодировка прошла успешно
   On Error Resume Next: Err.Clear
    With CreateObject("ADODB.Stream")
 
 
        .Type = 2
        If Len(chO$) Then .Charset = chO$    ' указываем исходную кодировку
       .Open
        .LoadFromFile pth$   ' загружаем данные из файла
       FileContent$ = .ReadText   ' считываем текст файла в переменную FileContent$
       .Close
       
'   Блок кода, чтобы записать значение в файл
'       .Charset = chI$   ' назначаем новую кодировку
'        .Open
'      .WriteText str$
'       .SaveToFile pth$, 2  ' сохраняем файл уже в новой кодировке
'      .Close
 
    End With
    
 
    exChangeContent = FileContent$    
 
End Function
 
Sub TEST()
 
Dim str As String  'Текст для записи в файл.
Dim pth As String  'Путь к файлу + имя.расширение
Dim chI As String  'Кодировка входящего текста
Dim chO As String
 
' Значения по умолчанию
str = "Текстовый файлик"    ' Значение по-умолчанию
pth = "D:tmpрусский.txt"    ' Значение по-умолчанию
chI = "UTF-8"
chO = "UTF-8"
 
 
 
' Берём путь к файлу из ячейки, если есть
 If Len(ActiveCell.Offset(0, 1).Value) Then pth = ActiveCell.Offset(0, 1).Value
 
 
  
  str = ActiveCell.Text
  ActiveCell.Value = exChangeContent(str, pth, chI, chO)
 
 
End Sub

макрос не работает, если в ячейке справа, есть путь с кириллическими символами.
Этот EXCEL оказывается ещё тот русофоб ))), причём файлы с директорией типа «D:tmp§patagraph.txt» он считывает!

Подскажите пожалуйста..
Как сделать, чтобы макрос понимал в адресе к файлу не только UTF-8 в тексте файла, но и русскоязычные символы?
Или в чём хотя бы может быть дело?
варианты пробовал вместо «D:tmpрусский.txt» поставить так «D:tmpðóññêèé.txt» — ноль реакции

Добавлено через 7 минут
Тут ещё такой полтергейст для путей с кириллицей! Если файл существует, то скрипт вернёт его путь, — а если нет, — то сделает ячейку пустой?



0



Understanding Unicode variants like UTF8 and UTF16 and how they impact your Office VBA development is not so straightforward. This post will guide thru the experience of reading a text file with VBA, explain some of the pitfalls you may encounter on this path when dealing with different text encodings and file formats. We’ll shed some light on essential Unicode concepts you’ve preferred to leave aside until now, because – let’s face it – who wants to spend hours reading wikipedia or MSDN just to read a text file or understand the many rules and APIs for converting between encodings ?

No bulky and verbose .NET or undecipherable C++ code complications here.
Just immediately actionable, simple and humble, VBA code with one function to rule them all, and a 10 to 15 minutes read to understand it all.

Let’s start the experience right now. Try something:

  • Open Windows notepad and copy/paste (or type) this text:

    Fancy a café ? Or a piña colada ? – Oh, that’s so cliché!

    I have a strong impression of Déjà vu.

    You hide your true motive behind a friendly façade.

    (Just my lame try to compose words with diacritics, in english. Inspiration found here and here)

  • Save the file; let’s say in c:temptextfilesnotepad_text.txt

Now we’ll try to read it and display it in Visual Basic, line by line, as usual:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

Private Const TEST_FILE1 As String = «c:temptextfilesnotepad_text.txt«

Public Sub ReadTextFileByLine(ByVal psFileName As String)

  Dim hFile     As Integer

  Dim sLine     As String   ‘Current line read from the file

  Dim iLineCt   As Integer  ‘Line counter

  Debug.Print «—- ReadTextFileByLine()«

  hFile = FreeFile

  Open psFileName For Input Access Read As #hFile

  Debug.Print «[File: « & psFileName & «]«

  While Not EOF(hFile)

    iLineCt = iLineCt + 1

    Line Input #hFile, sLine

    Debug.Print iLineCt & «:« & sLine

  Wend

  Close hFile

  Debug.Print «[EOF]«

End Sub

Public Sub Test_ReadTextFileByLine()

  ReadTextFileByLine TEST_FILE1

End Sub

Executing the “Test_ReadTextFileByLine” Sub (in the debug window) from this simple code snippet should do it…

…or not (!). The accented characters don’t display correctly.

Let’s state some facts before banging our heads on that:

  • Two forms of Unicode will be of interest here: UTF8 and UTF16.
  • “Windows is Unicode“, UTF16 Unicode. So is VBA. Unicode is a big character set which is meant to be able to represent the character glyphs of different languages.
  • Unicode (UTF16) encodes a character with two bytes (a “wide” character, in extension “wide” strings).
    (Note: UCS2 is history, assume UCS2 (or UCS-2) is UTF16)
  • The representation of a character in Unicode is also called a code point.
  • UTF8: not all the characters in the Unicode character set really need two bytes of encoding. UTF8 is sort of a packed representation of a series of Unicode characters, where one or two bytes can be used to represent a wide character.

Back to reading our file

At this point, we can guess that our Notepad old friend (on Windows 10 en_US version in my setup), probably stored our text file using a UTF8 encoding, which VBA is not aware of. Let’s take a look at the bytes in the file:

We see at lines 0 and 30 that our accented “é” are encoded as the two bytes C3 and A9, so this is a UTF8 file.

Then, at some point, we’ll have to convert an UTF8 representation of string to a UTF16 VBA friendly one.

Unfortunately, VBA cannot help here, so let’s take a detour to our trustworthy Win32 API.

Converting from UTF8 to UTF16 with the Win32 API

You’ll find all the code in the demo database of my reading_text_files github repository.

The function we’ll need is MultiByteToWideChar(), which we can declare as:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

Public Const CP_UTF8                        As Long = 65001 ‘UTF-8 Code Page

Private Const ERROR_NO_UNICODE_TRANSLATION  As Long = 1113& ‘No mapping for the Unicode character exists in the target multi-byte code page.

Private Const MB_ERR_INVALID_CHARS          As Long = &H8&

#If Win64 Then

  Private Declare PtrSafe Function MultiByteToWideChar Lib «kernel32« ( _

    ByVal CodePage As Long, _

    ByVal dwFlags As Long, _

    ByVal lpMultiByteStr As LongPtr, _

    ByVal cchMultiByte As Long, _

    ByVal lpWideCharStr As LongPtr, _

    ByVal cchWideChar As Long) As Long

  Private Declare PtrSafe Function GetLastError Lib «kernel32« () As Long

#Else

  ‘Sys call to convert multiple byte chars to a char

  Private Declare Function MultiByteToWideChar Lib «kernel32« ( _

      ByVal lCodePage As Long, _

      ByVal dwFlags As Long, _

      ByVal lpMultiByteStr As Long, _

      ByVal cchMultiByte As Long, _

      ByVal lpWideCharStr As Long, _

      ByVal cchWideChar As Long) As Long

  Private Declare Function GetLastError Lib «kernel32« () As Long

#End If

We’re going to have two variable sources, byte arrays and strings, to convert to UTF16, this is the VBA API functions signatures we’ll use:

Public Function UTF8DecodeByteArrayToString( _

  ByRef pabBytes() As Byte, _

  Optional ByVal plStart As Long = 0&) As String

End Function

Public Function UTF8DecodeString(ByVal psSource As String) As String

End Function

Unicode normalization

There’s more than one way to represent a combination of characters in Unicode (MSDN). Extract:

Capital A with dieresis (umlaut) can be represented either as a single Unicode code point “Ä” (U+00C4) or the combination of Capital A and the combining Dieresis character (“A” + “¨”, that is, U+0041 U+0308). Similar considerations apply for many other characters with diacritic marks.

Simply put, a problem rises if we compare two Unicode strings that conceptually are the same, but use different code points (as the example above).

There are two more Win32 API functions that can help with that. One, NormalizeString(), transforms a Unicode string to a standard form, so it can be compared with another, even if the representations are different. The other, IsNormalizedString(), tests if a Unicode string is in the expected form.

There are a number of standard forms, but mainly, the one that “compresses” the code points into one character (I mean we get the attached form of “ae” instead of the “a” and “e”) is “NormalizationC”, value 1, from the following (C++) enumeration:

typedef enum _NORM_FORM {

  NormalizationOther  = 0,   // Not supported

  NormalizationC      = 0x1, // Each base + combining characters to canonical precomposed equivalent.

  NormalizationD      = 0x2, // Each precomposed character to its canonical decomposed equivalent.

  NormalizationKC     = 0x5, // Each base plus combining characters to the canonical precomposed

                             //   equivalents and all compatibility characters to their equivalents.

  NormalizationKD     = 0x6  // Each precomposed character to its canonical decomposed equivalent

                             //   and all compatibility characters to their equivalents.

} NORM_FORM;

Normalization is an optional step, but for security considerations, should be used.
I wrapped the API (and followed MSDN guidance) in these two VBA API functions, and two others to get any error information:

Public Function UcIsNormalizedString(ByVal psText As String) As Boolean

End Function

Public Function UcNormalizeString(ByVal psText As String) As String

End Function

Public Function UcGetLastError() As Long

End Function

Public Function UcGetLastErrorText() As String

End Function

Note:

I’m not following my coding guidelines for keeping error information inside a module, because we get an error either when calling Win32 API functions or a “logical” error when using the VBA API.

Then to test if something went wrong when calling UcNormalizeString() we have to test like that:

  If (UcGetLastError() <> 0) Or (Len(UcGetLastErrorText()) > 0) Then

    TryNormalization = «FAILED: « & UcGetLastErrorText()

  End If

You can see a test scenario, that I sort of translated from the ones in MSDN, in the Test_Normalization() Sub, which calls:

Private Function TryNormalization(ByVal psText As String) As String

  If UcIsNormalizedString(psText) Then

    TryNormalization = «Already normalized in this form«

    Exit Function

  End If

  TryNormalization = UcNormalizeString(psText)

  If (UcGetLastError() <> 0) Or (Len(UcGetLastErrorText()) > 0) Then

    TryNormalization = «FAILED: « & UcGetLastErrorText()

  End If

End Function

Back to reading our file – again

Ok, now we know for sure that our file is in UTF8.
And we know that we have a nice UTF8DecodeString() at our disposal.

Are we not tempted to make this slight adaptation to our ReadTextFileByLine() function ? (see the UTF8DecodeString call in this code):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

Public Sub ReadTextFileByLine_BadIdea(ByVal psFileName As String)

  Dim hFile     As Integer

  Dim sLine     As String   ‘Current line read from the file

  Dim iLineCt   As Integer  ‘Line counter

  Debug.Print «—- ReadTextFileByLine()«

  hFile = FreeFile

  Open psFileName For Input Access Read As #hFile

  Debug.Print «[File: « & psFileName & «]«

  While Not EOF(hFile)

    iLineCt = iLineCt + 1

    Line Input #hFile, sLine

    Debug.Print iLineCt & «:« & UTF8DecodeString(sLine)

  Wend

  Close hFile

  Debug.Print «[EOF]«

End Sub

Public Sub Test_ReadTextFileByLine_BadIdea()

  ReadTextFileByLine TEST_FILE1

End Sub

The result:

Whaaaat ? – Let’s debug that using the provided DumpStringBytes() function:

Which brings us to that output:

As we can see (and compare with the previous file’s hex dump), we do not have an UTF8 string in the variable sLine that is read by VBA from the file.

VBA converts the line it read from the file to a double byte (UTF16) string.

We cannot use VBA to read an UTF8 encoded text file using string variables.

Solution for reading and converting an UTF8 text file

We have to open the file in binary mode and read the contents in a byte array. This way VBA doesn’t do any conversion. We then just convert the byte array to an UTF8 string with the UTF8DecodeByteArrayToString().

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

Public Sub ReadNotepadTextFile(ByVal psFileName As String)

  Dim abString()  As Byte

  Dim sDecoded    As String

  Dim hFile       As Integer

  Debug.Print «—- ReadNotepadTextFile() in a byte array and convert to UTF8:«

  Debug.Print «[File: « & psFileName & «]«

  hFile = FreeFile

  Open psFileName For Binary Access Read As #hFile

  ReDim abString(1 To LOF(hFile)) As Byte

  Get #hFile, 1, abString

  Close hFile

  sDecoded = UTF8DecodeByteArrayToString(abString)

  Debug.Print sDecoded & vbCrLf & «[UTF8] (len & Len(sDecoded) & «)«

  Debug.Print «[EOF]«

End Sub

And finally, we get it right:

Other text file encodings and BOMs

If Notepad saves files in UTF8 encoding, there are other encodings of text and file formats.

UTF8 and UTF16 text files may have, or not, a special series of bytes at the start of the file called the BOM (Byte Order Mark). The BOM is a magic number that we can use to infer the file encoding and byte endianness (order of bytes) of the file contents.

Without the BOM, guessing the file encoding can be tough.
But when there’s one, we can use it to make the necessary conversions, like in the following GetFileText() function, that can handle the following file encodings:

  • UTF16 BE / LE (Big Endian / Little Endian) with or without BOM,
  • UTF8 with or without BOM
  • ANSI (8 bits characters text, different character sets or code pages possible)

This is the signature of the function (code in the MTextFiles module of the Reading_Text_Files.accdb project), with a bit of documentation:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

‘GetFileText()

‘Synopsis

‘———

‘ Reads a text file as a binary file in memory, and converts the bytes into a VB string.

‘ Detects BOM (Byte Order Mark) if there’s one and handles BE/LE (Big/Little Endian).

‘Parameters

‘———-

‘ psFilename

‘   Full or relative path of file to read

‘ psInFileFormat

‘   can be either «», «utf8» or «utf16». Any other value plays

‘   as «», and «» is assumed to be UTF16 LE with no BOM (as when we

‘   write a text file with VB/A).

‘ Rules applied for psInFileFormat:

‘   1. «» (empty), UTF16 LE nom BOM is assumed;

‘   2. «utf8», if there’s a BOM, its not included in the returned

‘      text, and the text is UTF8decoded to a VB/A string (UTF16)

‘   3. «utf16», it there’s a BOM, it is used to determine LE/BE. If

‘      there’s no BOM, LE is assumed.

‘Returns

‘——-

‘ The decoded file text.

‘Notes:

‘——

‘1. Can handle files which size fits into a long, and in memory.

‘2. Use error trapping in your calling code to catch unexpected errors.

‘3. You can use Notepad++ to produce test files (See the «Encoding» menu)

‘   Notepad++ encoding        | Translates to

‘   —————————+—————

‘   «Encode in ANSI»          | «»

‘   «Encode in UTF-8»         | «utf8»

‘   «Encode in UTF-8-BOM»     | «utf8»

‘   «Encode in UCS-2 BE BOM»  | «utf16»

‘   «Encode in UCS-2 LE BOM»  | «utf16»

‘ (Remember UCS-2 = UTF16)

‘4. Tools like Typora (https://typora.io/) save files in utf8 with no BOM,

‘   Use «GetFileText(YourFilename,»utf8») to load them.

Public Function GetFileText( _

  ByVal psFileName As String, _

  ByVal psInFileFormat As String) As String

End Function

There’s a “text_files_samples” directory, in the github repository, with one file for each possible text file encoding.
Note that there are no UTF16 files with no BOM, as I used Notepad++ to generate the files and there’s no option in Notepad++ to generate UTF16 files with no BOM.

The Test_ReadSampleEncodings() procedure will read and check the contents of each file with this GetFileText() function:

Conclusion

We’ve seen different representation of text and encodings like UTF8, UTF16. We’re now able to convert between those encodings. And we now know how to read text from files with some of the most common file formats we may encounter, with VBA.

From here, it should be quite easy to also write any of these formats (using files open in binary mode helps).

Downloads

Head to the Reading_Text_Files github repository to get the source code, the example files and the Access demo database.

(MIT Licence)

Понравилась статья? Поделить с друзьями:
  • Excel vba изменить имя листа
  • Excel vba изменить высоту строки
  • Excel vba измененная ячейка
  • Excel vba игнорировать ошибку
  • Excel vba значение ячейки в другую ячейку