Visual2000 Ј јрхив статей ј. олесова & ќ.ѕавловой

—оветы тем, кто программирует на Visual Basic

јндрей  олесов, ќльга ѕавлова

© 199x, јндрей  олесов, ќльга ѕавлова
јвторский вариант. —тать€ была опубликована c незначительной литературной правкой в ....


¬ этот выпуск мы включили несколько советов, св€занных с перекодировкой строковых переменных.

—овет 119. »спользуйте функции API дл€ перекодировки текста.

ѕри решении многих задач требуетс€ выполнить перекодировку текста из одной кодовой таблицы в другую, например из 866 в 1251, или наоборот. „тобы справитьс€ с этой проблемой, можно обратитьс€ к соответствующим функци€м API.

Ќапомним, что Windows 3.x/95 использует в своей работе две кодовые таблицы, которые мы обычно называем DOS и Windows, но формально они именуютс€ OEM и ANSI. »менно последние названи€ используютс€ в соответствующих именах параметров управл€ющих файлов Windows. ƒл€ отечественных пользователей эти таблицы имеют номера 866 и 1251 соответственно. („тобы не вводить своих пользователей в искушение, Microsoft не позвол€ет производить динамическое переопределение номеров кодовых таблиц Ч они устанавливаютс€ только при инсталл€ции ќ—. ¬прочем, смельчаки могут отважитьс€ и на это, непосредственно изменив параметры управл€ющих файлов, что вполне возможно, но... лучше не играть с огнем.)

¬ составе API есть процедуры преобразовани€ кодировки из OEM-таблицы в ANSI, и наоборот. ¬ 32-разр€дном наборе интерес представл€ют прежде всего следующие функции:

CharToOem Ч преобразование строковой переменной из ANSI в OEM (в API/16 аналогична€ функци€ называетс€ AnsiToOem);

OemToChar Ч преобразование строковой переменной из OEM в ANSI (API/16 Ч OemToAnsi);

¬ API 16/32 есть и другие функции, св€занные с преобразованием символьных данных. »х применение можно проиллюстрировать таким примером (API/32):

Private Declare Function GetOEMCP Lib "kernel32" () As Long
Private Declare Function GetACP Lib "kernel32" () As Long
Private Declare Function OemToChar Lib "user32" Alias "OemToCharA"
(ByVal lpszSrc As String, ByVal lpszDst As String) As Long

Private Sub Form_Load()
   ' сначала можно проверить номера кодовых таблиц
   OemCP& = GetOEMCP    ' номер OEM (DOS) таблицы
   AnsiCP& = GetACP     ' номер ANSI (Windows) таблицы
   Debug.Print OemCP&; AnsiCP&   ' = 866 2151
   '
   ' открывает файл с русским текстом
   FilePath$ = "russian.txt"
   Open FilePath$ For Input As #1
   Line Input #1, InputStr$
   Print InputStr$
   OutputStr$ = Space$(Len(InputStr$)) ' ¬Ќ»ћјЌ»≈ Ќј Ё“” —“–ќ„ ”
   Code& = OemToChar(InputStr$, OutputStr$)  '
   '
   Print Code&: Print InputStr$: Print OutputStr$
   Close #1
End Sub

«десь нужно обратить внимание на следующие моменты:

  1. ƒанна€ функци€ производит перекодировку из установленной кодовой таблицы DOS в таблицу Windows. ≈сли вы пользуетесь американской Windows OSR2, то в ней, скорее всего, не установлены страницы 866 и 1251.

  2. ѕеременна€ OutputStr$, в которую записываетс€ результат операции, должна быть сформирована до обращени€ к функции OemToChar. ≈сли она вообще не была создана (в случае, например, комментари€ в строке OutputStr$=...), то выдаетс€ код Code& = 0. ≈сли данна€ переменна€ существует, то получаем Code& = 1. Ќо при этом формируетс€ только то число символов, которое было изначально определено. ƒл€ эксперимента установите: OutputStr$ = Space$(0) или OutputStr$ =Space$(Len(InputStr$)) + "fffffff".

  3. ≈сли вы не боитесь потер€ть исходное значение переменной, то проще использовать одну и ту же переменную: Code& = OemToChar(InputStr$, InputStr$).

