簡體   English   中英

位模式匹配和替換

[英]bit pattern matching and replacing

我遇到了一個非常棘手的位操作問題。

據我所知,保存值的最小變量大小是 8 位的一個字節。 C/C++ 中可用的位操作適用於整個字節單元。

想象一下,我有一個映射來用信號 10000(5 位)替換二進制模式 100100(6 位)。 如果來自文件的輸入數據的第 1 個字節是 10010001(8 位)被存儲在一個字符變量中,它的一部分與 6 位模式匹配,因此被 5 位信號替換以給出 1000001(7 位)的結果.

我可以使用掩碼來操作一個字節中的位,以獲得最左邊的位為 10000(5 位)的結果,但最右邊的 3 位變得非常難以操作。 我無法移動原始數據的最右邊 3 位以獲得正確的結果 1000001(7 位),然后是該 char 變量中的 1 個填充位,該填充位應由輸入的下一個后續字節的第 1 位填充。

我想知道 C/C++ 是否真的可以對不適合 Char(1 字節)變量甚至 Int(4 字節)的長度位模式進行這種替換。 C/C++ 可以解決這個問題,還是我們必須使用其他處理單個位操作的匯編語言?

我聽說 Power Basic 可能比 C/C++ 能夠更好地進行逐位操作。

  • <<左移
  • ^異或
  • >>右移
  • ~一個人的補充

使用這些操作,您可以輕松地隔離您感興趣的部分並將它們作為整數進行比較。

說字節001000100並且您想檢查它是否包含1000

char k = (char)68;
char c = (char)8;
int i = 0;
while(i<5){
    if((k<<i)>>(8-3-i) == c){
        //do stuff
        break;
    }
}

這是非常粗略的代碼,只是為了演示。

如果時間和空間不重要,那么您可以將位轉換為字符串表示並對字符串執行替換,然后在需要時轉換回來。 不是一個優雅的解決方案,而是一個有效的解決方案。

我想知道 C/C++ 是否真的可以對不適合 Char(1 字節)變量甚至 Int(4 字節)的長度位模式進行這種替換。

std::bitset 呢?

這是一個可能適合您需求的小讀者類。 當然,您可能希望為您的用例創建一個位編寫器。

#include <iostream>
#include <sstream>
#include <cassert>

class BitReader {
    public:
        typedef unsigned char BitBuffer;

        BitReader(std::istream &input) :
            input(input), bufferedBits(8) {
        }

        BitBuffer peekBits(int numBits) {
            assert(numBits <= 8);
            assert(numBits > 0);

            skipBits(0);    // Make sure we have a non-empty buffer

            return (((input.peek() << 8) | buffer) >> bufferedBits) & ((1 << numBits) - 1);
        }

        void skipBits(int numBits) {
            assert(numBits >= 0);

            numBits += bufferedBits;

            while (numBits > 8) {
                buffer = input.get();
                numBits -= 8;
            }

            bufferedBits = numBits;
        }

        BitBuffer readBits(int numBits) {
            assert(numBits <= 8);
            assert(numBits > 0);

            BitBuffer ret = peekBits(numBits);

            skipBits(numBits);

            return ret;
        }

        bool eof() const {
            return input.eof();
        }

    private:
        std::istream &input;
        BitBuffer buffer;
        int bufferedBits;   // How many bits are buffered into 'buffer' (0 = empty)
};

如果您可以一次將數據讀入向量,請使用vector<bool> 但是,查找和替換位序列可能更加困難。

如果我正確理解你的問題,你有一個輸入流和輸出流,你想用輸出中的 5 替換輸入的 6 位 - 你的輸出仍然應該是位流?

因此,可以應用最重要的程序員規則:分而治之! 您應該將組件分成三部分:

  1. 輸入流轉換器:將輸入流中的每個模式轉換為字符數組(環)緩沖區。 如果我理解正確,您的輸入“命令”是 8 位長,所以這沒有什么特別之處。

  2. 在環形緩沖區上進行替換,將每個匹配的 6 位模式替換為 5 位模式,但用前導零“填充” 5 位,因此總長度仍為 8 位。

  3. 編寫一個從環形緩沖區讀取的輸出處理程序,並讓該輸出處理程序僅將每個輸入字節的 7 LSB 寫入輸出流。 當然,為此再次需要一些位操作。 如果您的環形緩沖區大小可以被 8 和 7 整除(= 是 56 的倍數),那么最后您將擁有一個干凈的緩沖區,並且可以從 1 重新開始。

實現這一點的最簡單方法是只要輸入數據可用,就迭代這 3 個步驟。

如果性能真的很重要並且您在多核 CPU 上運行,您甚至可以拆分步驟和 3 個線程,但是您必須小心地同步對環形緩沖區的訪問。

我認為以下做你想要的。

PATTERN_LEN = 6
PATTERNMASK = 0x3F //6 bits
PATTERN     = 0x24 //b100100
REPLACE_LEN = 5
REPLACEMENT = 0x10  //b10000


void compress(uint8* inbits, uint8* outbits, int len)
{
  uint16 accumulator=0;
  int nbits=0;
  uint8 candidate; 

  while (len--) //for all input bytes
  {
    //for each bit (msb first)
    for (i=7;i<=0;i--)
    {
      //add 1 bit to accumulator
      accumulator<<=1;
      accumulator|=(*inbits&(1<<i));  
      nbits++;
      //check for pattern
      candidate = accumulator&PATTERNMASK;
      if (candidate==PATTERN)
      {
        //remove pattern
        accumulator>>=PATTERN_LEN; 
        //add replacement
        accumulator<<=REPLACE_LEN; 
        accumulator|=REPLACMENT;
        nbits+= (REPLACE_LEN - PATTERN_LEN);
      }
    }
    inbits++;
    //move accumulator to output to prevent overflow
    while (nbits>8)
    {
      //copy the highest 8 bits
      nbits-=8;      
      *outbits++ = (accumulator>>nbits)&0xFF;
      //clear them from accumulator
      accumulator&= ~(0xFF<<nbits);
    }
  }
  //copy remainder of accumulator  to output
  while (nbits>0)
  {
    nbits-=8;
    *outbits++ = (accumulator>>nbits)&0xFF;
    accumulator&= ~(0xFF<<nbits);
  }

}

您可以在中間使用開關或循環來根據多個模式檢查候選對象。 進行替換后可能需要進行一些特殊處理,以確保不會重新檢查替換模式是否匹配。

#include <iostream>
#include <cstring>

size_t matchCount(const char* str, size_t size, char pat, size_t bsize) noexcept 
{
  if (bsize > 8) {
    return 0;
  }
  size_t bcount = 0; // curr bit number
  size_t pcount = 0; // curr bit in pattern char
  size_t totalm = 0; // total number of patterns matched
  const size_t limit = size*8;
  while (bcount < limit)
  {
    auto offset = bcount%8;
    char c = str[bcount/8];
    c >>= offset;
    char tpat = pat >> pcount;
    if ((c & 1) == (tpat & 1))
    {
      ++pcount;
      if (pcount == bsize)
      {
        ++totalm;
        pcount = 0;
      }
    }
    else // mismatch
    {
      bcount -= pcount; // backtrack
      //reset
      pcount = 0;
    }
    ++bcount;
  }
  return totalm;
}

int main(int argc, char** argv)
{
  const char* str = "abcdefghiibcdiixyz";
  char pat = 'i';
  std::cout << "Num matches = " << matchCount(str, 18, pat, 7) << std::endl;
  return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM