位模式匹配和替换

时间:2010-08-07 06:59:46

标签: c++ c bit-manipulation

我遇到了一个棘手的位操作问题。

据我所知,保存值的最小可变大小是8位的一个字节。 C / C ++中可用的位操作适用于整个字节单元。

想象一下,我有一张地图用信号10000(5位)替换二进制模式100100(6位)。如果来自文件的输入数据的第一个字节是10010001(8位)存储在char变量中,则它的一部分与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 ++更好地进行逐位操作。

8 个答案:

答案 0 :(得分:1)

  • << shiftleft
  • ^ XOR
  • >>向右移动
  • ~一个补充

使用这些操作,您可以轻松地隔离您感兴趣的部分并将它们作为整数进行比较。

说出字节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;
    }
}

这是非常粗略的代码,只是为了演示。

答案 1 :(得分:1)

如果时间和空间不重要,那么您可以将位转换为字符串表示并执行字符串替换,然后在需要时转换回来。 不是一个优雅的解决方案,但它有效。

答案 2 :(得分:1)

  

我想知道C / C ++是否能真正做到这一点   一种替代的比特模式   长度不适合Char(1   byte)变量或甚至Int(4字节)。

std :: bitset怎么样?

答案 3 :(得分:1)

这是一个可以满足您需求的小型读者课程。当然,您可能想为您的用例创建一个位编写器。

#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)
};

答案 4 :(得分:0)

如果您可以一次性将数据读入矢量,请使用vector<bool>。但是,查找和替换位序列可能更困难。

答案 5 :(得分:0)

如果我理解你的问题,你有一个输入流和输出流,你想要在输出中用输入5替换输入的6位 - 你的输出仍然应该是一个位流?

因此,最重要的程序员规则可以适用:Divide et impera! 您应该将组件分为三个部分:

  1. 输入流转换器:将输入流中的每个模式转换为char数组(环)缓冲区。如果我理解你的输入“命令”是8位长,那么没有什么特别的。

  2. 在环形缓冲区上进行替换,以每个匹配的6位模式替换为5位,但是用前导零“填充”5位,因此总长度仍为8位。

  3. 编写一个输出处理程序,它从环形缓冲区读取并让此输出处理程序只将7 LSB写入每个输入字节的输出流。当然,为此需要一些位操作。 如果您的环形缓冲区大小可以除以8和7(=是56的倍数),则最后会有一个干净的缓冲区,并且可以从1再次开始。

  4. 实现此目的最简单的方法是,只要输入数据可用,就迭代这3个步骤。

    如果性能真的很重要并且您在多核CPU上运行,您甚至可以拆分步骤和3个线程,但是您必须小心地同步对环形缓冲区的访问。

答案 6 :(得分:0)

我认为以下是您想要的。

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);
  }

}

您可以在中间使用开关或循环来检查候选人是否存在多种模式。在进行替换后可能需要进行一些特殊处理,以确保不会重新检查替换模式的匹配。

答案 7 :(得分:0)

#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;
}