Access

  MS Access 2010+  |  Przetwarzanie tekstu  |   VBA 7.0



• Konwersja tekstu między różnymi stronami kodowymi (Code Page).

Strona kodowa – zestaw znaków przypisanych poszczególnym kodom binarnym (kodom znaku). Różne strony kodowe przyjmują dla tego samego kodu odmienne znaki, a ponadto różnią się samymi zestawami znaków. Tekst pisany „po polsku” po konwersji na inną stronę kodową może być częściowo nieczytelny, bądź zupełnie nieczytelny (popularnie określany jako tekst pisany pisany „robaczkami”).

Słów kilka o stronach kodowych (Code Page).

7-bitowy kod ASCII

Dawno, dawno temu zestaw znaków kodowany był jako 7-bitowy kod ASCII, który przyporządkowywał liczby z zakresu 0−127 literom alfabetu angielskiego, cyfrom, znakom przestankowym i innym symbolom oraz poleceniom sterującym.

8-bitowy kod ASCII

Ponieważ kod ASCII był 7-bitowy, a już od dawna komputery operują na 8-bitowych bajtach, dodatkowy (ósmy) bit został wykorzystany na powiększenie zbioru kodowanych znaków ze 128 znaków do 256 znaków. W ramach 256 znaków nie było możliwości stworzenie uniwersalnego zestawu obejmującego znaki wszystkich alfabetów. Powstało więc wiele różnych rozszerzeń ASCII wykorzystujących ósmy bit, które zostały nazywane stronami kodowymi (Code Page) i którymi w jakiś sposób trzeba było zarządzać.

Unicode

Próbą rozwiązania problemu stron kodowych było wprowadzenie zestawu znaków Unicode, obejmującego ponad milion znaków wszystkich alfabetów świata, nawet takich jak hieroglify. Unicode jest jednak tylko zestawem samych znaków, więc aby go zastosować, korzysta się z systemów ich kodowania, do których należą m.in. UTF8, UTF16UTF32 Również kodowanie UTF8 można uważać za rozszerzenie ASCII, tutaj jednak dodatkowe znaki są kodowane na 2 i więcej bajtach.

Częściej używane inne strony kodowe

Windows 1250
strona kodowa używana w systemie Microsoft Windows do reprezentowania tekstów w językach Europy środkowej i wschodniej, które używają alfabetu łacińskiego, takich jak polski, czeski, słowacki, węgierski, słoweński, bośniacki, chorwacki, serbski (alfabet łaciński), rumuński i albańskim. Może być również używana do tekstów w języku niemieckim, gdyż teksty niemieckojęzyczne zakodowane w Windows-1250 i Windows-1252 są identyczne.
ISO 8859-2
znany jako ISO Latin-2, bądź alfabet łaciński dla Europy środkowej i wschodniej, składa się ze 191 znaków łacińskiego pisma, z czego każdy znak zapisywany jest przy pomocy ośmiu bitów.
Używając tego zestawu znaków da się zapisać teksty w językach bośniackim, chorwackim, czeskim, węgierskim, polskim, rumuńskim, serbskim, serbsko–chorwackim, słowackim, słoweńskim. Dodatkowo nadaje się do przedstawienia kilku zachodnioeuropejskich języków takich jak język niemiecki, czy też angielski, choć dla tych języków powinno się zastosować stronę kodową ISO 8859-1.
Dawniej większość polskich stron internetowych (poprawnie wyświetlanych) zapisywanych było w formacie ISO 8859-2.
UTF8
system kodowania Unicode, wykorzystujący od 1 do 6 bajtów do zakodowania pojedynczego znaku, w pełni kompatybilny z ASCII. Jest najczęściej wykorzystywany do przechowywania tekstu w plikach i w komunikacji sieciowej.
Standard ten, rozwiązuje problemy jakie stwarzały wcześniej opisane strony kodowe Windows 1250 oraz ISO 8859-2 gdyż obsługuje wiele języków i wszystkie nowoczesne aplikacje obsługują ten format kodowania tekstu. Bogactwo znaków UTF8, nie tylko diakrytycznych, umożliwia stosowanie przeróżnych znaków i symboli np. ⊗ ⊥ ⊄ ♠ ♣ ♥ ♦ ≅ ∴ ∫ ∑ ∉
Obecnie strony internetowe są najczęściej zapisywane w formacie UTF8.
852
Strona kodowa 852 używana jest w wierszu poleceń cmd.exe.
Po wywołaniu polecenia Dir i przekierowaniu zwracanego strumienia do pliku tekstowego:
Dir C:\Test\* /A:D /B /S > C:\Test\~Dir2File.txt
otrzymamy plik wynikowy zawierający tekst zgodny ze stroną kodową 852. Testowy podfolder o nazwie „Gżegżółka” zapisany będzie jako „Gľegľ˘ˆka” a podfolder „Pójść” jako „P˘j˜†”
Tekst pliku wymaga więc konwersji na format Unicode, bądź wcześniejszej zmiany strony kodowej interpretatora poleceń cmd.exe na Windows 1250.
Inne strony kodowe
Pełny wykaz stron kodowych MS Windows znajduje się na stronie: Code Page Identifiers

Zestawienie najczęściej używanych stron kodowych:

Code PageNazwaTypOpis
852ibm852OEM Latin 2Środkowoeuropejski (DOS)
Standard ANSII Windows
1250Windows-1250ANSIŚrodkowoeuropejski (Windows)
1251Windows-1251ANSICyrylica (Windows)
1252Windows-1252ANSIZachodnia Europa (Windows)
1253Windows-1253ANSIGrecki (Windows)
1254Windows-1254ANSITurecki (Windows)
1255Windows-1255ANSIHebrajski (Windows)
1256Windows-1256ANSIArabski (Windows)
1257Windows-1257ANSIBaltic (Windows)
1258Windows-1258ANSIWietnamski (Windows)
Standard ISO 8859
28591iso-8859-1Latin 1alfabet łaciński dla Europy zachodniej
28592iso-8859-2Latin 2alfabet łaciński dla Europy środkowej i wschodniej
28593iso-8859-3Latin 3alfabet łaciński dla Europy południowej
28594iso-8859-4Latin 4alfabet łaciński dla Europy północnej (bałtyckie)
28595iso-8859-5-cyrylica
28596iso-8859-6-alfabet arabski
28597iso-8859-7-alfabet grecki
28598iso-8859-8-alfabet hebrajski ISO-Visual (porządek wizualny)
28599iso-8859-9-alfabet turecki
Standard Unicode
65000utf-7-Unicode (UTF-7)
65001utf-8-Unicode (UTF-8)

Funkcje konwertujące tekst z ASCII na UTF8 oraz z UTF8 na ASCII

Na stronie ASCII »» UTF8 przedstawiłem funkcję konwertującą ciąg znaków z formatu Unicode na ciąg znaków zgodny z określoną stroną kodową ASCII, w tym także na format wielobajtowy np. UTF8, a na stronie UTF8 »» ASCII. przedstawiłem odwrotną funkcję, konwertującą ciąg znaków z formatu ASCII na format Unicode (domyślną stroną kodową ASCII).

A może uniwersalna funkcja konwertująca tekst na dowolną stronę kodową

Po tak długim wstępie pozostało tylko napisać funkcję konwertującą tekst z jednej strony kodowej na inną, w tym również konwersja z ASCII » UTF8 oraz z UTF8 » ASCII.

• Użyte funkcje API

  • Funkcja MultiByteToWideChar(...) która konwertuje tekst wejściowy ze strony kodowej ASCII, lub UTF8 na format Unicode (ciąg znaków UTF-16). Konwertowany tekst wejściowy nie musi być zestawem znaków wielobajtowych.
  • Funkcja WideCharToMultiByte(...) która konwertuje ciąg znaków z formatu Unicode na jednobajtowy ciąg znaków zgodny z określoną stroną kodową ASCII lub na ciąg znaków w formacie wielobajtowym np. UTF8.
  • funkcja GetACP() która zwraca bieżący identyfikator strony kodowej ANSI Windows (ACP) dla systemu operacyjnego