¬ начало статьи

—овет 120. ѕростые процедуры преобразовани€ символьных данных.

ѕроблемы перекодировки не ограничиваютс€ только преобразованием из таблиц DOS в Windows. ƒл€ их решени€ хотелось бы предложить некоторые простые процедуры, которые сохранились у нас еще со времен QuickBasic 4.x:

FUNCTION SymChange$ (Text$, NewCode$, OldCode$)
'
'  ѕерекодировка символов  OldCode$ -> NewCode$:
'  в переменной Text$ все символы набора OldCode$
'  замен€ютс€ на соответствующие символы набора NewCode$
'
'  ¬Ќ»ћјЌ»≈!
'  ƒолжно выполн€тьс€ условие:  LEN(NewCode$)=LEN(OldCode$)
'ЧЧЧЧЧЧЧЧЧЧЧЧЧЧ-
     Sym$ = Text$: Ltext% = LEN(Text$)
     IF Ltext% > 0 THEN
       FOR i% = 1 TO Ltext%
         k% = INSTR(OldCode$, MID$(Text$, i%, 1))
         IF k% > 0 THEN 
           MID$(Sym$, i%, 1) = MID$(NewCode$, k%, 1)
         END IF
       NEXT i%
     END IF
     SymChange$ = Sym$
END FUNCTION

FUNCTION RusDosWin$ (Text$, Code%)
'
'  ѕреобразование  »–»ЋЋ»÷џ:
'  Code% = 0 - из DOS в Windows
'        = 1 - обратно
'ЧЧЧЧЧЧЧЧЧЧЧЧЧ
'  D$ - константа кодов русских символов дл€ DOS (cp866)
'  W$ - константа кодов русских символов дл€ Windows (cp1251)
'  обе константы содержат такую последовательность символов:
'  јЅ¬...яабв...€
'
'============================================
CONST D$ = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ_
†°Ґ£§•¶І®©™Ђђ≠Ѓѓабвгдежзийклмноп"
CONST W$ = "јЅ¬√ƒ≈∆«»… ЋћЌќѕ–—“”‘’÷„ЎўЏџ№Ёёя_
абвгдежзийклмнопрстуфхцчшщъыьэю€"
' ============================================
IF Code% = 0 THEN
      RusDosWin$ = SymChange(Text$, W$, D$)
    ELSE
      RusDosWin$ = SymChange(Text$, D$, W$)
    END IF
END FUNCTION
FUNCTION ULrus$ (Text$, Code%)
'
'   ѕреобразование русских символов:
'   Code% = 0  - строчные в прописные
'         = 1  - наоборот
'         ELSE - ничего не делаем
'   (јналог операторов UCASE$/LCASE$, но дл€ русских символов)
'ЧЧЧЧЧЧЧЧЧЧЧЧЧЧЧ
     CONST Urus$ = "…÷” ≈Ќ√Ўў«’‘џ¬јѕ–ќЋƒ∆Ёя„—ћ»“№ЅёЏ"
     CONST Lrus$ = "йцукенгшщзхфывапролджэ€чсмитьбюъ"
'
     SELECT CASE Code%
      CASE 0: ULrus$ = SymChange$(Text$, Urus$, Lrus$)
      CASE 1: ULrus$ = SymChange$(Text$, Lrus$, Urus$)
     END SELECT
END FUNCTION

FUNCTION DoubleSpaceDelete$ (Text$)
'
'   ”бирает двойные пробелы в переменной Text$
'ЧЧЧЧЧЧЧЧЧЧЧ
  Temp$ = RTRIM$(Text$)
  DO
    Num% = INSTR(Temp$, "  ")  ' поиск двойного пробела
    IF Num% <= 0 THEN EXIT DO
    Temp$ = LEFT$(Temp$, Num%) + MID$(Temp$, Num% + 2)
  LOOP
  DoubleSpaceDelete$ = Temp$
END FUNCTION

FUNCTION StrFilter$ (Text$, Filter$)
'================================================================
'   ‘ильтраци€ содержимого Text$
'
'   Filter$=CHR$(CodeFilter%)+FilterString$
'   ѕервый символ переменной Filter$ - код операции,
'   остальные символы - перечень допустимых/Ќ≈допустимых символов
'   CodeFilter%=ASC(Filter$)
'      =0 - удаление всех символов из Text$, кроме указанных в
'               списке допустимых - FilterString$
'      =1 - удаление всех символов из Text$, указанных в
'               списке Ќ≈допустимых - FilterString$
'===============================================================
      Temp$ = "": CodeFilter% = ASC(Filter$)
      FOR i% = 1 TO LEN(Text$)
        Sym$ = MID$(Text$, i%, 1)
        IF (SGN(INSTR(2, Filter$, Sym$)) XOR CodeFilter%) <> 0 THEN
          Temp$ = Temp$ + Sym$
        END IF
      NEXT i%
      StrFilter$ = Temp$
END FUNCTION

Ёти процедуры представлены в формате Basic/DOS (операторы €зыка написаны прописными буквами) и нагл€дно демонстрируют преемственность исходных текстов версий DOS/Windows Ч дл€ VB/Win нужно только удалить операторы DECLARE.

  1. ‘ункци€ StrChange$. — ее помощью можно производить замену того или иного набора символов на любой другой. Ќапример, в строке можно помен€ть местами буквы "т" и "м":

    a$ = "там"
    b$ = SymChange$(a$, "тм", "мт")
    PRINT b$  ' напечатано - "мат"
    

  2. ‘ункции RusDosWin$ и ULrus$ представл€ют собой примеры практического применени€ функции StrChange$. ѕерва€ производит преобразование русских символов из кодировки DOS в Windows и наоборот, а втора€ Ч строчных русских символов в прописные, и наоборот. Ќабор русских символов в кодировке DOS и Windows (константа W$) очень просто получить программным образом:

    W$ = "": D$ = ""
    For i = 192 To 255: W$ = W$ + Chr$(i): Next
    For i = 128 To 175: D$ = D$ + Chr$(i): Next
    For i = 224 To 236: D$ = D$ + Chr$(i): Next
    Print W$: Print D$
    

  3. ‘ункци€ DoubleSpaceDelete$ убирает лишние пробелы в строке (в качестве разделител€ слов остаетс€ один пробел).

  4. ‘ункци€ StrFilter$ удал€ет допустимые или недопустимые символы из строковой переменной в соответствии с заданным пользователем набором символов. Ќапример, с ее помощью можно убрать все разделители из номера телефона, чтобы представить его в чисто цифровом виде:

    PhoneNumber$ = "369-76-97"
    CodeFilter% = 0       ' код операции
    FilterString$ = "1234567890"
    Filter$ = CHR$(CodeFilter%) + FilterString$
    NewNumber$ = StrFilter$(PhoneNumber$, Filter$)
    PRINT NewNumber$
    ' напечатано: '3697697'
    

¬ начало статьи

—овет 121. Ѕудьте внимательны при преобразовании исходных текстов программ из DOS в Windows, и наоборот.

ћногие процедуры, написанные в верси€х Basic дл€ DOS, могут примен€тьс€ и в VB/Win. Ќо при переносе исходных программных модулей из DOS в Windows (и наоборот) нужно быть очень внимательным. » особенно в том случае, если вы хотите, чтобы одна и та же копи€ модул€ могла использоватьс€ в обеих верси€х. ѕроблема здесь заключаетс€ в следующем: DOS и Windows используют разные кодировки русских букв Ч таблицы с номерами 866 и 1251 соответственно.

≈сли напр€мую загрузить написанный в DOS модуль в VB, то очевидно, что русский текст, например комментарии, превратитс€ в нечитаемый набор символов. ≈сли вы работаете только в VB/Win, то такой исходный файл, конечно же, лучше сначала преобразовать из кодировки 866 в 1251. ƒл€ этого можно, к примеру, использовать WinWord: прочитать исходный файл как "“екст MS-DOS", а запомнить как "“олько текст". ¬прочем, если вопрос касаетс€ только тех комментариев, которые вам не очень-то и нужны, то такое преобразование можно не делать, а работать с одним исходным файлом как в DOS, так и в Windwos.

