2016-06-15 4 views
4

У меня есть большой кусок данных (hexdump), который включает в себя тысячи небольших изображений, а структура данных - вот что.Отдельные данные в текстовом файле

20 00 20 00 00 10 00 00 <data> 20 00 20 00 00 10 00 00 <data> ... 

Где (20 00 20 00 00 10 00 00) является разделение между каждой секцией данных (изображений).

Файл myfile включая все шестнадцатеричные выглядит что-то вроде этого

3C 63 9E FF 38 5F 9E FF 
31 59 91 FF 20 00 20 00 
00 10 00 00 55 73 A2 FF 
38 5D 9C FF 3A 5E 95 FF 

То, что я хочу сделать, это в основном отделить его. Я хочу взять часть, разделенную 20 00 20 00 00 10 00 00, и поместить каждую часть в файл txt в виде 1.txt, 2.txt ... n.txt

Я пробовал читать строки, но это вызывает некоторые проблемы, потому что часть 20 00 .. может быть найдена в 2 строках в некоторых случаях, как в приведенном выше примере, поэтому она не будет обнаруживать каждое происхождение.

while (getline(myfile,line,'\n')){ 
    if (line == "20 00 20 00 00 10 00 00") 
     ... 
} 
+0

Таким образом, фактическое содержимое файла представляет собой шестнадцатеричные числа в текстовой форме? Это не двоичный файл? –

+0

Правильно, содержимое находится в текстовом файле. Я думал, что с ней будет легче работать, поэтому я выкинул ее в текстовый файл. У меня есть доступ к двоичному файлу, если это лучше. – Michael

+0

Кроме того, похоже, что записи не обязательно разделяются линиями, что означает, что вы не можете использовать построчное чтение. И даже если в правильном месте есть разрывы строк, вы не можете использовать сравнение '==', так как строки содержат больше, чем разделитель, и сравнение двух строк с использованием '==' ищет точное соответствие *. Объявление, если файл действительно не текстовый, а все двоичные данные, вы не можете использовать 'std :: getline' и сравнение строк вообще. –

ответ

1

Определенно сохраняйте файл в двоичном и дампном действительном шестнадцатеричном байтах, в отличие от текстовой формы. Вы сэкономите 3 раза больше места, и для чтения файлов проще писать.

Это, как говорится, если ваш файл в двоичном формате, это решение:

#include <fstream> 

using std::ifstream; 
using std::ofstream; 
using std::string; 

void incrementFilename(char* filename) { 
    int iFile; 
    sscanf(filename, "%d.dat", &iFile); 
    sprintf(filename, "%d.dat", ++iFile); 
} 

int main() { 
    char outputFilename[16] = "1.dat"; 
    ifstream input("myfile.dat", ifstream::binary); 
    ofstream output(outputFilename, ofstream::binary); 

    while (!input.eof() || !input.is_open()) { 
    char readbyte; 
    input.read(&readbyte, 1); 

    if (readbyte == 0x20) { 
     char remaining[7]; 
     char testcase[7] = { 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00 }; 
     input.read(remaining, 7); 
     if (strncmp(remaining, testcase, 7) == 0) { 
     incrementFilename(outputFilename); 
     output.close(); 
     output.open(outputFilename, ofstream::binary); 
     } else { 
     output.write(&readbyte, 1); 
     output.write(remaining, 7); 
     } 
    } else { 
     output.write(&readbyte, 1); 
    } 
    } 

    return 0; 
} 
2

Мое предложение состоит в том, чтобы прочитать двоичный файл. Если он достаточно мал, вы можете прочитать все это в памяти за один раз, иначе я предлагаю вам использовать операционную систему map the file into memory (или хотя бы «окно»).

Тогда довольно легко найти 8-байтовую последовательность, разделяющую записи. Сначала просто найдите 0x20, и всякий раз, когда это будет найдено, вы увидите, является ли это началом всей последовательности разделителей.

Когда вы находите последовательность разделителей, вы берете сохраненную позицию предыдущего разделителя и позицию вновь найденного разделителя, а данные между ними - данные, которые вы хотите. Сохраните позицию вновь найденного разделителя как старую позицию и продолжите поиск следующего разделителя.

0

Учитывая, что фактическая последовательность данных, которую вы используете, потенциально разделена по линиям, вам необходимо прочитать данные в самом маленьком «укусе», который вы можете - двухсимвольные массивы, и игнорировать пробелы (пробелы или символы новой строки) ,

Как только вы сделаете это, вы сможете отслеживать, что вы читали, когда пишете его в свой файл. Когда вы получите свою «магическую последовательность», запустите новый подфайл.

