VBA Excel. Переменная диапазона ячеек (As Range)

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

Например, константа Pi хранит значение 3,14159265… Число “Пи” не будет изменяться в ходе выполнения программы, но все же хранить такое значение удобнее как константу.

В то же время мы можем использовать переменную sVAT_Rate для хранения ставки НДС на покупаемые товары. Величина переменной sVAT_Rate может изменяться в зависимости от того, что за товар приобретается.

Типы данных

Все переменные и константы относятся к определённому типу данных. В таблице ниже приведены типы данных, используемые в VBA, с описанием и диапазоном возможных значений:

Тип данных Размер Описание Диапазон значений
Byte 1 байт Положительные целые числа; часто используется для двоичных данных от 0 до 255
Boolean 2 байта Может принимать значения либо True, либо False True или False
Integer 2 байта Целые числа (нет дробной части) от -32 768 до +32 767
Long 4 байта Большие целые числа (нет дробной части) от -2 147 483 648 до +2 147 483 647
Single 4 байта Число с плавающей точкой одинарной точности от -3.4e38 до +3.4e38
Double 8 байт Число с плавающей точкой двойной точности от -1.8e308 до +1.8e308
Currency 8 байт Число с плавающей точкой, с фиксированным количеством десятичных разрядов от -922 337 203 685 477.5808 до +922 337 203 685 477.5807
Date 8 байт Дата и время – данные типа Date представлены числом с плавающей точкой. Целая часть этого числа выражает дату, а дробная часть – время от 1 Января 100 до 31 Декабря 9999
Object 4 байта Ссылка на объект Любая ссылка на объект
String изменяется Набор символов. Тип String может иметь фиксированную или изменяющуюся длину. Чаще используется с изменяющейся длиной Фиксированной длины – приблизительно до 65 500 символов. Переменной длины – приблизительно до 2 миллиардов символов
Variant изменяется Может содержать дату, число с плавающей точкой или строку символов. Этот тип используют в тех случаях, когда заранее не известно, какой именно тип данных будет введён Число – Double, строка – String

Очевидно, что пользуясь приведённой выше таблицей и правильно выбирая тип данных, можно использовать память более экономно (например, выбрать тип данных Integer вместо Long или Single вместо Double ). Однако, используя более компактные типы данных, нужно внимательно следить за тем, чтобы в коде не было попыток уместить в них не соразмерно большие значения.

Объявление переменных и констант

Примечание переводчика: Говоря о переменных в VBA, стоит упомянуть ещё один очень важный момент. Если мы объявляем переменную, но не присваиваем ей какое-либо значение, то она инициализируется значением по умолчанию:
текстовые строки – инициализируются пустыми строками;
числа – значением 0;
переменные типа Boolean – False;
даты – 30 декабря 1899.

Прежде чем использовать переменную или константу, её нужно объявить. Для этого в макрос добавляют вот такую простую строку кода:

Dim Имя_Переменной As Тип_Данных

В показанной выше строке кода Имя_Переменной – это имя переменной, которая будет использована в коде, а Тип_Данных – это один из типов данных из таблицы, приведённой чуть ранее в этой статье. Например:

Dim sVAT_Rate As Single Dim i As Integer

Аналогично объявляются константы, но при объявлении констант обязательно сразу указывается их значение. Например, вот так:

Const iMaxCount = 5000 Const iMaxScore = 100

Объявлять переменные в Excel не обязательно. По умолчанию все введённые, но не объявленные переменные в Excel будут иметь тип Variant и смогут принять как числовое, так и текстовое значение.

Таким образом, программист в любой момент сможет использовать новую переменную (даже если она не была объявлена), и Excel будет рассматривать её как переменную типа Variant . Однако, есть несколько причин, почему так поступать не следует:

  1. Использование памяти и скорость вычислений. Если не объявлять переменную с указанием типа данных, то по умолчанию для неё будет установлен тип Variant . Этот тип данных использует больше памяти, чем другие типы данных.Казалось бы, несколько лишних байт на каждую переменную – не так уж много, но на практике в создаваемых программах могут быть тысячи переменных (особенно при работе с массивами). Поэтому излишняя память, используемая переменными типа Variant , по сравнению с переменными типа Integer или Single , может сложится в значительную сумму.К тому же, операции с переменными типа Variant выполняются гораздо медленнее, чем с переменными других типов, соответственно лишняя тысяча переменных типа Variant может значительно замедлить вычисления.
  2. Профилактика опечаток в именах переменных. Если все переменные объявляются, то можно использовать оператор VBA – Option Explicit (о нём расскажем далее), чтобы выявить все не объявленные переменные.Таким образом исключается появление в программе ошибки в результате не верно записанного имени переменной. Например, используя в коде переменную с именем sVAT_Rate , можно допустить опечатку и, присваивая значение этой переменной, записать: “VATRate = 0,175”. Ожидается, что с этого момента, переменная sVAT_Rate должна содержать значение 0,175 – но, конечно же, этого не происходит. Если же включен режим обязательного объявления всех используемых переменных, то компилятор VBA сразу же укажет на ошибку, так как не найдёт переменную VATRate среди объявленных.
  3. Выделение значений, не соответствующих объявленному типу переменной. Если объявить переменную определённого типа и попытаться присвоить ей данные другого типа, то появится ошибка, не исправив которую, можно получить сбой в работе программы.На первый взгляд, это может показаться хорошей причиной, чтобы не объявлять переменные, но на самом деле, чем раньше выяснится, что одна из переменных получила не те данные, которые должна была получить – тем лучше! Иначе, если программа продолжит работу, результаты могут оказаться неверными и неожиданными, а найти причину ошибок будет гораздо сложнее.Возможно также, что макрос будет “успешно” выполнен. В результате ошибка останется незамеченной и работа продолжится с неверными данными!

В связи с этим, неверный тип данных желательно обнаруживать и как можно раньше исправлять такие ошибки в коде. По этим причинам при написании макроса VBA рекомендуется объявлять все переменные.

Option Explicit

Оператор Option Explicit заставляет объявлять все переменные, которые будут использованы в коде VBA, и при компиляции выделяет все не объявленные переменные как ошибки (прежде чем будет запущено выполнение кода). Применить этот оператор не сложно – просто запишите в самом верху файла VBA такую строку:

Option Explicit

Если хотите всегда вставлять Option Explicit в начало каждого нового созданного модуля VBA, то это можно делать автоматически. Для этого необходимо включить параметр Require Variable Declaration в настройках редактора VBA.

Это делается так:

  • В меню редактора Visual Basic нажмите Tools > Options
  • В появившемся диалоговом окне откройте вкладку Editor
  • Отметьте галочкой параметр Require Variable Declaration и нажмите ОК

При включенном параметре строка Option Explicit будет автоматически вставляться в начало каждого нового созданного модуля.

Область действия переменных и констант

Каждая объявленная переменная или константа имеет свою ограниченную область действия, то есть ограниченную часть программы, в которой эта переменная существует. Область действия зависит от того, где было сделано объявление переменной или константы. Возьмём, к примеру, переменную sVAT_Rate , которая используется в функции Total_Cost . В следующей таблице рассмотрены два варианта области действия переменной sVAT_Rate , объявленной в двух различных позициях в модуле:

Option Explicit Dim sVAT_Rate As Single Function Total_Cost() As Double ... End Function

Если переменная sVAT_Rate объявлена в самом начале модуля, то областью действия этой переменной будет весь модуль (т.е. переменная sVAT_Rate будет распознаваться всеми процедурами в этом модуле).

Следовательно, если в функции Total_Cost переменной sVAT_Rate будет присвоено некоторое значение, то следующая функция, выполняемая в пределах этого же модуля, будет использовать переменную sVAT_Rate с этим же значением.

Однако, если будет вызвана какая-то функция, расположенная в другом модуле, то для неё переменная sVAT_Rate будет не известна.

Option Explicit Function Total_Cost() As Double Dim sVAT_Rate As Single ... End Function