ƒругое дело, если русские символы используютс€ непосредственно в коде программы. —амый простой пример: Print "ѕривет,  ол€!" ¬ этом случае перекодировка исходного текста процедуры €вл€етс€ просто необходимой.  ак следствие, одну и ту же копию программного модул€, использующего подобные символьные константы, нельз€ примен€ть при работе и в DOS и в Windows.

¬ то же врем€ может возникнуть и друга€ ситуаци€, примером которой €вл€етс€ текст приведенной выше функции RusDosWin$. ƒело в том, что используемые в ней константы D$ и W$ €вл€ютс€ не осмысленным текстом, а кодовыми таблицами, представленными дл€ удобства в символьном виде.  ажда€ константа содержит коды символов последовательности "јЅ...яаб...€" дл€ кодировки DOS и Windows соответственно. ѕоэтому, если смотреть текст процедуры RusDosWin$ в среде DOS, вы должны увидеть:

Const D$ = "јЅ...яаб...€"
Const W$ = хаотичный набор символов

а если в Windows, то наоборот:

Const D$ = хаотичный набор символов (но другой!)
Const W$ = "јЅ...яаб...€"

ѕри этом вас не должен смущать по€вл€ющийс€ в изображении хаотичный набор символов повтор€ющихс€ знаков Ч просто дл€ изображени€ разных кодов, не используемых реально дл€ вывода, примен€ютс€ одни и те же знаки.

’отелось бы обратить внимание, что при написании процедуры RusDosWin$ в среде DOS в редакторе QB была введена с клавиатуры только константа D$, а константа W$ по€вл€етс€ в нем совершенно иным способом. ƒл€ этого в "Ћексиконе>"дл€ DOS был сделан отдельный текстовый файл, содержащий D$. «атем этот файл прочитали в WinWord как "“екст MS-DOS", сделали единственное изменение Ч D$ на W$ Ч и сохранили как "“олько текст". ѕрочитав новый файл в DOS, можно увидеть, что он содержит константу W$. ≈сли же набирать текст в Windows, то делаетс€ аналогична€ операци€ преобразовани€, но при этом с клавиатуры вводитс€ уже содержимое W$.

»з вышесказанного можно сделать два вывода:

  1. Ќельз€ преобразовывать исходный текст процедур типа RusDosWin$ из одной кодировки в другую. ¬ернее так: можно преобразовывать комментарии, нужно преобразовывать константы типа "ѕривет,  ол€!" (это касаетс€, например, констант Urus$ и Lrus$ в процедуре ULrus$), но нельз€ преобразовывать специальные кодовые последовательности, представленные в виде символьных констант. —оответственно, перевод файла с процедурой RusDosWin$ из DOS в Windows необходимо сделать в два этапа: весь текст перекодировать обычным образом, а потом перенести в него строки с D$ и W$ без перекодировки.

  2. ≈сли вы захотите создать файл с процедурой RusDosWin$ из приведенного выше листинга, то можно ввести с клавиатуры только константу D$ (при работе в DOS) или W$ (в Windows), а вторую константу создать самосто€тельно, выполнив, например, приведенную выше операцию.

ѕримечание. „тобы разобратьс€, почему при распечатке листинга разным символам в кодировке DOS соответствуют одни и те же символы в кодировке Windows, наберите в редакторе дл€ DOS файл с тестом:

јЅ¬√ƒ≈∆«»… ЋћЌќѕ–—“”‘’÷„ЎўЏџ№Ёёя

а затем введите его в WinWord в режиме "текст" (без преобразовани€ кодов) и сохраните его в том же режиме "текст". ѕросмотрев его в DOS, вы увидите:

јЅ¬√ƒ≈∆«»… ЋћЌќѕ–''""’÷„ЎўЏџ№Ёёя

“акое несоответствие объ€сн€етс€ очень просто: символы с кодом 145 и 146 (в DOS Ч C и “) означают левую и правую одиночную кавычку, которые при записи в режиме "текст" замен€ютс€ на стандартную одиночную кавычку с кодом 39. ј все потому, что в DOS был один код кавычки, а в Windows Ч три (стара€ оставлена дл€ совместимости)! “о же самое происходит и с двойной кавычкой (34, 147, 148).

¬ начало статьи