Страницы

Поиск по вопросам

суббота, 4 января 2020 г.

Битовая математика

#c_sharp #инспекция_кода #byte


Программа читает бинарные данные из файла. У меня был пример чтения, на основе которого,
я реализовал данный пример записи.Общий смысл: есть заголовок файла (это код до слов
"пишем данные"). С заголовком все просто. Пишется без всяких преобразований. Далее
пишется маска и после маски пишутся 3 значения переменных. В маску пишется сколько
байт каждое из чисел занимает.

Код:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ConnectButton_Click(object sender, RoutedEventArgs e)
    {
        // Переменная хранит путь к файлу
        string file_path = "ResultFile.rls";

        // Удаляем файл, если он есть
        if (File.Exists(file_path))
        {
            File.Delete(file_path);
        }

        // Создаем файл
        FileStream fs = File.Open(file_path, FileMode.OpenOrCreate, FileAccess.Write,
FileShare.Write);
        // Поток для записи в файл
        BinaryWriter bw = new BinaryWriter(fs);

        // Пишем размерность. Double
        bw.Write(-1.0);
        // Пишем какую то хрень. int32. 
        bw.Write(2);
        // Количество записей. int32. 
        bw.Write(5);
        // Val1. Double
        bw.Write(6800.0);
        // Val2. Double
        bw.Write(6800.0);
        // Val3. Double
        bw.Write(6800.0);
        // Val4 Double
        bw.Write(6800.0);
        // DateTime. Int64
        bw.Write(new DateTime(2015, 03, 12, 23, 0, 0).Ticks);
        // Val5. Int64
        bw.Write((Int64)2);

        // пишем данные
        AddItem(bw, 5, 10, 0);
        AddItem(bw, 1, 0, 1);
        AddItem(bw, 3, 20, 3);
        AddItem(bw, 5, 60, 1);
        AddItem(bw, 4, 80, 2);

        bw.Close();
        fs.Close();
    }

    // Функция отрезает не значимые значения массива
    byte[] CutZero(byte[] in_array)
    {
        // Список байт
        List result;
        // Инициализируем список
        result = new List();

        // Переменная определяет, что пора начинать копирование
        bool ready_to_copy = false;

        // В цикле перебираем каждый байт переданного массива с конца
        for (int i = in_array.Length - 1; i >= 0; i--)
        {
            // Если это не нулевой байт, то начинаем копирование
            if (in_array[i] != 0x00)
            {
                ready_to_copy = true;
            }

            // Если копирование началось
            if(ready_to_copy)
            {
                result.Add(in_array[i]);
            }
        }

        // Возвращаем полученный массив
        return result.ToArray();
    }

    // Добавить данные в файл
    private void AddItem(BinaryWriter _bw, int _vol, int _dp, int _dt)
    {
        // 1.Считаем переменные
        // 1.1. Val1
        byte[] vol = CutZero(BitConverter.GetBytes(_vol));
        // 1.2. Val2
        byte[] dp = CutZero(BitConverter.GetBytes(ConvertVal2Bytes(_dp)));
        // 1.3. Val3
        byte[] dt = CutZero(BitConverter.GetBytes(_dt));

        // 1. Количество байт. Сразу пишем в двоичном виде
        string str_length = "0";
        // 1.1. Для Val1
        str_length += ConvertFromVal1(vol.Length);
        // 1.2. Для Val2
        str_length += ConvertFromVal2(dp.Length);
        // 1.3. Для Val3
        str_length += ConvertFromVal3(dt.Length);

        // Пишем маску
        _bw.Write(Convert.ToByte(str_length, 2));

        // Val3
        _bw.Write(dt);
        // Val2
        _bw.Write(dp);
        // Val1
        _bw.Write(vol);
    }

    // Конвертируем байты
    private byte ConvertVal2Bytes(int _dp)
    {
        byte result;
        // Получим массив байт, для определения длины
        byte[] dp = CutZero(BitConverter.GetBytes(_dp));

        // В зависимости от количества байт добавляем дельты
        switch (dp.Length)
        {
            case 0:
            {
                // Ничего не добавляем
                result = (byte)_dp;
                break;
            }
            case 1:
                {
                    result = (byte)((byte)_dp + 0x80);
                    break;
                }
            case 2:
                {
                    result = (byte)((byte)_dp + 0x400);
                    break;
                }
            case 4:
                {
                    result = (byte)((byte)_dp + 0x4000000);
                    break;
                }
            default:
                {
                    result = (byte)_dp;
                    break;
                }
        }

        // Возвращаем результат
        return result;
    }

    // Конвертируем длину в двоичное представление
    private string ConvertFromVal3(int length)
    {
        string result = "";

        switch (length)
        {
            case 0:
                result = "00";
                break;
            case 1:
                result = "01";
                break;
            case 2:
                result = "10";
                break;
            case 3:
                result = "11";
                break;
            default:
                break;
        }

        return result;
    }
    // Конвертируем длину в двоичное представление
    private string ConvertFromVal2(int length)
    {
        string result = "";

        switch (length)
        {
            case 0:
                result = "00";
                break;
            case 1:
                result = "01";
                break;
            case 2:
                result = "10";
                break;
            case 4:
                result = "11";
                break;
            default:
                break;
        }

        return result;
    }
    // Конвертируем длину в двоичное представление
    private string ConvertFromVal1(int length)
    {
        string result = "";

        switch (length)
        {
            case 1:
                result = "001";
                break;
            case 2:
                result = "110";
                break;
            case 4:
                result = "111";
                break;
            case 8:
                result = "010";
                break;
            default:
                break;
        }

        return result;
    }
}


