Рейтинг@Mail.ru
Программирование

Программирование (20)

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

Реализация SPI: библиотека libMPSSE и .NET

by
Print Friendly, PDF & Email
Рассмотрим программную реализацию класса для .NET, который использует динамически подключаемую библиотеку libMPSSE.dll фирмы FTDI Chip в режиме SPI. Для демонстрации работы библиотеки воспользуемся микросхемой FT2232H и датчиком давления и температуры BMP280.
Print Friendly, PDF & Email
Подробнее ...

Дискретное преобразование Фурье на VB.NET

by
Print Friendly, PDF & Email

Даётся программный код для прямого и обратного преобразования Фурье. Рассматривается быстрое преобразование Фурье.

Дискретное преобразование Фурье (ДПФ) – это мощный инструмент анализа, который широко используется в области цифровой обработки сигналов (ЦОС). Существуют прямое и обратное преобразования Фурье. Прямое дискретное преобразование Фурье переводит сигнал из временной области в частотную и служит для анализа частотного спектра сигнала. Обратное преобразование делает ровно противоположное: по частотному спектру сигнала восстанавливает сигнал во временной области.

Для расчёта преобразования Фурье обычно используется ускоренная процедура расчёта – т.н. быстрое преобразование Фурье (БПФ). Это позволяет в значительной мере сократить процессорное время на достаточно сложные и ресурсоёмкие математические расчёты.

1Комплексныечисла

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