Две сложности, которые не охватывают:

  1. ли «волшебная последовательность» вообще можно существовать в файле как часть нормальных данных? Если это так, вы собираетесь разделить один-единственный файл.
  2. Я предполагаю, что вы не хотите «волшебную последовательность» в конце каждого подфайла. Это добавит немного сложности для сравнения:
    • Если вы начинаете матч, вам нужно приостановить запись в подфайл.
    • Если вы на полпути и внезапно прекратите сопоставление, вам придется выписать частичное совпадение, прежде чем записывать новую несогласованную запись.

Одно из преимуществ делать это таким образом: если суб-файл, в то же время в главном файле, началась в конце строки, то он начнет на новой линии и перерыв после 16 двух -характеры, а не имитировать свою позицию в основном файле.Или вы хотите, чтобы субфайлы выводились в истинных байтах без разделителей пробелов?

Я собираюсь уйти и написать эту программу: это звучит весело!


ОК, я написал следующее. Надеюсь, что использование описывает, что делать. Я не особенно хочу использовать потоки - Я нахожу их ужасно неэффективен - но вы начали его ...

// 
// SubFile.cpp 
// 

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

using namespace std; 

const unsigned MaxBytesPerLine = 16; 

const unsigned char magic[] = { '\x20','\x00','\x20','\x00','\x00','\x10','\x00','\x00' }; 

class OutFile : private ofstream { 
public: // Methods 
    using ofstream::is_open; // Let others see whether I'm open 
    OutFile(const string &fileName, bool bin); 
    bool Write(unsigned b); 
    ~OutFile(); 
private: // Variables 
    unsigned num; // Number bytes in line 
    bool bin; // Whether to output binary 
}; // OutFile 

OutFile::OutFile(const string &filename, bool bin) : 
     ofstream(filename), 
     num(0), 
     bin(bin) { 
    if (!bin) { 
     setf(uppercase); 
    } // if 
} // OutFile::OutFile(name, bin) 

bool OutFile::Write(unsigned b) { 
    if (bin) { 
     char c = (char)b; // Endian fix! 
     return write(&c, 1).good(); 
    } // if 
    if (num > 0) { 
     *this << " "; 
    } // if 
    *this << setbase(16) << setw(2) << setfill('0') << b; 
    if (++num == MaxBytesPerLine) { 
     *this << endl; 
     num = 0; 
    } // if 
    return good(); 
} // OutFile::Write(b) 

OutFile::~OutFile() { 
    if (bin) { 
     return; 
    } // if 
    if (num == 0) { 
     return; 
    } // if 
    if (!good()) { 
     return; 
    } // if 
    *this << endl; 
} // OutFile::~OutFile 

void Usage(char *argv0) { 
    cout << "Usage:" << endl; 
    cout << "  " << argv0 << " <filename.txt> [bin]" << endl; 
    cout << " Read <filename.txt> in hex char pairs, ignoring whitespace." << endl; 
    cout << " Write pairs out to multiple sub-files, called \"1.txt\", \"2.txt\" etc." << endl; 
    cout << " New files are started when the following sequence is detected: " << endl << " "; 
    for (unsigned i = 0; i < sizeof(magic); ++i) { 
     cout << ' ' << hex << setw(2) << setfill('0') << (int)magic[i]; 
    } // for 
    cout << endl; 
    cout << " If bin is specified: write out in binary, and files have a '.bin' extension" << endl; 
} // Usage(argv0) 

int main(int argc, char *argv[]) { 
    if (argc < 2) { 
     Usage(argv[0]); 
     return 1; 
    } // if 
    ifstream inFile(argv[1]); 
    if (!inFile.is_open()) { 
     cerr << "Could not open '" << argv[1] << "'!" << endl; 
     Usage(argv[0]); 
     return 2; 
    } // if 

    bool bin = (argc >= 3) && 
       (argv[2][0] == 'b'); // Close enough! 
    unsigned fileNum = 0; // Current output file number 

    inFile >> setbase(16); // All inFile accesses will be like this 
    while (inFile.good()) { // Let's get started! 
     string outFileName = to_string(++fileNum) + (bin ? ".bin" : ".txt"); 
     OutFile outFile(outFileName, bin); 
     if (!outFile.is_open()) { 
      cerr << "Could not create " << outFileName << "!" << endl; 
      return (int)(fileNum + 2); 
     } // if 

     unsigned b; // byte read in 
     unsigned pos = 0; // Position in 'magic' 
     while (inFile >> b) { 
      if (b > 0xFF) { 
       cerr << argv[1] << " contains illegal value: " 
        << hex << uppercase << showbase << b << endl; 
       return -1; 
      } // if 
      if (b == magic[pos]) {   // Found some magic! 
       if (++pos == sizeof(magic)) { // ALL the magic? 
        break;     // Leave! 
       } // if 
       continue;      // Otherwise go back for more 
      } // if 
      if (pos > 0) {     // Uh oh. No more magic! 
       for (unsigned i = 0; i < pos; ++i) { 
        outFile.Write(magic[i]); // So write out what we got 
       } // for 
       pos = 0; 
      } // if 
      outFile.Write(b); 
     } // while 
    } // for 
    if (inFile.eof()) { 
     return 0; // Success! 
    } // if 

    string s; 
    inFile.clear(); 
    getline(inFile, s); 
    cerr << argv[1] << " contains invalid data: " << s << endl; 
    return -2; 
} // main(argc,argv) 

Всякий раз, когда кто-то код сообщения, там неизменно комментариев:
«Почему не сделал ты делаешь это?"
«Зачем ты это сделал?»
Пусть открываются шлюзы!

0

Вот мое решение. Это немного неэффективно, но я могу переписать его, как только закончу. Я предполагаю, что есть байты данных, разделенных пробелом. Проблема довольно проста тогда -> это просто проблема соответствия шаблонов. Я мог бы использовать некоторые сложные методы для этого, но наш шаблон имеет размер исправления, который довольно мал. Даже метод грубой силы будет иметь линейное время.

Код сам пояснительный. Я читаю байт по байтам и добавляю его в буфер (не слишком эффективно, может хранить только окно данных с границами индексов в файле -> это может сделать возможным более эффективные операции ввода-вывода при создании новых файлов). Как только завершающая последовательность будет найдена, мы поместим ее и сохраним в файл (я сделал предположение, что мы не хотим пустые файлы).

void save(const std::vector<short>& bytes, std::string filename, int sequenceLength) 
{ 
    if (!bytes.size()) return; // Don't want empty files 

    std::ofstream outputFile(filename); 
    int i = 0; 
    for (short byte : bytes) 
    { 
     outputFile << std::uppercase << std::hex << byte; 

     i = (i + 1) % sequenceLength; 
     if (i) outputFile << " "; 
     else outputFile << std::endl; 
    } 
} 

std::string getFilename(int number) 
{ 
    std::stringstream ss; 
    ss << number << ".txt"; 
    return ss.str(); 
} 

short getIntFromHex(const char* buffer) 
{ 
    short result; 
    std::stringstream ss; 
    ss << std::hex << buffer; 
    ss >> result; 
    return result; 
} 

bool findTerminatingSequence(const std::vector<short>& bytes, short terminatingSequence[], int sequenceLength) 
{ 
    int i = 0; 
    int startIndex = bytes.size() - sequenceLength; 
    for (i; i < sequenceLength; i++) 
     if (terminatingSequence[i] != bytes[startIndex + i]) 
      break; 
    return i == sequenceLength; 
} 

void popSequence(std::vector<short>& bytes, int sequenceLength) 
{ 
    for (int j = 0; j < sequenceLength; j++) 
     bytes.pop_back(); 
} 

int main() 
{ 
    std::vector<short> bytes; 
    std::ifstream inputFile("input.txt"); 
    int outputFileIndex = 1; 
    int sequenceLength = 8; 
    short terminatingSequence[] = { 0x20, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00 }; 
    short nextByte; 
    char buffer[3]; 

    while (inputFile >> buffer) 
    { 
     nextByte = getIntFromHex(buffer); 
     bytes.push_back(nextByte); 
     if (bytes.size() < sequenceLength || 
      !findTerminatingSequence(bytes, terminatingSequence, sequenceLength)) 
      continue; 

     popSequence(bytes, sequenceLength); 
     save(bytes, getFilename(outputFileIndex++), sequenceLength); 
     bytes.clear(); 
    } 

    save(bytes, getFilename(outputFileIndex), sequenceLength); 

    return 0; 
} 
0

Я бы с Perl вдоль этих линий:

#!/usr/bin/perl 
use warnings; 
use strict; 

# Slurp entire file from stdin into variable $data 
my $data = <>; 

# Find offsets of all occurrences of marker in file 
my @matches; 
my $marker='\x20\x00\x20\x00\x00\x10\x00\x00'; 
while ($data =~ /($marker)/gi){ 
    # Save offset of this match - you may want to add length($marker) here to avoid including marker in output file 
    push @matches, $-[0]; 
} 

# Extract data between pairs of markers and write to file 
for(my $i=0;$i<scalar @matches -1;$i++){ 
    my $image=substr $data, $matches[$i], $matches[$i+1] - $matches[$i]; 
    my $filename=sprintf("file-%05d",$i); 
    printf("Saving match at offset %d to file %s\n",$matches[$i],$filename); 
    open(MYFILE,">$filename"); 
    print MYFILE $image; 
    close(MYFILE); 
} 

Выходные

Saving match at offset 12 to file file-00000 
Saving match at offset 44 to file file-00001 

Run так:

./perlscript < binaryData 

Я использую более или менее точно T его техника для восстановления поврежденных карт флэш-памяти с камер. Вы просто просматриваете всю флеш-карту для некоторых байтов, которые выглядят как начало файла JPEG/raw и захватывают следующие 10-12MB и сохраняют его как файл.

0

Ваша проблема может быть решена путем реализации простого finite state machine, так как вы не длинное условие. Вы будете читать шестнадцатеричные значения, разделенные пробелами, и проверять значения один за другим, если они соответствуют вашим критериям. Если он соответствует, создайте новый поток продолжения файла, если вы не пишете, вы прочитали текущий файл. Вот решение, часть чтения может быть оптимизирована путем изменения цикла.

(предположительно ввод имя файла как ввод.TXT)

#include <fstream> 
#include <sstream> 

using namespace std; 

void writeChunk(ostream& output, int value) { 
    if (value == 0) 
     output << "00" << " "; 
    else 
     output << hex << value << " "; 
} 

bool readNext(fstream& input, int& value, stringstream* keep = NULL) { 
    if (input.eof()) { 
     return false; 
    } else { 
     input >> hex >> value; 
     if (keep != NULL) 
      writeChunk(*keep, value); 
     return true; 
    } 
} 

string getFileName(int count) { 
    stringstream fileName; 
    fileName << count << ".txt"; 
    return fileName.str(); 
} 

int main() { 
    int fileCount = 1; 
    stringstream fileName; 
    fstream inputFile, outputFile; 

    inputFile.open("input.txt"); 
    outputFile.open(getFileName(fileCount), ios::out); 

    int hexValue; 
    while (readNext(inputFile, hexValue)) { 
     // It won't understand eof until an unsuccessful read, so double checking 
     if (inputFile.eof()) 
      break; 

     if (hexValue == 0x20) { 
      stringstream ifFails; 
      ifFails << "20 "; 
      if (readNext(inputFile, hexValue, &ifFails) && hexValue == 0x00 && 
        readNext(inputFile, hexValue, &ifFails) && hexValue == 0x20 && 
        readNext(inputFile, hexValue, &ifFails) && hexValue == 0x00 && 
        readNext(inputFile, hexValue, &ifFails) && hexValue == 0x00 && 
        readNext(inputFile, hexValue, &ifFails) && hexValue == 0x10 && 
        readNext(inputFile, hexValue, &ifFails) && hexValue == 0x00 && 
        readNext(inputFile, hexValue, &ifFails) && hexValue == 0x00) { 
       outputFile.close(); 
       outputFile.open(getFileName(++fileCount), ios::out); 
       continue; 
      } 
      outputFile << ifFails.str(); 
     } else { 
      writeChunk(outputFile, hexValue); 
     } 
    } 

    return 1; 
} 
0

Вы можете также использовать токенизатор для этого: Сначала прочитайте «MYFILE» в строку. Это необходимо, потому что в файле вы можете иметь только вперед итератор, но регулярное выражение нужно двунаправленным один:

auto const& str(dynamic_cast<ostringstream&> (ostringstream().operator<<(ifstream("myfile").rdbuf())).str()); 

Тогда вам нужен шаблон, чтобы разделить с extended на «» матчи также новая строка:

auto const& re(regex(".?20.00.20.00.00.10.00.00.?", regex_constants::extended)); 

И, наконец, перебрать лексемы строки и записать его в файл 0.txt и так далее.

auto i(0u); 
for_each(sregex_token_iterator(str.cbegin(), str.cend(), re, -1), 
     sregex_token_iterator(), 
     [&i] (string const& s) {ofstream(to_string(i++) + ".txt") << s; }); 

Пожалуйста, обратите внимание, что выходные файлы не полностью отформатированный, они похожи на 1.txt:

55 73 A2 FF 
38 5D 9C FF 3A 5E 95 FF 

Это просто содержимое без разделителей.

Смежные вопросы