Все работает. Но у меня есть такое ощущение, что можно как то это упростить. Я не
работал ни разу с байтами / битами. Возможно часть кода можно заменить какими то стандартными
функциями. Очень было бы поучительно.
    


Ответы

Ответ 1



Например, функцию CutZero можно представить на LINQ так: IEnumerable CutZero(IEnumerable in_array) { return in_array.Reverse().SkipWhile(b => b == 0x00); } Затем, код (byte)((byte)_dp + 0x400) выглядит странно: прибавление 0x400 не затрагивает младший байт, так что после преобразования к (byte) он всё равно отбросится. Затем, ConvertFromVal3 — это перевод в двоичное представление? Его можно легко заменить на встроенную функцию: Convert.ToString(length, 2).PadLeft(2, '0') Затем, совершенно неясно, что делают функции ConvertFromVal2 и ConvertFromVal1. Они не конвертируют в двоичное представление (т. к., например, двоичное представление для 4 будет никак не "111"), но возвращают явно двоичные строки. Затем, конвертация чисел в строку лишь для того, чтобы потом склеить и сконвертировать назад, выглядит как-то совсем странно. Если вы хотите склеить биты, применяйте битовые операции: private byte ConvertFromVal3(int length) { if (length < 0 || length > 3) throw new ArgumentException(...); return (byte)length; } private byte ConvertFromVal2(int length) { switch (length) { case 0: return (byte)0; case 1: return (byte)1; case 2: return (byte)2; case 4: return (byte)3; default: throw new ArgumentException(...); } } private byte ConvertFromVal1(int length) { switch (length) { case 1: return (byte)1; case 2: return (byte)6; case 4: return (byte)7; case 8: return (byte)2; default: throw new ArgumentException(...); } } // ... // 1. Количество байт. Сразу пишем в двоичном виде byte lengths = 0; // 1.1. Для Val1 lengths |= (ConvertFromVal1(vol.Length) << 4); // 1.2. Для Val2 lengths |= (ConvertFromVal2(dp.Length) << 2); // 1.3. Для Val3 lengths |= ConvertFromVal3(dt.Length); // Пишем маску _bw.Write(lengths);

Ответ 2



Если под упростить подразумевается уменьшение количества строк кода, то можно в ConnectButton_Click вместо многократных bw.Write(...) значения поместить в массив и вывести их в цикле. А ConvertFromVal3, например, определить так: private string ConvertFromVal3(int length) { var arr = new[] { "00", "01", "10", "11" }; return length < arr.Length ? arr[length] : ""; }

Комментариев нет:

Отправить комментарий