Konwersja tekstu pomiędzy stronami kodowymi, także wielobajtowymi np. UTF8


⊗ Funkcja tekstCodePageToCodePage( _
	sStrIn As String, _
	lFromCP As Long, _
	lOutCP As Long) As String
  • Konwertuje ciąg znaków (tekst) z jednej strony kodowej, na drugą stronę kodową. Konwersja tekstu nie ogranicza się do konwersji pomiędzy jednobajtowymi zestawami znaków ASCII, lecz możliwa jest również konwersję tekstu zgodnego z wielobajtowym zestawami np. UTF8
  • argumenty:
    • sStrIn
    • ciąg znaków który ma zostać przekonwertowany na docelową stronę kodową
    • sStrIn
    • identyfikator strony kodowej ANSI Windows (ACP), określający format tekstu wejściowego sStrIn
    • sStrIn
    • identyfikator strony kodowej ANSI Windows (ACP), na którą ma zostać przekonwertowany ciąg wejściowy sStrIn
  • zwraca:
  • Zwraca ciąg znaków w formacie zgodnym z identyfikatorem lOutCP docelowej strony kodowej ANSI Windows (ACP) . Przy niepowodzeniu zwraca ciąg zerowej długości "".
  • autor: Zbigniew Bratko
  • data: 03.02.2018
Option Compare Database
Option Explicit
#If VBA7 Then
  Private Declare PtrSafe Function WideCharToMultiByte Lib "kernel32" ( _
          ByVal CodePage As Long, _
          ByVal dwFlags As Long, _
          ByVal lpWideCharStr As LongPtr, _
          ByVal cchWideChar As Long, _
          ByVal lpMultiByteStr As String, _
          ByVal cchMultiByte As Long, _
          ByVal lpDefaultChar As LongPtr, _
          ByVal lpUsedDefaultChar As LongPtr) As Long
  Private Declare PtrSafe Function MultiByteToWideChar Lib "kernel32" ( _
          ByVal CodePage As Long, _
          ByVal dwFlags As Long, _
          ByVal lpMultiByteStr As String, _
          ByVal cchMultiByte As Long, _
          ByVal lpWideCharStr As LongPtr, _
          ByVal cchWideChar As Long) As Long
  Private Declare PtrSafe Function GetACP Lib "kernel32" () As Long
#Else
  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 String, _
          ByVal cchMultiByte As Long, _
          ByVal lpDefaultChar As Long, _
          ByVal lpUsedDefaultChar As Long) As Long
  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
  Private Declare Function GetACP Lib "kernel32" () As Long
#End If


Public Function tekstCodePageToCodePage( _
								sStrIn As String, _
								lFromCP As Long, _
								lOutCP As Long) As String
Dim lLenStrOut  As Long
Dim sAscii      As String
Dim lLenAscii   As Long
Dim lCurrentCP   As Long

  ' pobierz aktualna stronę kodową
  lCurrentCP = GetACP
  
  If lFromCP = lCurrentCP Then
   ' nie konwertuj, ciąg wejściowy jest zgodny z aktualną stroną kodową
    sAscii = sStrIn
  Else
    lLenAscii = MultiByteToWideChar(lFromCP, 0&, sStrIn, Len(sStrIn), 0&, 0&)
    ' przygotuj bufor na przyjęcie ciągu po konwersji
    sAscii = String$(lLenAscii, vbNullChar)
    ' konwertuj wejściowy ciąg na ASCII (bieżącą stronę kodową)
    lLenAscii = MultiByteToWideChar(lFromCP, 0&, sStrIn, Len(sStrIn), _
                                    StrPtr(sAscii), lLenAscii)
  End If
  
  If lOutCP = lCurrentCP Then
   ' nie konwertuj, ciąg wyjściowy sAscii jest zgodny z aktualną stroną kodową
    tekstCodePageToCodePage = sAscii
  Else
    ' pobierz wielkość potrzebnego buforu na wyjściowy ciąg znaków (strona kodowa lOutCP)
    lLenStrOut = WideCharToMultiByte(lOutCP, 0&, StrPtr(sAscii), Len(sAscii), 0&, 0&, 0&, 0&)
    ' przygotuj bufor na przyjęcie ciągu po konwersji
    tekstCodePageToCodePage = String$(lLenStrOut, vbNullChar)
    ' konwertuj ciąg na ASCII na określoną stronę kodową lOutCP
    lLenStrOut = WideCharToMultiByte(lOutCP, 0&, StrPtr(sAscii), Len(sAscii), _
                                    tekstCodePageToCodePage, lLenStrOut, 0&, 0&)
  End If

