[英]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 位 - 你的輸出仍然應該是位流?
因此,可以應用最重要的程序員規則:分而治之! 您應該將組件分成三部分:
輸入流轉換器:將輸入流中的每個模式轉換為字符數組(環)緩沖區。 如果我理解正確,您的輸入“命令”是 8 位長,所以這沒有什么特別之處。
在環形緩沖區上進行替換,將每個匹配的 6 位模式替換為 5 位模式,但用前導零“填充” 5 位,因此總長度仍為 8 位。
編寫一個從環形緩沖區讀取的輸出處理程序,並讓該輸出處理程序僅將每個輸入字節的 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.