C# Создание целочисленного значения с помощью двоичных операций из битов

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

byte[] buf = new byte[] { 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

Двоичное представление: 1111 1110 (0xFF = 0, 0xF0= 1).

Значение целого числа или байта, которое необходимо построить из этого, равно 0xFE (254).

Я думал о преобразовании buf в строку 1s и 0s, а затем обратно в целое число, но должны существовать более элегантные способы сделать это с помощью двоичных операций.

Код Pascal, который, как известно, делает это идеально (используя строку):

  if NumberOfBytes > 0  then begin    // there was a response
     for c := Length(Str) downto 1 do begin
        Ch := Str[c];
        if (ord(ch) AND 2) = 0 then begin
           Sin := Sin + '0';
        end else begin
           Sin := Sin + '1';
        end;
        INC(Teller);
        if (Teller mod 8) = 0 then begin
            N := BinaryStrToByte(Sin);
            Edit2.Text := Edit2.Text + ByteHex(N) + ' ' ;
            Sin := '';
        end;
     end;

Также код C, который, похоже, не переносится на C# из-за некоторых различий в поведении:

for(bytes = 0; bytes < len; bytes++)
 {
   newByte=0;
   for(bits=0; bits<8; bits++)
   {
     newByte >>= 1;
    if(inBuf[0]==0xff) 
    {
      newByte |= 0x80;
    }
   }
   pBuf[bytes]=newByte;
 }

Я хочу эффективно перебрать свой исходный массив, а затем поместить настоящий двоичный файл 0 или 1 в int или byte, в зависимости от того, есть ли у меня значение 0xF0 или 0xFF в текущей позиции.


person NxtLevel    schedule 30.05.2015    source источник
comment
Взгляните на BitConverter. -класс.   -  person khlr    schedule 30.05.2015
comment
Ухх -- вы сказали Двоичное представление равно 1111 1110 (FF = 0, F0= 1), что ОЧЕНЬ сбивает с толку. Вы хотели сказать что-то вроде: Двоичное представление: 1111 1111 1111 1110 (где первый и второй байты поменялись местами, так что значение x'FF' равно position = 0, а x'F0' равно position = 1) ???   -  person David Tansey    schedule 30.05.2015
comment
Вы просите людей написать код для вас? Какой у вас здесь вопрос?   -  person Eric Lippert    schedule 30.05.2015
comment
Да, это звучит запутанно, но аппаратное обеспечение, с которым я общаюсь, возвращает мне двоичные данные через последовательный порт, FF означает 0, а F0 означает 1, я должен сначала преобразовать их в реальные двоичные значения, а затем я могу использовать BitConverter чтобы разобрать...   -  person NxtLevel    schedule 30.05.2015
comment
@EricLippert, мой вопрос: я хочу эффективно перебрать исходный массив, а затем поместить реальный двоичный 0 или 1 в целое число или байт, в зависимости от того, есть ли у меня значение F0 или FF в текущей позиции.   -  person NxtLevel    schedule 30.05.2015
comment
@NxtLevel Вы имеете в виду 0xFF = 1 и 0xF0 = 0? В противном случае ваш вопрос либо не имеет смысла, либо очень запутан.   -  person Will Vousden    schedule 30.05.2015
comment
FF — это ноль, а F0 — это единица, так устройство, с которым я общаюсь, тянет линию связи, так как rx и tx закорочены на коммуникациях 1-wire, поэтому, если я получу эхо F0, я знаю, что оно пытается отправить меня один   -  person NxtLevel    schedule 30.05.2015


Ответы (3)


Это может помочь:

public static void Main()
{
    byte[] buf = new byte[] { 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    byte result = 0;
    foreach(var b in buf.Reverse())
    {
        result <<= 1;
        if(b == 0xFF) result |= 1;
    }
    Console.WriteLine(result);
}
person venerik    schedule 30.05.2015
comment
Возможно, вы можете заменить result = (byte)(result << 1); на result <<= 1;, это сэкономит преобразование и сделает его более читабельным. - person Willem Van Onsem; 30.05.2015
comment
Это работает на 100%, я до сих пор не могу понять, почему пример C не хочет выполнять свою работу в прямом переносе на C#. Очень чистое решение :) - person NxtLevel; 30.05.2015
comment
@NxtLevel: проблема, вероятно, inBuf[0], здесь вы всегда берете первый элемент. Вы уверены, что код C, например, не увеличивает указатель? - person Willem Van Onsem; 30.05.2015

Подход с минимальным количеством ветвлений (и вызовов LINQ):

byte result = 0x00;
foreach(byte b in buf) {
    result >>= 0x01;
    byte bs = b;
    bs <<= 0x07;
    result |= bs;
}

Этот код делает предположение, что младший значащий бит каждого байта определяет, следует ли добавить 0 (в случае 0xf0=1111 0000) или 1 (в случае 0xff=1111 1111).

Демонстрация в интерактивной оболочке Mono C#:

csharp> byte[] buf = new byte[] { 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
csharp> byte result = 0x00;
csharp> foreach(byte b in buf) {
      >     result >>= 0x01;
      >     byte bs = b;
      >     bs <<= 0x07;
      >     result |= bs;
      > }
csharp> result.ToString("X");
"FE"
person Willem Van Onsem    schedule 30.05.2015

Вот немного более общее решение, похожее на LINQ:

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

public static IEnumerable<Boolean> BytesToBooleans(IEnumerable<Byte> bytes)
{
    if (bytes == null)
        throw new ArgumentNullException("bytes");

    return bytes
        .Select(b =>
            {
                if (b == 0xF0)
                    return false;
                if (b == 0xFF)
                    return true;
                throw new ArgumentException();
            });
}

Метод, который интерпретирует логические значения как биты, начиная с самого младшего:

public static IEnumerable<Byte> BooleansAsBitsFromLowest(this IEnumerable<Boolean> bits)
{
    if (bits == null)
        throw new ArgumentNullException("bits");

    Int32 bitsInByte = 0;
    Byte curByte = 0;

    foreach (var bit in bits)
    {
        var mask = (Byte)(bit ?
            0x1 : 0x0);

        mask = (Byte)(mask << bitsInByte);

        curByte |= mask;
        bitsInByte++;

        if (bitsInByte == 8)
        {
            yield return curByte;
            curByte = 0;
            bitsInByte = 0;
        }
    }

    if (bitsInByte != 0)
        yield return curByte;
}

И вот использование:

byte[] buf = new byte[] { 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

var bytesFromBits = BytesToBooleans(buf)
    .BooleansAsBitsFromLowest();

foreach (var resultingByte in bytesFromBits)
{
    Console.WriteLine(resultingByte.ToString("X2"));
}
person Eugene Podskal    schedule 30.05.2015