Код класса для описания комплексных чисел (разворачивается)
''' <summary>
''' Комплексное число.
''' </summary>
Public Class ComplexNumber

    ''' <summary>
    ''' Действительная часть комплексного числа.
    ''' </summary>
    Public Real As Double = 0

    ''' <summary>
    ''' Мнимая часть комплексного числа.
    ''' </summary>
    Public Imaginary As Double = 0

    Public Sub New()
        Real = 0
        Imaginary = 0
    End Sub

    ''' <summary>
    ''' Создаёт комплексное число.
    ''' </summary>
    ''' <param name="r">Действительная часть комплексного числа.</param>
    ''' <param name="im">Мнимая часть комплексного числа.</param>
    Public Sub New(ByVal r As Double, Optional ByVal im As Double = 0)
        Real = r
        Imaginary = im
    End Sub

    Private usCult As New Globalization.CultureInfo("en-US") 'используем культуру "en-US" чтобы целая и дробная части разделялись точкой, а не запятой

    ''' <summary>
    ''' Возвращает строку, состоящую из действительной и мнимой части, разделённых символом табуляции.
    ''' </summary>
    Public Overrides Function ToString() As String
        Return (Real.ToString(usCult) & ControlChars.Tab & Imaginary.ToString(usCult))
    End Function

End Class

2Прямое дискретное быстрое преобразование Фурье

Теперь приведу код функции, которая реализует расчёт прямого дискретного преобразования Фурье. Здесь встречается термин «бабочка». Не пугайтесь, это общепринятое название одной из элементарных операций, которые входят в алгоритм БПФ. Подробнее с термином «бабочка» можно ознакомиться здесь.

На вход функции передаётся массив комплексных чисел. Действительная часть которого представляет произвольный дискретный сигнал, с отсчётами через равные промежутки времени. Мнимая часть содержит нули. Число отсчётов в сигнале должно равняться степени двойки. Если ваш сигнал короче, то дополните его нулями до числа, кратного степени 2: 256, 512, 1024 и т.д. Чем длиннее сигнал, тем у рассчитанного спектра будет выше разрешение по частоте.

Код для расчёта прямого быстрого преобразования Фурье на VB.NET (разворачивается)
''' <summary>
''' Рассчитывает спектр сигнала методом быстрого преобразования Фурье. Использовать только (N/2+1) возвращаемых значений (до половины частоты дискретизации).
''' </summary>
''' <param name="signal">Сигнал, содержащий количество отсчётов, кратное степени двойки, и состоящий из действительной и мнимой частей. Все мнимые части сигнала заполнены нулями.</param>
''' <returns>Возвращает массив комплексных чисел спектра. 
''' Значимы только первые N/2+1, остальные - симметричная часть, соответствующая отрицательным частотам.
''' Первое значение спектра - это постоянная составляющая, последнее - соответствует половине частоты дискретизации (частота Найквиста).
''' Значения выше половины частоты дискретизации - не использовать.
''' </returns>
Public Shared Function FFT(ByVal signal As ComplexNumber()) As ComplexNumber()

    Dim order As Integer = signal.Length 'порядок ДПФ
    CheckFftOrder(order) 'Проверяем, что порядок равен степени двойки

    Dim spectrumLen As Integer = order \ 2
    Dim j As Integer = spectrumLen

    'Бит-реверсная сортировка:
    For i As Integer = 1 To order - 2
        If (i < j) Then
            Dim tmpRe As Double = signal(j).Real
            Dim tmpIm As Double = signal(j).Imaginary
            signal(j).Real = signal(i).Real
            signal(j).Imaginary = signal(i).Imaginary
            signal(i).Real = tmpRe
            signal(i).Imaginary = tmpIm
        End If
        Dim k As Integer = spectrumLen
        Do Until (k > j)
            j -= k
            k \= 2
        Loop
        j += k
    Next

    'Цикл по уровням разложения:
    For level As Integer = 1 To CInt(Math.Log(order) / Math.Log(2))
        Dim lvl As Integer = CInt(2 ^ level)
        Dim lvl2 As Integer = lvl \ 2
        Dim tmp As Double = Math.PI / lvl2
        Dim sr As Double = Math.Cos(tmp)
        Dim si As Double = -Math.Sin(tmp)

        Dim tr As Double = 0
        Dim ur As Double = 1
        Dim ui As Double = 0
        For jj As Integer = 1 To lvl2 'Цикл по спектрам внутри уровня
            For i As Integer = (jj - 1) To (order - 1) Step lvl 'Цикл по отдельным "бабочкам"
                Dim ip As Integer = i + lvl2
                tr = signal(ip).Real * ur - signal(ip).Imaginary * ui 'Операция "бабочка"
                Dim ti As Double = signal(ip).Real * ui + signal(ip).Imaginary * ur
                signal(ip).Real = signal(i).Real - tr
                signal(ip).Imaginary = signal(i).Imaginary - ti
                signal(i).Real = signal(i).Real + tr
                signal(i).Imaginary = signal(i).Imaginary + ti
            Next
            tr = ur
            ur = tr * sr - ui * si
            ui = tr * si + ui * sr
        Next
    Next

    'Заполняем массив комплексных чисел, обработанных БПФ:
    Dim spectrum(order - 1) As ComplexNumber
    For i As Integer = 0 To order - 1 
        With signal(i)
            spectrum(i) = New ComplexNumber(.Real, .Imaginary)
        End With
    Next

    Return spectrum

End Function

3Обратное дискретное быстрое преобразование Фурье

Обратное дискретное преобразование Фурье (ОДПФ) одним из этапов расчёта включает в себя прямое ДПФ на массиве комплексных чисел, где мнимая часть – это инверсия относительно оси X мнимой части спектра.

Код для расчёта обратного быстрого преобразования Фурье на VB.NET (разворачивается)
''' <summary>
''' Восстанавливает сигнал по его спектру методом обратного быстрого преобразования Фурье.
''' </summary>
''' <param name="spectrum">Спектр сигнала, содержащий количество отсчётов, кратное степени двойки, и состоящий из действительной и мнимой частей.</param>
Public Shared Function InverseFFT(ByVal spectrum As ComplexNumber()) As ComplexNumber()

    Dim order As Integer = spectrum.Length 'Порядок обратного ДПФ.
    CheckFftOrder(order)

    'Изменение арифметического знака элементов мнимой части:
    For i As Integer = 0 To spectrum.Length - 1
        spectrum(i).Imaginary = -spectrum(i).Imaginary
    Next

    'Вычисление прямого БПФ:
    Dim directFFT As ComplexNumber() = FFT(spectrum)

    'Деление на order во временной области со сменой арифметического знака мнимой части:
    Dim signal(directFFT.Length - 1) As ComplexNumber
    For i As Integer = 0 To directFFT.Length - 1
        Dim ReX As Double = directFFT(i).Real / order
        Dim ImX As Double = -directFFT(i).Imaginary / order
        signal(i) = New ComplexNumber(ReX, ImX)
    Next

    Return signal