Если переменная sVAT_Rate объявлена в начале функции Total_Cost , то её область действия будет ограничена только этой функцией (т.е. в пределах функции Total_Cost , можно будет использовать переменную sVAT_Rate , а за её пределами – нет).

При попытке использовать sVAT_Rate в другой процедуре, компилятор VBA сообщит об ошибке, так как эта переменная не была объявлена за пределами функции Total_Cost (при условии, что использован оператор Option Explicit ).

В показанном выше примере переменная объявлена на уровне модуля при помощи ключевого слова Dim . Однако, бывает необходимо, чтобы объявленными переменными можно было пользоваться в других модулях. В таких случаях для объявления переменной вместо ключевого слова Dim нужно использовать ключевое слово Public .

Кстати, для того, чтобы объявить переменную на уровне модуля, вместо ключевого слова Dim можно использовать ключевое слово Private , которое укажет на то, что данная переменная предназначена для использования только в текущем модуле.

Для объявления констант также можно использовать ключевые слова Public и Private , но не вместо ключевого слова Const , а вместе с ним.

В следующих примерах показано использование ключевых слов Public и Private в применении к переменным и к константам.

Присвоение диапазона ячеек объектной переменной в VBA Excel. Адресация ячеек в переменной диапазона и работа с ними. Определение размера диапазона. Примеры.

Присвоение диапазона ячеек переменной

Чтобы переменной присвоить диапазон ячеек, она должна быть объявлена как Variant , Object или Range :

Dim myRange1 As Variant Dim myRange2 As Object Dim myRange3 As Range

Чтобы было понятнее, для чего переменная создана, объявляйте ее как Range .

Присваивается переменной диапазон ячеек с помощью оператора Set :

Set myRange1 = Range("B5:E16") Set myRange2 = Range(Cells(3, 4), Cells(26, 18)) Set myRange3 = Selection

В выражении Range(Cells(3, 4), Cells(26, 18)) вместо чисел можно использовать переменные.

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

Адресация ячеек в диапазоне

К ячейкам присвоенного диапазона можно обращаться по их индексам, а также по индексам строк и столбцов, на пересечении которых они находятся.

Индексация ячеек в присвоенном диапазоне осуществляется слева направо и сверху вниз, например, для диапазона размерностью 5х5:

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

Индексация строк и столбцов начинается с левой верхней ячейки. В диапазоне этого примера содержится 5 строк и 5 столбцов. На пересечении 2 строки и 4 столбца находится ячейка с индексом 9. Обратиться к ней можно так:

"обращение по индексам строки и столбца myRange.Cells(2, 4) "обращение по индексу ячейки myRange.Cells(9)

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

обращение к первой строке присвоенного диапазона размерностью 5х5:

MyRange.Range("A1:E1") "или myRange.Range(Cells(1, 1), Cells(1, 5))

и обращение к первому столбцу присвоенного диапазона размерностью 5х5:

MyRange.Range("A1:A5") "или myRange.Range(Cells(1, 1), Cells(5, 1))

Работа с диапазоном в переменной

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

MsgBox myRange.Cells(6) MsgBox myRange.Cells(6).Value

равнозначны. В обоих случаях информационное сообщение MsgBox выведет значение ячейки с индексом 6.

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

Преимущество работы с диапазоном ячеек в объектной переменной заключается в том, что все изменения, внесенные в переменной, применяются к диапазону (который присвоен переменной) на рабочем листе.

Пример 1 - работа со значениями

Скопируйте процедуру в программный модуль и запустите ее выполнение.

Sub Test1() "Объявляем переменную Dim myRange As Range "Присваиваем диапазон ячеек Set myRange = Range("C6:E8") "Заполняем первую строку "Присваиваем значение первой ячейке myRange.Cells(1, 1) = 5 "Присваиваем значение второй ячейке myRange.Cells(1, 2) = 10 "Присваиваем третьей ячейке "значение выражения myRange.Cells(1, 3) = myRange.Cells(1, 1) _ * myRange.Cells(1, 2) "Заполняем вторую строку myRange.Cells(2, 1) = 20 myRange.Cells(2, 2) = 25 myRange.Cells(2, 3) = myRange.Cells(2, 1) _ + myRange.Cells(2, 2) "Заполняем третью строку myRange.Cells(3, 1) = "VBA" myRange.Cells(3, 2) = "Excel" myRange.Cells(3, 3) = myRange.Cells(3, 1) _ & " " & myRange.Cells(3, 2) End Sub

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

