1. Структура заданного исходного файла и структуры данных, соответствующие данным файла
Файл – это последовательность байтов, хранящаяся в памяти.
Текстовый файл – это так же последовательность байтов, но каждый байт текстового файла можно представить кодом символа.
Заданный исходный файл – текстовый, так как в каждом байте хранится код символа.
В файле хранится текст:
Пределы воспламеняемости некоторых газов и паров в воздухе и в кислороде, % (объемы). Давление 1 бар, температура 20 °С.
Вещество Нижний предел в воздухе Верхний предел в воздухе Нижний предел в кислороде Верхний предел в кислороде
Аммиак NH3 15,0 28,0 15 79
Окись углерода СО 12,5 74 15,5 94
Водород Н2 4,0 75,6 4,0 94
Метан СН4 5,0 15,0 5 61
Метилхлорид СН3С1 7,1 18,5 8,0 66
Этан С2Н6 3,0 12,5 3,0 66
Диметилэфир С2Н6О 2,0 27,0 3,9 61
Этилен С2Н4 2,7 28,5 2,9 80
Окись этилена С2Н4О 2,6 100 - -
Ацетальдегид С2Н4О 4,0 57,0 4,0 93
Винилхлорид С2Н3С1 3,8 29,3 4,0 70
Ацетилен С2Н2 1,5 82,0 2,8 93
Трихлорэтилен С2НС13 7,9 - 10,0 65
Пропан СзН8 2,1 9,5 2,3 55
Пропилен С3Н6 2,0 11,7 2,1 53
н-Бутан C4H10 1,5 8,5 1,8 49
Диэтиловый эфир С4Н10О 1,7 36 2,0 82
1-бутилен С4Н8 1,6 10 1,8 58
2-бутилен С4Н8 1,7 9,7 1,7 55
Текст разбит на строки непечатными (управляющими) символами CR/LF.
Первая строка никак не разделена и в программе будет представлена типом String.
Вторая строка разбита на элементы непечатным (управляющим) символом горизонтальной табуляции (НТ). Для представления второй строки в программе будет использоваться строковый массив типа String.
Третья и последующие строки, так же как и вторая, разбиты на элементы символом горизонтальной табуляции (НТ), но элементы имеют разные типы (строковые и числовые), поэтому будет использоваться ЗАПИСЬ пользовательского типа “param”, состоящего из одной переменной типа String и массива типа Single - для одной строки
Type param
prop As String
vol(7) As Single
EndType
и массив ЗАПИСЕЙ – для нескольких строк.
Dim mas() As param
В тексте также вместо чисел встречается символ «дефис» («-»), что затрудняет сортировку строк, поэтому данный символ программа будет заменять на число ноль.
If smb = "-" Then
par.vol(q) = 0
Для последовательного чтения строк из файла будет использован цикл DOUNTIL, условием выхода из цикла будет являться состояние EOF (EndOfFile-конец файла). Конец файла определяется размером файла. Подпрограмма находится в отдельном модуле и вызывается главной программой.
Sub InputData(name As String, nf1 As Integer, st() As String, sk() As String, k As Integer)
k = 0
Open name For Input As nf1
Do Until EOF(nf1)
ReDim Preserve st(k)
Line Input #nf1, st(k)
ReDim Preserve sk(k)
sk(k) = st(k)
k = k + 1
Loop
Close #nf1
End Sub
2. Определение кодировки файла
Кодировка представляет собой таблицу символов, где каждой букве алфавита (а также цифрам и специальным знакам) присвоен свой уникальный номер - код символа.
Стандартизирована только половина таблицы, т.н. ASCII-код - первые 128 символов, которые включают в себя буквы латинского алфавита. И с ними никогда не бывает проблем. Вторая же половина таблицы (а всего в ней 256 символов - по количеству состояний, который может принять один байт) отдана под национальные символы, и в каждой стране эта часть различна. Но только в России было придумано целых 5 различных кодировок. Термин "различные" обозначает то, что одному и тому же символу соответствует разный цифровой код. Т.е. если неправильно определить кодировку текста, то пользователю предстанет абсолютно нечитаемый текст.
Использование множества кодировок в современном ПО создаёт много неудобств не только программистам, но и пользователям. Согласно рациональной точке зрения, справиться с непонятными символами можно, если программы будут автоматически распознавать кодировку входящего текста.
Для однобайтных кодировок можно учитывать тот факт, что частота использования разных букв сильно различается (например, в русском часто используется «о», но редко «ъ»). Поэтому, зная язык текста, можно легко выбрать кодировку, в которой частота байтов лучше соответствует частоте букв данного языка.
Для определения кодировки текстового файла нужно выполнить следующий план действий:
1. Поочередно перебирая символы из текста, определять код символа и проверять принадлежность его к каждой кодовой таблице.
2. Увеличивать на 1 счетчики тех кодовых таблиц, которым не противоречит код символа.
3. Найти максимальное значение среди счетчиков – оно укажет на наиболее вероятную кодировку.
Текст, кодированный в Unicode, выглядит иначе. Каждый символ в Unicode кодируется двумя байтами, в первом байте памяти хранится код символа Unicode, а во втором всегда 04. Поэтому чтобы определить имеет ли текст кодировку Unicode, достаточно проверить второй байт памяти, он должен хранить код 04.
Подпрограмма проверки принадлежности текста к одной из шести кодовых таблиц:
Sub FindCP(stroky() As String, msg1 As String, msg2 As String, index As Integer)
Dim s As Integer, z As Integer
Dim symb As String * 1
Dim kod As Byte
Dim scp(7) As codepage
Dim ks As String, ks1 As String
Dim ks2 As String, ne As String
ks = "Ваш текст предположительно имеет кодировку "
ne = "не "
ks1 = "Требуется "
ks2 = "Перекодировка "
For s = 0 To UBound(stroky)
For z = 1 To Len(stroky(s))
symb = Mid(stroky(s), z, 1)
kod = Asc(symb)
If cp1(kod) Then scp(0).vol = scp(0).vol + 1: scp(0).name = "КОИ-8R"
If cp2(kod) Then scp(1).vol = scp(1).vol + 1: scp(1).name = "Cp1251"
If cp3(kod) Then scp(2).vol = scp(2).vol + 1: scp(2).name = "OEM"
If cp4(kod) Then scp(3).vol = scp(3).vol + 1: scp(3).name = "Cp866"
If cp5(kod) Then scp(4).vol = scp(4).vol + 1: scp(4).name = "Mac"
If cp6(kod) Then scp(5).vol = scp(5).vol + 1: scp(5).name = "ISO"
If cp71(symb) Then scp(6).vol = scp(6).vol + 1: scp(6).name = "Unicode"
Next z
Next s
z = 0
For s = 0 To 6
If scp(s).vol >= z Then
z = scp(s).vol: index = s
End If
Next s
'При совпадении счетчиков "КОИ-8R" и "cp1251" кодировка текста определяется как "cp1251"
If ((scp(0).vol = scp(1).vol) And index <= 1) Then index = 1
If index = 1 Then
msg1 = ks & scp(index).name
msg2 = ks2 & ne & LCase(ks1)
Else:
msg1 = ks & scp(index).name
msg2 = ks1 & LCase(ks2)
End If
End Sub
Данная подпрограмма использует функции проверки принадлежности кода к заданному диапазону. Функции находятся в отдельном модуле.
Функции проверки принадлежности кода к заданному диапазону:
'КодоваятаблицаКОИ-8R
Function cp1(kod As Byte) As Boolean
Dim a As Boolean, b As Boolean
Dim e As Boolean, d As Boolean
Const x1 = 163, X2 = 179
Const x4 = 195, X5 = 255
a = x1 = kod: b = X2 = kod
d = x4 <= kod: e = kod <= X5
cp1 = (a) Or (b) Or (d And e)
End Function
'Кодоваятаблица Cp1251
Function cp2(kod As Byte) As Boolean
Dim a As Boolean, b As Boolean
Dim c As Boolean, d As Boolean
Const x1 = 168, X2 = 184
Const x3 = 195, x4 = 255
a = x1 = kod: b = kod = X2
c = x3 <= kod: d = kod <= x4
cp2 = (a) Or (b) Or (c And d)
End Function
'Кодоваятаблица OEM
Function cp3(kod As Byte) As Boolean
Dim a As Boolean, b As Boolean
Dim c As Boolean, d As Boolean
Dim a1 As Boolean, b1 As Boolean
Dim c1 As Boolean, d1 As Boolean
Dim a2 As Boolean, b2 As Boolean
Dim c2 As Boolean, d2 As Boolean
Dim a3 As Boolean, b3 As Boolean
Dim c3 As Boolean, d3 As Boolean
Dim a4 As Boolean, b4 As Boolean
Dim c4 As Boolean, d4 As Boolean
Const x1 = 132, X2 = 133
Const x3 = 156, x4 = 159
Const X5 = 160, X6 = 173
Const X7 = 181, X8 = 184
Const X9 = 189, X10 = 190
Const X11 = 198, X12 = 199
Const X13 = 208, X14 = 216
Const X15 = 221, X16 = 222
Const X17 = 224, X18 = 238
Const X19 = 225, X20 = 252
a = x1 <= kod: b = kod <= X2: c = x3 <= kod: d = kod <= x4
a1 = X5 <= kod: b1 = kod <= X6: c1 = X7 <= kod: d1 = kod <= X8
a2 = X9 <= kod: b2 = kod <= X10: c2 = X11 <= kod: d2 = kod <= X12
a3 = X13 <= kod: b3 = kod <= X14: c3 = X15 <= kod: d3 = kod <= X16
a4 = X17 <= kod: b4 = kod <= X18: c4 = X19 <= kod: d4 = kod <= X20
cp3 = (a And b) Or (c And d) Or (a1 And b1) Or (c1 And d1) Or (a2 And b2) Or (c2 And d2) Or (a3 And b3) Or (c3 And d3) Or (a4 And b4) Or (c4 And d4)
End Function
'Кодоваятаблица Cp866
Function cp4(kod As Byte) As Boolean
Dim a As Boolean, b As Boolean
Dim c As Boolean, d As Boolean
Const x1 = 128, X2 = 175
Const x3 = 224, x4 = 241
a = x1 <= kod: b = kod <= X2
c = x3 <= kod: d = kod <= x4
cp4 = (a And b) Or (c And d)
End Function
'Кодоваятаблица Mac
Function cp5(kod As Byte) As Boolean
Dim a As Boolean, b As Boolean
Dim c As Boolean, d As Boolean
Const x1 = 128, X2 = 159
Const x3 = 221, x4 = 254
a = x1 <= kod: b = kod <= X2
c = x3 <= kod: d = kod <= x4
cp5 = (a And b) Or (c And d)
End Function
'Кодоваятаблица ISO
Function cp6(kod As Byte) As Boolean
Dim a As Boolean, b As Boolean
Dim c As Boolean, d As Boolean
Const x1 = 160, X2 = 240
Const x3 = 176, x4 = 238
a = x1 = kod: b = kod = X2
c = x3 <= kod: d = kod <= x4
cp6 = (a And b) Or (c And d)
End Function
'Кодовая таблица Unicode (младшие разряды)
Function cp7(kod As Byte) As Boolean
Dim a As Boolean, b As Boolean
Dim c As Boolean, d As Boolean
Const x1 = 1, X2 = 81
Const x3 = 16, x4 = 79
a = x1 = kod: b = kod = X2
c = x3 <= kod: d = kod <= x4
cp7 = a Or b Or (c And d)
End Function
'Продолжение Unicode (старшие разряды(04))
Function cp71(symb As String) As Boolean
Dim k As Byte
Dim a As Boolean
Const x1 = 4
k = AscB(symb)
a = x1 = k
cp71 = a
End Function
3. Алгоритмы перекодировки файла в cp1251
Зная кодировку (п.2) можно составить алгоритм перекодировки текста исходной кодировки в заданную-ср1251. Мною были выбраны шесть кодовых таблиц: КОИ-8R, OEM, cp866, ISO, MAC и Unicode.
С первыми пятью кодировками все просто:
1. Выбрать из строки поочередно каждый символ.
2. Определить код символа заданной кодировки.
3. Добавить (отнять) к коду разницу от кода такого же символа в кодировке 1251.
4. Определить символ по полученному новому коду.
5. Добавить полученный символ в новую строку.
Подпрограмма выбора варианта перекодировки (КОИ-8R, 1251, OEM, 866, MAC, Unicode):
Sub Decoder(Fmas() As String, IndxCP As Integer, r As Integer, Smas() As String)
Dim i As Integer
Dim n As Integer
Dim Stroka As String
Dim OutStr As String
Dim smb As String
Dim code As Byte
IfIndxCP = 1 ThenExitSub'если кодировка cp1251, то выход из процедуры без перекодирования
If IndxCP = 6 Then
Call DecUnicodeTo1251(Fmas, Smas)
Exit Sub
End If
ReDim Smas(r - 1)
For i = 0 To r - 1
Stroka = Fmas(i)
OutStr = ""
For n = 1 To Len(Stroka)
smb = Mid(Stroka, n, 1)
code = Asc(smb)
Select Case IndxCP
Case 0
OutStr = OutStr & Chr(cpKoiTo1251(code))
Case 2
OutStr = OutStr & Chr(cpOEMTo1251(code))
Case 3
OutStr = OutStr & Chr(cp866To1251(code))
Case 4
OutStr = OutStr & Chr(cpMACTo1251(code))
Case 5
OutStr = OutStr & Chr(cpISOTo1251(code))
End Select
Next n
Smas(i) = OutStr
Next i
End Sub
С Unicode немного сложнее:
· В начало текста (Unicode) добавляется два символа «я» и «ю». Их нужно удалить.
· Перекодировать нужно только первый байт, во втором байте всегда 04.
· Символы такие как «точка», «запятая» и другие, кодируются в памяти двумя байтами, но второй байт будет пустой.
1. Выбрать из строки поочередно каждый символ и определить его код.
2. Выбрать следующий за ним символ и определить его код.