End Function

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

''' <summary>
''' Проверяет, является ли порядок БПФ степенью двойки, и если нет - вызывает исключение.
''' </summary>
''' <param name="order">Порядок БПФ.</param>
Private Shared Sub CheckFftOrder(ByVal order As Integer) 
    Dim chk As Double = Math.Abs(Math.Floor(Math.Log(order, 2)) - Math.Log(order, 2))
    If (chk > 0.0001) Then
        Throw New ArgumentException(String.Format("Длина массива ({0}) не кратна степени двойки.", order))
    End If
End Sub

4Проверка прямого и обратного преобразования Фурье

Теперь давайте проверим, что наши функции работают. Для этого пропустим произвольный сигнал через механизм прямого преобразования Фурье, а затем «соберём» его обратно с помощью обратного преобразования Фурье. Восстановленный сигнал должен практически совпадать с исходным. Ошибки округления, возникающие при работе с числами в компьютере, имеют место быть, поэтому сигналы не будут идентичны полностью, но их отклонение друг от друга должно быть пренебрежимо малым.

Для примера в качестве исходного сигнала возьмём функцию синуса и сформируем данные длиной 128 отсчётов вот таким образом:

Dim cn(127) As ComplexNumber
For i As Integer = 0 To cn.Length - 1
    cn(i) = New ComplexNumber(Math.Sin(i * 3 * Math.PI / 180))
Next

Получим вот такой сигнал:

Исходный сигнал во временной области до преобразования Фурье
Исходный сигнал во временной области до преобразования Фурье

Здесь по оси X – номера отсчётов во временной области, по оси Y – амплитуда. Обратим внимание, что сигнал состоит только из действительных частей, а мнимая часть на всём отрезке равна "0".

Теперь передадим этот сигнал на вход функции FFT(). По полученным в ходе прямого преобразования Фурье массивам комплексных чисел построим два графика – действительной (Re) и мнимой (Im) частей спектра:

Действительная и мнимая части сигнала в частотной области
Действительная и мнимая части сигнала в частотной области

Здесь по оси X – отсчёты в частотной области, по оси Y – амплитуда. Чтобы получить реальные значения частоты, необходимо рассчитать их, учитывая, что "0" оси Y соответствует нулевой частоте, максимум оси Y соответствует частоте дискретизации.

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

Действительная и мнимая части восстановленного с помощью обратного преобразования Фурье сигнала
Действительная и мнимая части восстановленного с помощью обратного преобразования Фурье сигнала

Как видно, восстановленный сигнал полностью повторяет исходный.

Print Friendly, PDF & Email
Подробнее ...

Кодер и декодер кода Хэмминга на VB.NET

by
Print Friendly, PDF & Email

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

1Что такое код Хэмминга

Код Хэмминга добавляет к сообщению (информационные разряды) некоторое количество избыточной информации (проверочные разряды), сформированной определённым образом. Сообщение с добавленной проверочной информацией называется «кодовый символ» или «кодовое слово». Параметры кода указываются, например, так: (7, 4). Это означает, что длина кодового слова равна 7 битам, а длина сообщения – 4 бита. В зависимости от количества информационных и проверочных разрядов в кодовых словах существуют коды Хэмминга (7,4), (9,5), (11, 7), (15, 11), (31, 26), (63, 57) и т. д.

Общий вид формулы, по которой определяются виды кодов Хэмминга по соотношению числа информационных символов к проверочным: (2x − 1, 2x − x − 1), где x – натуральное число.

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

Из-за своей простоты, кодирование кодом Хемминга получило широкое распространение. Оно применяется, например, в беспроводной технологии WiFi, в системах хранения данных (RAID-массивах), в некоторых типах микросхем памяти, в схемотехнике и т.д.

Хорошая статья, описывающая принцип работы кода Хэмминга, есть, например, на Хабре.