Пример 2 - работа с форматами

Продолжаем работу с тем же диапазоном рабочего листа «C6:E8»:

Sub Test2() "Объявляем переменную Dim myRange As Range "Присваиваем диапазон ячеек Set myRange = Range("C6:E8") "Первую строку выделяем жирным шрифтом myRange.Range("A1:C1").Font.Bold = True "Вторую строку выделяем фоном myRange.Range("A2:C2").Interior.Color = vbGreen "Третьей строке добавляем границы myRange.Range("A3:C3").Borders.LineStyle = True End Sub

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

Пример 3 - копирование и вставка диапазона из переменной

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

Скопировать и вставить диапазон полностью со значениями и форматами можно при помощи метода Copy , указав место вставки (ячейку) на рабочем листе.

В примере используется тот же диапазон, что и в первых двух, так как он уже заполнен значениями и форматами.

Sub Test3() "Объявляем переменную Dim myRange As Range "Присваиваем диапазон ячеек Set myRange = Range("C6:E8") "Присваиваем ячейкам рабочего листа "значения ячеек переменной диапазона Range("A1:C3") = myRange.Value MsgBox "Пауза" "Копирование диапазона переменной "и вставка его на рабочий лист "с указанием начальной ячейки myRange.Copy Range("E1") MsgBox "Пауза" "Копируем и вставляем часть "диапазона из переменной myRange.Range("A2:C2").Copy Range("E11") End Sub

Информационное окно MsgBox добавлено, чтобы вы могли увидеть работу процедуры поэтапно, если решите проверить ее в своей книге Excel.

Размер диапазона в переменной

При получении диапазона с помощью метода и присвоении его переменной диапазона, бывает полезно узнать его размерность. Это можно сделать следующим образом:

Sub Test4() "Объявляем переменную Dim myRange As Range "Присваиваем диапазон ячеек Set myRange = Application.InputBox("Выберите диапазон ячеек:", 8) "Узнаем количество строк и столбцов MsgBox "Количество строк = " & myRange.Rows.Count _ & vbNewLine & "Количество столбцов = " & myRange.Columns.Count End Sub

Запустите процедуру, выберите на рабочем листе Excel любой диапазон и нажмите кнопку «OK». Информационное сообщение выведет количество строк и столбцов в диапазоне, присвоенном переменной myRange .

Ровно четыре года назад в КомпьютерПресс 3’96 была опубликована наша первая статья под названием “Советы для тех, кто программирует на Visual Basic”. В ней было всего три совета, но, честно говоря, в тот момент мы не думали, что их число будет расти и к сегодняшнему дню достигнет предела значения байтовой переменной - 255.

Но мы надеемся, что проблем с переполнением счетчика не будет: двухбайтная Integer обеспечит еще несколько лет для счета советов, а там можно будет перейти и к Long.

Кстати, появление этого цикла статей именно в КомпьютерПресс было совсем не случайным: еще за четыре года до этого, в 1991 году, также в мартовском номере журнала появилась статья “QuickBASIC - это то, что вам нужно”, которая стала нашей первой публикацией в только зарождавшейся тогда отечественной компьютерной прессе.

Наш читатель прислал письмо, в котором рассказывает о следующей проблеме. Необходимо программным образом скопировать содержимое ячеек из таблицы одной рабочей книги в таблицы другой. Для этого он написал такую макрокоманду в рабочей книге Macros.xls:

Sub Макро1() Workbooks.Open Filename:="Source.xls" " открываем книгу Source.xls Cells.Select " выделяем все ячейки в активной таблице Selection.Copy " копируем в буфер обмена ActiveWindow.Close " закрываем активную таблицу " теперь активной стала текущая таблица книги Macros.xls Range("A1").Select " начальная позиция для вставки ActiveSheet.Paste " вставка из буфера обмена End Sub

