2015-11-25 2 views
1

У меня есть тестовый файл, который выглядит следующим образом:Текстовый файл двоичного поиска

Ampersand   Gregorina   5465874526370945 
Anderson   Bob     4235838387422002 
Anderson   Petunia    4235473838457294 
Aphid    Bumbellina   8392489357392473 
Armstrong-Jones  Mike    8238742438632892 

И код, который выглядит следующим образом:

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

class CardSearch 
{ 
protected: 
    std::ifstream cardNumbers; 

public: 
    CardSearch(std::string fileName) 
    { 
     cardNumbers.open(fileName, std::ios::in); 

     if (!cardNumbers.is_open()) 
     { 
      std::cout << "Unable to open: " << fileName; 
     } 
     return; 
    } 

    std::string Find(std::string lastName, std::string firstName) 
    { 
     // Creating string variables to hold first and last name 
     // as well as card number. Also creating bools to decide whether 
     // or not the person has been found or if the last name is the only 
     // identifier for a found person 
     std::string lN; 
     std::string fN; 
     std::string creditNumber; 
     bool foundPerson = false; 

     // By using the seekg and tellg functions, we can find our place 
     // in the file and also calculate the amount of lines within the file 
     cardNumbers.seekg(0, std::ios::beg); 
     cardNumbers.clear(); 
     std::streamsize first = cardNumbers.tellg(); 
     cardNumbers.ignore(std::numeric_limits<std::streamsize>::max()); 
     cardNumbers.clear(); 
     std::streamsize last = cardNumbers.tellg(); 
     cardNumbers.seekg(0, std::ios::beg); 
     std::streamsize lineNumbers = (last/57); 
     std::streamsize middle; 

     while (first <= lineNumbers) 
     { 
      middle = (first + lineNumbers)/2; 
      // middle * 57 takes us to the beginning of the correct line 
      cardNumbers.seekg(middle * 57, std::ios::beg); 
      cardNumbers.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 

      cardNumbers >> lN >> fN; 

      if (lN < lastName) 
      { 
       first = middle + 1; 
      } 
      else if (lN > lastName) 
      { 
       lineNumbers = middle - 1; 
      } 
      else 
      { 
       if (fN < firstName) 
       { 
        first = middle + 1; 
       } 
       else if (fN > firstName) 
       { 
        lineNumbers = middle - 1; 
       } 
       else if (fN == firstName) 
       { 
        foundPerson = true; 
        break; 
       } 
      } 
     } 

     if (foundPerson) 
     { 
      // When a person is found, we seek to the correct line position and 
      // offset by another 40 characters to receive the card number 
      cardNumbers.seekg((middle * 57) + 40, std::ios::beg); 
      std::cout << lN << ", " << fN << " "; 
      cardNumbers >> creditNumber; 
      return creditNumber; 
     } 
     return "Unable to find person.\n"; 
    } 
}; 

int main() 
{ 
    CardSearch CS("C:/Users/Rafael/Desktop/StolenNumbers.txt"); 
    std::string S = CS.Find("Ampersand", "Gregorina"); 
    std::cout << S; 

    std::cin.ignore(); 
    std::cin.get(); 

    return 0; 
} 

Я могу получить все, кроме первой записи в список. Кажется, что seekg ищет правильную позицию, но cardNumbers не читает правильную информацию. Когда «средний» установлен в 0, seekg должен искать 0-ю строку (средний * 57), читать в Амперсанде Грегорине и делать сравнение. Вместо этого он продолжает читать Андерсон Боб.

Любые идеи относительно того, почему это может происходить?

Благодаря

+1

Это середина? 'middle = (first + lineNumbers)/2;' – stark

+0

Да, эта средняя – rafa

+0

@rafa Весь ваш подход ошибочен. Большая ошибка заключается в том, что вы используете 'seekg' в файле, открытом в текстовом режиме. Вы не можете надежно использовать такие функции, когда вы открыли файл таким образом. 'Seekg' работает так, как вы ожидали бы от файлов, открытых в двоичном режиме' ios: binary'. Причина, по которой ваш код обречен на провал, заключается в том, что в текстовом режиме переводы выполняются для линии конца строки и EOF, о которых вы не знаете и не хотели бы отслеживать (вы потеряете свое здравомыслие, если вы пытался). Если вы собираетесь это сделать, откройте файл в двоичном режиме и откройте его. – PaulMcKenzie

ответ

0

номера строк модифицируется вашей петли, идущей от 4, 1, -1. -1 делает ваш цикл завершен слишком рано, чтобы вы не правильно подобрали первую запись.

Это кажется домашней проблемой, поэтому я надеюсь, что вы можете использовать это, чтобы направить себя на ответ.

+0

Это не должно заходить так далеко. Когда среднее равно 0, seekg должен отнести его к первой строке, которая является правильной записью, которую я ищу. Во втором цикле, когда LineNumbers все еще 1, средний - 0, но неправильно читает имя. Это должна быть первая запись, но вместо этого она читает запись 2, которая представляет собой Андерсон Боб – rafa

0

При использовании таких функций, как seekg, всегда лучше всего открыть файл в binary mode, а не в текстовом режиме, так как ваш код сейчас работает. Другими словами, вы должны делать это:

cardNumbers.open(fileName, std::ios::in | std::ios::binary); 

Причина заключается в том, что открытие файла в текстовом режиме позволит истекшим линии переводы можно сделать. Это дает такие функции, как seekg, tellg и т. Д. Что-либо между неустойчивым (или удачным для работы) в лучшем случае, а в худшем случае - бесполезным для обработки текста.

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

Кроме того, как только вы это сделаете, длина данных в строке включает не только видимый текст, но и невидимые символы, которые составляют последовательность конца строки. Таким образом, ваш ручной расчет 57 не будет правильным в двоичном режиме - он должен быть 58 или 59, в зависимости от того, используете ли вы Linux/Unix или Windows соответственно.