2Кодер кода Хэмминга (15, 11), написанный на VB.NET

Напишем кодировщик, который будет получать на вход 11 бит данных, кодировать их и возвращать 15 бит выходной информации. Если на вход пришло больше 11-ти бит данных, генерируется исключение. Если данных меньше 11-ти бит (например, 1 байт – 8 бит), то число дополняется нулями в старших разрядах до 11-ти бит и далее кодируется обычным образом. Возвращает кодер 16 бит (кодовое слово).

Код кодера Хэмминга (15, 11) на VB.NET (разворачивается)
''' <summary>
''' Кодирует 11 бит информации кодом Хэмминга (15, 11). 
''' Входные данные – 11 бит, выходные – 16 бит.
''' </summary>
''' <param name="dataIn">Входные данные, не более 11-ти бит.</param>
''' <remarks>
''' Размещение проверочных и информационных бит в кодовом слове:
''' 
'''          |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
''' in_data  |  |  |  |  |  | L| K| I| H| G| F| E| D| C| B| A|
''' code_word| L| K| I| H| G| F| E| P| D| C| B| P| A| P| P| X|
''' 
''' A,B,C,D,E,F,G,H,I,K,L – биты данных информационного слова;
'''                     P – проверочный бит;
'''                     X – бит, равный 0 (не используется).
''' Кодовое слово дополняется одним битом, чтобы длина была равна степени двойки. Сейчас этот бит никак не используется.
''' Можно его использовать как бит чётности и получить так называемый дополненный код Хэмминга. Здесь этого не сделано.
''' </remarks>
Public Shared Function Encode_15_11(ByVal b As BitArray) As BitArray
 
            'Можно также добавить проверку на длину передаваемой битовой последовательности:
            'If (b.Count > 11) Then
            '    Throw New ArgumentException("Входная последовательность длиннее 11-ти битов.")
            'End If

            Dim preDataIn As New BitArray(11)
            For i As Integer = 0 To 10
                If (b.Count > i) Then
                    preDataIn(i) = b(i)
                Else
                    Exit For
                End If
            Next

            Dim dataIn As New BitArray(preDataIn)

            'Весь процесс кодирования – это сложение по модулю два бит информационного слова.
            Dim codeWord As New BitArray(16)

            'Младший разряд не используется. Можно добавить в него проверку на чётность.
            codeWord(0) = False

            'Вычисление первого проверочного символа:
            codeWord(1) = dataIn(0) Xor dataIn(1) Xor dataIn(3) Xor dataIn(4) Xor dataIn(6) Xor dataIn(8) Xor dataIn(10)

            'Вычисление второго проверочного символа:
            codeWord(2) = dataIn(0) Xor dataIn(2) Xor dataIn(3) Xor dataIn(5) Xor dataIn(6) Xor dataIn(9) Xor dataIn(10)

            'Вычисление третьего проверочного символа:
            codeWord(4) = dataIn(1) Xor dataIn(2) Xor dataIn(3) Xor dataIn(7) Xor dataIn(8) Xor dataIn(9) Xor dataIn(10)

            'Вычисление четвертого проверочного символа:
            codeWord(8) = dataIn(4) Xor dataIn(5) Xor dataIn(6) Xor dataIn(7) Xor dataIn(8) Xor dataIn(9) Xor dataIn(10)

            'Информационные символы:
            codeWord(3) = dataIn(0)
            codeWord(5) = dataIn(1)
            codeWord(6) = dataIn(2)
            codeWord(7) = dataIn(3)
            codeWord(9) = dataIn(4)
            codeWord(10) = dataIn(5)
            codeWord(11) = dataIn(6)
            codeWord(12) = dataIn(7)
            codeWord(13) = dataIn(8)
            codeWord(14) = dataIn(9)
            codeWord(15) = dataIn(10)

            Return codeWord

        End Function

Данный кодер легко переписать таким образом, чтобы он работал не с битовыми массивами типа BitArray(), а с байтами: на вход получал 11-разрядное число (от 0 до 0x7FF) и выдавал 2 закодированных байта:

Public Shared Function Encode_15_11(ByVal numberToEncode As Integer) As Byte()
    Dim enc As BitArray = Hamming.Encode_15_11(New BitArray({numberToEncode}))
    Dim encBytes(1) As Byte
    enc.CopyTo(encBytes, 0)
    Return encBytes
End Function

3Декодер кода Хэмминга (15, 11), написанный на VB.NET

Теперь пора поговорить о декодере. Декодер плучает на вход 2 байта закодированных данных и возвращает 11 бит декодированных данных, которые распределены по двум байтам. Если в кодер были переданы 8 бит данных, то нас будет интересовать только первый байт, полученный с декодера.

Код декодера Хэмминга (15, 11) на VB.NET (разворачивается)
''' <summary>
''' Декодер кода (15, 11). 
''' </summary>
''' <param name="b">Входные данные, 16 бит (2 байта).</param>
''' <returns>Выходные данные – 10 бит.</returns>
''' <remarks>
''' Размещение проверочных и информационных бит в кодовом слове:
''' 
'''          |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
''' code_word| L| K| I| H| G| F| E| P| D| C| B| P| A| P| P| X|
''' out_data |  |  |  |  |  | L| K| I| H| G| F| E| D| C| B| A|
''' 
''' A,B,C,D,E,F,G,H,I,K,L – биты данных информационного слова;
'''                     P – проверочный бит;
'''                     X – бит, равный 0 (не используется).
''' 
''' Кодовое слово дополняется одним битом, чтобы длина была равна степени двойки. Сейчас этот бит никак не используется.
''' Можно его использовать как бит четности и получить так называемый дополненный код Хэмминга. Здесь этого не сделано.
''' </remarks>
Public Shared Function Decode_15_11(ByVal b As Byte()) As Integer

    Dim codeWord As New BitArray(b) '16 бит входных данных

    'Весь процесс декодирования – это сложение по модулю два бит информационного слова, по весу полученных единиц в результате – получение позиции ошибки.
    Dim syndrome As New BitArray(4) 

    'Вычисление первого проверочного символа из полученного кодового слова и далее сравнение его с полученным.
    syndrome(0) = codeWord(3) Xor codeWord(5) Xor codeWord(7) Xor codeWord(9) Xor codeWord(11) Xor codeWord(13) Xor codeWord(15) Xor codeWord(1)

    'Вычисление второго проверочного символа из полученного кодового слова и далее сравнение его с полученным.
    syndrome(1) = codeWord(3) Xor codeWord(6) Xor codeWord(7) Xor codeWord(10) Xor codeWord(11) Xor codeWord(14) Xor codeWord(15) Xor codeWord(2)

    'Вычисление третьего проверочного символа из полученного кодового слова и далее сравнение его с полученным.
    syndrome(2) = codeWord(5) Xor codeWord(6) Xor codeWord(7) Xor codeWord(12) Xor codeWord(13) Xor codeWord(14) Xor codeWord(15) Xor codeWord(4)

    'Вычисление четвёртого проверочного символа из полученного кодового слова и далее сравнение его с полученным.
    syndrome(3) = codeWord(9) Xor codeWord(10) Xor codeWord(11) Xor codeWord(12) Xor codeWord(13) Xor codeWord(14) Xor codeWord(15) Xor codeWord(8)

    'Вычисление по синдрому позиции ошибки. Это просто ПЗУ или дешифратор.
    'Если смотреть на синдром как на число - то это и есть номер позиции ошибки.
    'Синдром равен 0 - ошибки нет.
    'Поскольку на выход модуля передаются только биты данных - не все варианты перечислены, нет смысла исправлять проверочные биты.
    Dim syn As Integer = (Convert.ToInt32(syndrome(3)) << 3) Or (Convert.ToInt32(syndrome(2)) << 2) Or (Convert.ToInt32(syndrome(1)) << 1) Or Convert.ToInt32(syndrome(0))

    '         |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
    'code_word| L| K| I| H| G| F| E| P| D| C| B| P| A| P| P| X|
    'Синдромы ошибок в информационных битах, позиции в кодовом слове 3,5,6,7,9,10,11,12,13,14,15:
    Dim correction As New BitArray(11) 
    Select Case syn
        Case 3 'позиция 3
            correction = New BitArray({True, False, False, False, False, False, False, False, False, False, False}) 
        Case 5 'позиция 5
            correction = New BitArray({False, True, False, False, False, False, False, False, False, False, False}) 
        Case 6 'позиция 6
            correction = New BitArray({False, False, True, False, False, False, False, False, False, False, False}) 
        Case 7 'позиция 7
            correction = New BitArray({False, False, False, True, False, False, False, False, False, False, False}) 
        Case 9 'позиция 9
            correction = New BitArray({False, False, False, False, True, False, False, False, False, False, False}) 
        Case 10 'позиция 10
            correction = New BitArray({False, False, False, False, False, True, False, False, False, False, False})  
        Case 11 'позиция 11
            correction = New BitArray({False, False, False, False, False, False, True, False, False, False, False})  
        Case 12 'позиция 12
            correction = New BitArray({False, False, False, False, False, False, False, True, False, False, False})  
        Case 13 'позиция 13
            correction = New BitArray({False, False, False, False, False, False, False, False, True, False, False})  
        Case 14 'позиция 14
            correction = New BitArray({False, False, False, False, False, False, False, False, False, True, False})  
        Case 15 'позиция 15
            correction = New BitArray({False, False, False, False, False, False, False, False, False, False, True})  
        Case Else
            'Если синдром равен 0 или указывает на ошибку в проверочном символе "P" - коррекция информационных символов не требуется.
            'Мы инициализировали correction() нулями (correction = New BitArray(11)), поэтому ничего делать не нужно.
        End Select

        'Результат декодирования с учетом коррекции (11 бит выходных данных):
        Dim outData As New BitArray(11)
        outData(0) = codeWord(3) Xor correction(0)    
        outData(1) = codeWord(5) Xor correction(1)     
        outData(2) = codeWord(6) Xor correction(2)     
        outData(3) = codeWord(7) Xor correction(3)     
        outData(4) = codeWord(9) Xor correction(4)     
        outData(5) = codeWord(10) Xor correction(5)   
        outData(6) = codeWord(11) Xor correction(6)  
        outData(7) = codeWord(12) Xor correction(7) 
        outData(8) = codeWord(13) Xor correction(8) 
        outData(9) = codeWord(14) Xor correction(9) 
        outData(10) = codeWord(15) Xor correction(10) 

        Dim masks(31) As Integer
        masks(0) = BitVector32.CreateMask()
        For i As Integer = 1 To 31
            masks(i) = BitVector32.CreateMask(masks(i - 1))
        Next

        Dim v As New BitVector32
        For i As Integer = 0 To 10
            v(masks(i)) = outData(i)
        Next
        Dim decoded As Integer = v.Data

        Return decoded

End Function

4Консольная программа, кодирующая и декодирующая код Хемминга (15, 11)

Для быстрой проверки кодировщика и декодировщика кода Хэмминга (15, 11), используя вышеописанные классы, я написал две программы. Первая – кодер Хэмминга. Вводите 11-разрядное число (от 0 до 0x7FF или 2047), и на выходе получаем 16-разрядное число, представленное в виде двух байтов.

Внешний вид программы кодера кода Хэмминга (15, 11)
Внешний вид программы кодера кода Хэмминга (15, 11)

Вторая программа – декодер кода Хмминга (15, 11).

Внешний вид программы декодера кода Хэмминга (15, 11)
Внешний вид программы декодера кода Хэмминга (15, 11)

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

Обе программы работают под ОС Windows и требуют .NET версии 3.5. Выкладываю описанные программы.

Скачать кодер и декодер кода Хэмминга (15, 11)

Print Friendly, PDF & Email
Подробнее ...

Работа с *.INI файлами в VB.NET

by
Print Friendly, PDF & Email

Как правило, настраиваемые параметры приложения хранятся во внешних по отношению к программе расположениях. Имеется множество способов вынести настройки программы вне кода самой программы. Распространёнными способами являются: хранение параметров приложения в реестре Windows, в базах данных, в конфигурационных XML файлах, в файлах *.config или *.cfg, в специфических двоичных форматах и, конечно же, в *.ini файлах. Эти файлы мы сейчас и рассмотрим.

1Что такое INI-файлы

Что такое INI-файлы известно, наверное, каждому более-менее опытному пользователю операционной системы Windows. Зачастую файл с расширением *.INI – это самый быстрый и удобный способ вынести настраиваемые параметры приложения вне кода программы. Эти файлы имеют свою, довольно простую, структуру и, как правило, отличаются малыми размерами. Это обычные текстовые файлы, которые открываются даже в «Блокноте».

Вообще, INI-файл может иметь некоторые отступления от общепринятых норм, каждый разработчик определяет структуру и синтаксис файла для себя. Но есть некоторые общепринятые соглашения. Собственно, вот как выглядит структура INI-файла в общем виде:

; комментарий
# комментарий в стиле Unix

[Section1]
; комментарий 
key1=value_1
key2=value_2
  
[Section2]
;другой комментарий
key3=value_3 
;ещё один комментарий
key4=value_4

Видно, что *.INI файл может содержать:

  • комментарии;
  • заголовки разделов (секций);
  • параметры в виде пары «ключ-значение»;
  • пустые строки.

Пример INI-файла:

;;;;;;;;;;;;;;;;;;;;;;;;;;
; Copyright Soltau.ru ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;

[Settings]
height=600
width=800
lang=RU

2Создание разделов и ключей INI-файлас помощью Visual Basic .NET и kernel32.dll

Можно изобретать велосипед и создать свой разборщик и генератор для INI-файлов. Но мы поступим более рационально и приземлённо и воспользуемся готовым решением из самой ОС Windows.

Дело в том, что в поставку любой операционной системы семейства Windows входит динамическая библиотека kernel32.dll. Эта библиотека расположена в системном каталоге Windows/system32. Она имеет множество функций для доступа к API Windows. В том числе и для работы с INI-файлами. Описывать все его возможности нет смысла, т.к. их великое множество (вот, например, описание kernel32 на 500+ страниц). Посмотреть все методы библиотеки можно с помощью наших старых знакомых DllExportViewer или DependencyWalker.

Нас здесь интересуют всего два метода: GetPrivateProfileString() для чтения ключей и разделов INI-файлов и WritePrivateProfileString() для записи.

Напишем класс-обёртку для упрощённого доступа к функциям работы с файлами *.INI, предоставляемым библиотекой kernel32:

Код класса IniFile.vb для работы с INI-файлами (разворачивается)
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text

Namespace Ini

    Public Class IniFile

#Region "ИМПОРТ DLL"

        ''' <summary>
        ''' Записывает ключ в заданный раздел INI-файла.
        ''' </summary>
        ''' <param name="section">Имя раздела.</param>
        ''' <param name="key">Имя ключа.</param>
        ''' <param name="value">Значение ключа.</param>
        ''' <param name="filePath">Путь к INI-файлу.</param>
        <DllImport("kernel32")>
        Private Shared Function WritePrivateProfileString(ByVal section As String, ByVal key As String, ByVal value As String, ByVal filePath As String) As Long
        End Function

        ''' <summary>
        ''' Считывает ключ заданного раздела INI-файла.
        ''' </summary>
        ''' <param name="section">Имя раздела.</param>
        ''' <param name="key">Имя ключа.</param>
        ''' <param name="[default]"></param>
        ''' <param name="retVal"></param>
        ''' <param name="size"></param>
        ''' <param name="filePath">Путь к INI-файлу.</param>
        ''' <remarks>С помощью конструктора записываем путь до файла и его имя. </remarks>
        <DllImport("kernel32")>
        Private Shared Function GetPrivateProfileString(ByVal section As String, ByVal key As String, ByVal [default] As String, ByVal retVal As StringBuilder, ByVal size As Integer, ByVal filePath As String) As Integer
        End Function