Однако результат выполнения копирования из таблицы книги Source.xls с помощью этой макрокоманды отличался от ожидаемого как в среде Excel 97, так и в Excel 2000 (с помощью команд в среде пакета эта операция выполняется верно):

Из этих данных видно, что ошибка копирования каким-то образом связана с неверным использованием региональных установок (хотя работа велась в среде русскоязычных Windows и Office) - очевидна путаница русских и английских установок в Excel 97.

Так 12,12 с точки зрения RegionalSetting = 1033 (США) является просто символьной строкой. И в данном случае она копируется как символьная строка (обратите внимание, что после выполнения Макро1 в Excel 97 ячейка стала выровнена по левому краю). А 5,559 представляет (для установок США) целое число с точкой в качестве разделителя триад. При вставке же этого числа используется русский разделитель триад - пробел. Нечто аналогичное, но плохо поддающееся логическому объяснению, происходит с числом 5,7777.

Содержимое же четвертой ячейки - 5.559 - в Source.xls является символьной строкой, выровненной по левому краю. Но для установок США это число с десятичной точкой, которая в русской языке меняется на запятую (и, соответственно, выравнивается по правому краю). Эта же ошибка имеет место в Excel 2000.

Механизм ошибки понятен, но что же делать? Как копировать ячейки?

По нашей просьбе служба технической поддержки в России занялась этой проблемой и после запроса в европейский центр получила такой ответ:

«Переменная копируется как текстовая строка в буфер обмена, после чего исходная книга закрывается и выполняется вставка во вторую книгу. Excel старается преобразовать ее в вид по своему усмотрению и делает это неверно.

Но все будет работать нормально, если держать исходную рабочую книгу открытой до окончания вставки данных».

И действительно, все работает без ошибок, если использовать для копирования такой вариант макроса:

Sub Макро2() Workbooks.Open FileName:="c:\Source.xls" Cells.Select Selection.Copy " делаем активной книгу Macros.xls Workbooks("Macros.xls").Activate Range("A1").Select ActiveSheet.Paste " вставка " только теперь закрываем исходную книгу Workbooks("Source.xls").Close End Sub

Еще один совет из службы поддержки Microsoft: без особой нужды не следует копировать таблицу целиком - используйте только тот диапазон, который вам действительно нужен. То есть вместо

Cells.Select

в данном случае лучше написать:

Range("A1:E2").Select

При тестировании прилагаемых программных примеров необходимо файл Source.xls скопировать в каталог C:\ или указать другой путь к файлу в коде макросов.

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

Предположим, у вас в модуле Process.bas есть некая вычислительная процедура. Идея реализации механизма ее прерывания заключается в использовании некоторого флага (глобальная переменная), состояние которого отслеживается внутри процедуры и который может изменяться другой внешней процедурой.

В этом случае модуль Process.bas может выглядеть приблизительно так:

Global ProcessFlag% " флаг управления процессом, глобальная переменная Public Sub Process(Result%) " имитация некоего процесса " Result возвращает флаг окончания процесса " 1 - закончился сам, 0 - внешнее аварийное завершение " ProcessFlag = 1 " флаг начала процесса For i& = 1 To 1000000 " увеличить длину цикла, если слишком малое время задержки If ProcessFlag = 0 Then Exit For "проверка флага Value# = 100 / 0.3 * 1.5 / 2.3 Next Result = ProcessFlag End Sub

Теперь создадим форму frmInterrupt, на которой поместим кнопку с названием "Щелкни здесь, чтобы прервать процесс Process". Для кнопки напишем такую процедуру:

Private Sub Command1_Click() ProcessFlag = 0 " очистка глобального флага End Sub

Public Sub Main() " процедура для демонстрации механизма прерывания " некоего вычислительного процесса frmInterrupt.Show 0 " открываем форму в немодальном режиме Call Process(Result%) " запускаем форму Unload frmInterrupt " выгружаем форму " анализ кода завершения процедуры MsgBox "Результат завершения процедуры = " & Result% End Sub

Внешне все выглядит правильно, но, запустив проект на выполнение, мы обнаружим, что произвести прерывание процесса не удается. Более того, даже форма не прорисовывается до конца. Подобный вопрос мы обсуждали месяц назад в советах 246-247: дело в том, что вычислительный цикл съедает все ресурсы компьютера, не давая выполняться другим процессам приложения. Для решения этой проблемы необходимо внутрь вычислительного цикла вставить функцию DoEvents, которая передает управление операционной системе для выполнения других процессов.

Однако вариант

For i = 1 To 1000000 DoEvents " передача управления другим процессам If ProcessFlag = 0 Then Exit For "проверка флага Value# = 100 / 0.3 * 1.5 / 2.3 Next

также не очень хорош - выполнение DoEvents требует очень много времени. В нашем простом вычислительном примере мы увидим, что быстродействие процедуры Process упадет в 1000 раз (то есть время выполнения DoEvents в 1000 раз больше, чем полезное вычисление по формуле). Чтобы избежать таких непроизводительных затрат, можно написать следующий код:

For i = 1 To 1000000 If i Mod 100000 Then " проверка один раз на 100 000 циклов DoEvents " передача управления другим процессам If ProcessFlag = 0 Then Exit For " проверка флага End If Value# = 100 / 0.3 * 1.5 / 2.3 Next

В этом случае DoEvents будет занимать лишь 1% от полезных вычислений.

К сожалению, такой вариант решения задачи отслеживания двух параллельных процессов представляется не самым лучшим, но таковы уж реалии Windows.

Короче говоря, совет такой: для обеспечения прерывания вычислительных задач по ходу программы расставляйте (но не очень часто) подобные конструкции:

DoEvents If ProcessFlag = 0 Then Exit something

Совет 253. Как решить проблему с сохранением проектов с цифровой подписью

В ряде случаев в Office 2000 возникает ситуация, когда пользователь не может сохранить проект (документ с макрокодом) с электронной подписью - запись документа возможна только без подписи. При этом выдается неверная диагностика о нехватке места на диске.

В данном случае имеет место программная ошибка, которая, как мы надеемся, будет исправлена. На самом же деле она проявляется только в тех проектах, которые содержат внутренние синтаксические ошибки в коде. Поэтому, встретив подобное сообщение о невозможности сохранения документа с электронной подписью, мы рекомендуем вам, прежде чем отменять подпись, проверить работоспособность вашего кода. Довольно часто ошибка связана с отсутствием описания переменной или ссылки на внешний объект.

Чтобы лучше понять суть ситуации, сделайте такой простой пример в Word.

Создайте новый документ и перейдите в среду VBA. Там создайте макрокоманду Test1:

Sub Test1 () Avar = 1 End If

Разумеется, сначала должен быть задан режим Option Explicit (обязательное объявление переменных).

Теперь установите электронную подпись и попробуйте сохранить документ. Скорее всего, у вас появится сообщение о нехватке места на диске для записи файла.

Запустите макрокоманду Test1 на выполнение - транслятор выдаст сообщение о синтаксической ошибке (не определена переменная Avar). Добавьте в процедуру описание:

Dim Avar As Integer

Теперь макрокоманда станет выполняться (правда, не делая ничего полезного), и документ тоже без проблем сохранится с электронной подписью.

Совет 254. Как автоматически определить кодовую таблицу для русских текстов

Проблема известна: для русского языка применяется несколько различных кодовых таблиц, и поэтому актуальной задачей является преобразование текстов из одной кодировки в другую. Для чего нужно автоматически определять используемую кодовую таблицу? Тут есть два очевидных примера:

  1. Это необходимо при загрузке Web-страниц в браузер, текстовых файлов - в Word или при работе с почтовыми программами (гораздо лучше поступать так, чем заниматься перебором разных вариантов кодировок).
  2. Это нужно для дополнительного контроля при преобразовании кодов файлов. Например, мы постоянно осуществляем преобразование HTML-страниц из Windows в KOI8 (наш Web-сервер работает под UNIX), но при этом порой из-за невнимательности либо делаем двойную перекодировку, либо неправильно устанавливаем исходный код, либо вообще пропускаем файлы.

