3

(非テキスト) ファイルでバイト シーケンス "9µ}Æ" (または "\x39\xb5\x7d\xc6") を検索する必要があります。

オンラインで5時間検索した後、これが私ができる最善の方法です. それはうまくいきますが、もっと良い方法があるかどうか知りたいです:

char buffer;

int pos=in.tellg();

// search file for string
while(!in.eof()){
    in.read(&buffer, 1);
    pos=in.tellg();
    if(buffer=='9'){
        in.read(&buffer, 1);
        pos=in.tellg();
        if(buffer=='µ'){
            in.read(&buffer, 1);
            pos=in.tellg();
            if(buffer=='}'){
                in.read(&buffer, 1);
                pos=in.tellg();
                if(buffer=='Æ'){
                    cout << "found";
                }
            }
        }
    }

    in.seekg((streampos) pos);

ノート:

  • 使えませんgetline()。テキストファイルではないので、改行が少ないかもしれません。
  • 複数文字のバッファーを使用してから、バッファーを C++ 文字列にコピーしてから、string::find(). ファイル全体に多くの'\0'文字があるため、これは機能しませんでした。そのため、文字列にコピーされると、バッファ内のシーケンスが非常に短くなります。
4

5 に答える 5

5

bames53が投稿したものと同様です。ベクトルをバッファーとして使用しました。

std::ifstream ifs("file.bin");

ifs.seekg(0, std::ios::end);
std::streamsize f_size = ifs.tellg();
ifs.seekg(0, std::ios::beg);

std::vector<unsigned char> buffer(f_size);
ifs.read(buffer.data(), f_size);

std::vector<unsigned char> seq = {0x39, 0xb5, 0x7d, 0xc6};

bool found = std::search(buffer.begin(), buffer.end(), seq.begin(), seq.end()) != buffer.end();
于 2012-07-04T20:14:55.943 に答える
0
const char delims[] = { 0x39, 0xb5, 0x7d, 0xc6 };
char buffer[4];
const size_t delim_size = 4;
const size_t last_index = delim_size - 1;

for ( size_t i = 0; i < last_index; ++i )
{
  if ( ! ( is.get( buffer[i] ) ) )
    return false; // stream to short
}

while ( is.get(buffer[last_index]) )
{
  if ( memcmp( buffer, delims, delim_size ) == 0 )
    break; // you are arrived
  memmove( buffer, buffer + 1, last_index );
}

あなたは4バイトを探しています:

unsigned int delim = 0xc67db539;
unsigned int uibuffer;
char * buffer = reinterpret_cast<char *>(&uibuffer);

for ( size_t i = 0; i < 3; ++i )
{
  if ( ! ( is.get( buffer[i] ) ) )
    return false; // stream to short
}

while ( is.get(buffer[3]) )
{
  if ( uibuffer == delim )
    break; // you are arrived
  uibuffer >>= 8;
}
于 2012-07-04T20:14:36.803 に答える
0

ファイル全体をインメモリ配列にロードする (またはファイルがメモリ内にあるように見せるために mmap() を使用する) ことを気にしない場合は、メモリ内で文字シーケンスを検索できます。やりやすい:

// Works much like strstr(), except it looks for a binary sub-sequence rather than a string sub-sequence
const char * MemMem(const char * lookIn, int numLookInBytes, const char * lookFor, int numLookForBytes)
{
        if (numLookForBytes == 0)              return lookIn;  // hmm, existential questions here
   else if (numLookForBytes == numLookInBytes) return (memcmp(lookIn, lookFor, numLookInBytes) == 0) ? lookIn : NULL;
   else if (numLookForBytes < numLookInBytes)
   {
      const char * startedAt = lookIn;
      int matchCount = 0;
      for (int i=0; i<numLookInBytes; i++)
      {
         if (lookIn[i] == lookFor[matchCount])
         {
            if (matchCount == 0) startedAt = &lookIn[i];
            if (++matchCount == numLookForBytes) return startedAt;
         }
         else matchCount = 0;
      }
   }
   return NULL;
}

.... 次に、メモリ内データ配列で上記の関数を呼び出すだけです。

char * ret = MemMem(theInMemoryArrayContainingFilesBytes, numBytesInFile, myShortSequence, 4);
if (ret != NULL) printf("Found it at offset %i\n", ret-theInMemoryArrayContainingFilesBytes);
            else printf("It's not there.\n");
于 2012-07-04T19:56:19.140 に答える
0

このプログラムは、ファイル全体をメモリにロードしてから使用std::searchします。

int main() {
    std::string filedata;
    {
        std::ifstream fin("file.dat");
        std::stringstream ss;
        ss << fin.rdbuf();
        filedata = ss.str();
    }

    std::string key = "\x39\xb5\x7d\xc6";
    auto result = std::search(std::begin(filedata), std::end(filedata),
                              std::begin(key), std::end(key));
    if (std::end(filedata) != result) {
        std::cout << "found\n";
        // result is an iterator pointing at '\x39'
    }
}
于 2012-07-04T20:10:01.097 に答える
0

文字列に null ターミネータ文字が含まれているため、ファイル全体を検索できないと言ったので、ファイル全体を読み取り、再帰を使用してファイル全体内の文字列の最初の出現を見つける代替手段を次に示します。

    #include <iostream>
    #include <fstream>
    #include <string>

    using namespace std;

    string readFile (char *fileName) {
      ifstream fi (fileName);
      if (!fi)
        cerr << "ERROR: Cannot open file" << endl;
      else {
        string str ((istreambuf_iterator<char>(fi)), istreambuf_iterator<char>());
        return str;
      }
      return NULL;
    }

    bool findFirstOccurrenceOf_r (string haystack, char *needle, int haystack_pos, int needle_pos, int needle_len) {
      if (needle_pos == needle_len)
        return true;
      if (haystack[haystack_pos] == needle[needle_pos]) 
        return findFirstOccurrenceOf_r (haystack, needle, haystack_pos+1, needle_pos+1, needle_len);
      return false;
    }

    int findFirstOccurrenceOf (string haystack, char *needle, int length) {
      int pos = -1;
      for (int i = 0; i < haystack.length() - length; i++) {
        if (findFirstOccurrenceOf_r (haystack, needle, i, 0, length))
          return i;
      }
      return pos;
    }

    int main () {
      char str_to_find[4] = {0x39, 0xB5, 0x7D, 0xC6};
      string contents = readFile ("input");

      int pos = findFirstOccurrenceOf (contents, str_to_find, 4);

      cout << pos << endl;
    }

ファイルが大きすぎない場合、最善の解決策は、ファイル全体をメモリにロードすることです。これにより、ドライブから読み続ける必要がなくなります。ファイルが大きすぎて一度にロードできない場合は、一度にファイルのチャンクをロードする必要があります。ただし、チャックにロードする場合は、チャンクの端まで確認してください。検索している文字列の途中でチャンクが分割される可能性があります。

于 2012-07-04T20:29:57.497 に答える