#End Region '/ИМПОРТ DLL

#Region "КОНСТРУКТОР"

        ''' <summary>
        ''' Имя файла.
        ''' </summary>
        Private IniPath As String

        ''' <summary>
        ''' Читаем ini-файл и возвращаем значение указного ключа из заданной секции. 
        ''' </summary>
        ''' <param name="iniPath"></param>
        Public Sub New(ByVal iniPath As String)
            Me.IniPath = New FileInfo(iniPath).FullName.ToString
        End Sub

#End Region '/КОНСТРУКТОР

#Region "МЕТОДЫ РАБОТЫ С INI-ФАЙЛАМИ"

        ''' <summary>
        ''' Проверяет, что заданный ключ существует в INI-файле.
        ''' </summary>
        ''' <param name="section">Имя раздела.</param>
        ''' <param name="key">Имя ключа.</param>
        Public Function KeyExists(ByVal section As String, ByVal key As String) As Boolean
            Return (Me.ReadKey(section, key).Length > 0)
        End Function

        ''' <summary>
        ''' Читает значение заданного ключа в заданном разделе INI-файла.
        ''' </summary>
        ''' <param name="section">Имя раздела.</param>
        ''' <param name="key">Имя ключа.</param>
        Public Function ReadKey(ByVal section As String, ByVal key As String) As String
            Dim retVal As New StringBuilder(&HFF)
            IniFile.GetPrivateProfileString(section, key, "", retVal, &HFF, Me.IniPath)
            Return retVal.ToString()
        End Function

        ''' <summary>
        ''' Создаёт заданный ключ в заданном разделе. Если раздел не существует, он будет создан.
        ''' </summary>
        ''' <param name="section">Имя раздела.</param>
        ''' <param name="key">Имя ключа.</param>
        ''' <param name="value">Значение ключа. Если NULL, то ключ будет удалён. Если String.Empty, то присвоится пустое значение.</param>
        Public Sub WriteKey(ByVal section As String, ByVal key As String, ByVal value As String)
            IniFile.WritePrivateProfileString(section, key, value, Me.IniPath)
        End Sub

        ''' <summary>
        ''' Удаляет заданный ключ из заданного раздела INI-файла.
        ''' </summary>
        ''' <param name="section">Имя раздела.</param>
        ''' <param name="key">Имя ключа.</param>
        Public Sub DeleteKey(ByVal section As String, ByVal key As String)
            Me.WriteKey(section, key, Nothing)
        End Sub

        ''' <summary>
        ''' Удаляет заданный раздел INI-файла.
        ''' </summary>
        ''' <param name="section">Имя раздела.</param>
        Public Sub DeleteSection(ByVal section As String)
            Me.WriteKey(section, Nothing, Nothing)
        End Sub