Идея автоматического определения кодировки русских текстов достаточно очевидна: необходимо определить частоту попадания кодов 128-255 (&h80-&hFF) в различные числовые диапазоны. Понятно, что основное количество этих кодов приходится именно на русские буквы (в старшей половине кодовой таблицы находится также ряд специальных символов типа «№», открывающих-закрывающих кавычек, Copyright и др.), причем строчных букв гораздо больше, чем прописных.

В таблице приведены результаты подсчета такой статистики для довольно типичного русскоязычного текста, которые получены с помощью подпрограммы CodeTableTest (листинг 1). Тут хорошо видно, что строчные буквы попадают в разные числовые диапазоны для разных кодовых таблиц. Любопытно также отметить, что частота появления букв первой половины русского алфавита (от "а" до "п") в два раза выше, чем частота букв от "р" до "я". Соответственно, если принять за условие, что процент строчных русских букв среди кодов 128-255 превышает заданную величину, например, Lpercent = 0,70, то критерий определения кодовой таблицы будет выглядеть таким образом:

Windows частота (&hE0-&hFF) > Lpercent
KOI8-R частота (&hC0-&hDF) > Lpercent
DOS частота (&hA0-&hAF + &hE0-&EF) > Lpercent
ISO частота (&hD0-&hEF) > Lpercent

Единственная проблема здесь заключается в идентификации различных кодировок Windows и Macintosh, у которых диапазоны кодов русских строчных букв почти совпадают. Но тут можно осуществить дополнительную проверку, которая основана на том, что в Windows практически не используются коды в диапазоне &h80-&h8F, а прописные буквы от "А" до "П" (&hC0-&CF) составляют значительную величину, тогда как в Macintosh ситуация для этих же диапазонов диаметрально противоположная.

Функция NumberTableTest, реализующая описанный выше алгоритм, приведена в листинге 1 .

Обратите внимание еще на один важный аспект -- для хранения кодов символов мы используем не строковую переменную, а байтовый массив. Соответственно, если нужно прочитать исходный файл, необходимо применять следующую конструкцию:

Open SourceFile$ For Binary As #1 LenS = LOF(1) ReDim bytSourceArray(1 To LenS) As Byte Get #1, bytSourceArray Close #1

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

BytSourceArray() = StrConv (SourceString$, vbFromUniCode, &h419)

Совет 255. Как определить кодировку текста: еще один вариант

Алгоритм, приведенный в предыдущем совете, не работает в случае неверной перекодировки текста, то есть когда исходные данные уже не соответствуют ни одной из кодовых таблиц. (Когда, например, текст, записанный в Windows, ошибочно преобразуется «из DOS в любой другой».) В таких случаях восстановление текста порой бывает вообще невозможно, но в любом случае подобную ситуацию полезно идентифицировать.

С проблемой перекодировки мы чаще всего сталкиваемся при создании собственных HTML-страниц, предназначенных для размещения на Web-сервере. Для контроля реальной кодовой таблицы в данном файле можно использовать следующий прием. (Мы сами выполняем сканирование обновлений локального варианта нашего сервера перед их записью на удаленный компьютер в целях обнаружения подобных ошибок.)

В каждую новую HTML-страницу вставляется строка комментария, в которую включены все буквы русского алфавита, кроме «ё» (такая строка автоматически создается нашим простеньким генератором страниц):

Конечно, можно минимизировать число используемых символов, но полный их набор точно гарантирует нужное решение.

Программный код утилиты TstCode2, которая проверяет код отдельного файла, приведен в листинге 2 . Ключевой процедурой здесь является NumberTableTestKey, выполняющая идентификацию символьного кода.

В свой реальной работе для проверки кодовой таблицы текстовых и HTML-файлов мы пользуемся утилитой TestCode (рис. 1), которая применяет комбинацию двух описанных выше методов.

КомпьютерПресс 3"2000