End Function

Test funkcji

Do testów weźmiemy tekst zawierający dość dużo polskich znaków diakrytycznych i spróbujmy przekonwertować ten tekst ze strony kodowej Windows-1250 (ANSI Środkowoeuropejski Windows) na format UTF8 (65001), potem na stronę kodową 852 (ibm852 Środkowoeuropejski DOS), natępnie na iso-8859-2 (Latin 2, alfabet łaciński dla Europy środkowej i wschodniej) i z powrotem na Windows-1250 (ANSI Środkowoeuropejski Windows).

Private Const cMyString As String = _
		"Rzekł pajączek do pajączki:" & vbNewLine & _
		"Popatrz, ja mam złote rączki!" & vbNewLine & _
		"Już uprzędłem dla was nową" & vbNewLine & _
		"Pajęczynę własnościową." & vbNewLine & _
		"Będziesz pewnie bardzo rada," & vbNewLine & _
		"Bo to cud, mucha nie siada!" & vbNewLine & _
		"Na to ona z gniewem wrzaśnie:" & vbNewLine & _
		"A ma, durniu, siadać właśnie!" & vbNewLine & _
		"           (Bogdan Brzeziński)"


Public Function testCP() As String

  testCP = tekstCodePageToCodePage( _
        tekstCodePageToCodePage( _
        tekstCodePageToCodePage( _
        tekstCodePageToCodePage( _
        cMyString, 1250, 65001), 65001, 852), 852, 28592), 28592, 1250)

End Function

Po wywołaniu w oknie „Immediate” funkcji testCP(), która wykonuje cztery kolejne konwersje tekstu cMyString na następujące strony kodowe 1250 » 65001 » 28592 » 1250, otrzymujemy tekst taki sam jak tekst wejściowy cMyString przekazany do funkcji testCP(). Można powiedzieć, że funkcja konwertująca w tych przypadkach działa poprawnie .

Funkcja CodePageToCodePage
Tekst wynikowy po czterokrotnej konwersji.

Konwersja tekstu ASCII na format UTF8.

Funkcję tekstCodePageToCodePage(...) możemy wykorzystać zamiast przedstawionej na stronie ASCII »» UTF8 funkcji konwertującej tekst ASCII (1250) na format UTF8.

Jeżeli funkcję tekstCodePageToCodePage(...) wywołamy z argmentami lFromCP = 1250 oraz lOutCP = 65001, to możemy funkcję tą wykorzystać do konwertowania tekstu ze strony kodowej ASCII (1250) na format UTF8 zamiast przedstawionej na stronie ASCII »» UTF8 funkcji konwertującej tekst.

Public Function AsciiToUTF8(sStrIn As String) As String

  AsciiToUTF8 = tekstCodePageToCodePage(sStrIn, 1250, 65001)

End Function

Konwersja tekstu UTF8 na domyślną stronę kodową ASCII.

Jeżeli funkcję tekstCodePageToCodePage(...) wywołamy z argmentami lFromCP = 65001 oraz lOutCP = 1250, to możemy funkcję tą możemy wykorzystać do konwertowania tekstu z formatu UTF8 na domyślną stronę kodową ASCII (1250) zamiast przedstawionej na stronie UTF8 »» ASCII funkcji konwertującej tekst.

Public Function UTF8ToAscii(sStrIn As String) As String

 UTF8ToAscii = tekstCodePageToCodePage(sStrIn, 65001, 1250)

End Function