#End Region '/МЕТОДЫ РАБОТЫ С INI-ФАЙЛАМИ

    End Class  

End Namespace

Использовать класс IniFile можно, например, так:

Dim iniFile as New IniFile("config.ini")
iniFile.WriteKey("SECTION1", "KEY1", "12345")
If iniFile.KeyExists("SECTION1", "KEY1")
    MessageBox.Show("Ключ создан!")
    iniFile.DeleteKey("SECTION1", "KEY1")
    MessageBox.Show("Ключ удалён!")
End If
Print Friendly, PDF & Email
Подробнее ...

Можно ли использовать константы условной компиляции в XAML

by
Print Friendly, PDF & Email

Мы рассматривали такое мощное средство Visual Studio как константы условной компиляции. Это средство позволяет, в зависимости от значения заданной константы, включать и исключать из исполняемого файла те или иные участки кода. А существует ли аналогичная возможность для разметки XAML?

Print Friendly, PDF & Email
Подробнее ...

Как в Visual Studio создать повторно используемый фрагмент кода (snippet)

by
Print Friendly, PDF & Email

Рассмотрим процесс создания повторно используемого фрагмента кода (сниппета) в Visual Studio 2017 на примере. Создадим сниппет на языке VB.NET, который объявляет объект типа BackgroundWorker() и создаёт для него методы DoWork(), ProcessChanged() и RunWorkerCompleted().

Print Friendly, PDF & Email
Подробнее ...
Подписаться на этот канал RSS