2017-01-24 4 views
0

Здравствуйте, я делаю программу, и в моей программе у меня есть класс Customer. Чтобы сохранить клиента на компьютере, я создаю файл и разделяю все данные клиента с :: как имя :: пароль :: phonenbr. Но моя проблема в том, что если я напишу строку, которая находится в комментарии к моему коду, данные будут сохранены в файле, но если я напишу ту же строку в if(), которая проверяет, является ли файл пустым, это не сделайте что-нибудь, хотя я вижу с компилятором, что с этой линией нет проблем.Текст не сохраняется в файле

Если вы можете мне помочь, это будет изящно!

void Shop::Add_Customer() 
{ 
    fstream myfile; myfile.open("CustomerFile.txt"); 
    string name, password, phonenbr; 
    string buffer, delimitor = "::"; 

    system("cls"); 
    cout << "Name of the customer: "; cin >> name; 
    cout << "Password of the customer: "; cin >> password; 
    cout << "Phone number of the customer: "; cin >> phonenbr; 

    if (!myfile.is_open()) 
    { 
     myfile.open("CustomerFile.txt", ios::out); 
    } 
    //myfile << name + delimitor + password + delimitor + phonenbr << endl; 

    if (myfile.peek() == std::ifstream::traits_type::eof()) 
    { 
     myfile << name + delimitor + password + delimitor + phonenbr << endl; 
    } 
    else 
    { 
     while (getline(myfile, buffer)) 
     { 
      if (CheckIfCustomerExist(buffer, name, phonenbr) == true) 
      { 
       cout << "Customer already exist" << endl; 
      } 
      else 
      { 
       myfile << name + delimitor + password + delimitor + phonenbr << endl; 
       cout << "Customer insert in the file " << endl; 
      } 
     } 
    } 


} 
+2

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

ответ

0

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

Вот очень простой пример того, что происходит:

#include <iostream> 
#include <fstream> 
using namespace std; 

int main() 
{ 
    fstream myfile("CustomerFile.txt", ios::out); 
    if (!myfile.is_open()) 
    { 
     cout << "file not open." << endl; 
    } 
    else 
    { 
     if (myfile.peek() == std::ifstream::traits_type::eof()) 
     { 
      if (myfile.eof()) 
      { 
       cout << "Need to clear the EOF flag." << endl; 
      } 
     } 
    } 
} 

Заглянув в EOF установлен флаг EOF, помещая поток в состоянии ошибки и делает его недоступным на запись. Поскольку мы хотим расширить файл, нам нужно очистить этот флаг с the aptly named clear method.

#include <iostream> 
#include <fstream> 
using namespace std; 

int main() 
{ 
    fstream myfile("CustomerFile.txt", ios::out); 
    if (!myfile.is_open()) 
    { 
     cout << "file not open." << endl; 
    } 
    else 
    { 
     if (myfile.peek() == std::ifstream::traits_type::eof()) 
     { 
      if (myfile.eof()) 
      { 
       cout << "Need to clear the EOF flag." << endl; 
      } 
      myfile.clear(); 
      if (!myfile.eof()) 
      { 
       cout << "OK. EOF clear now." << endl; 
      } 
     } 
    } 
} 

Off тему материал:

Следующий код

while (getline(myfile, buffer)) 
    { 
     if (CheckIfCustomerExist(buffer, name, phonenbr) == true) 
     { 
      cout << "Customer already exist" << endl; 
     } 
     else 
     { 
      myfile << name + delimitor + password + delimitor + phonenbr << endl; 
      cout << "Customer insert in the file " << endl; 
     } 
    } 

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

Лучше было бы прочитать и сравнить, а затем, если совпадение не будет найдено до конца файла и добавит входного клиента.

Кроме того, открытие файла логики неоправданно сложна и может по-прежнему не

fstream myfile; myfile.open("CustomerFile.txt"); 

if (!myfile.is_open()) 
{ 
    myfile.open("CustomerFile.txt", ios::out); 
} 

Первый вызов, чтобы открыть, безусловно, не сработать, если файл не существует, заставляя второго вызова, чтобы открыть. Возможно, просто добавьте ios::out к этому вызову и сделайте это. Второй вызов верхней открытой может не по другим причинам, и не тестируется на успех, поэтому я рекомендую

fstream myfile("CustomerFile.txt", ios::out); 
if (!myfile.is_open()) 
{ 
    perror("file not open: "); 
} 
else 
{ 
    // your code goes here 
} 

Documentation for perror

0

корень вашей проблемы лежит в состоянии вашего Условный оператор в:

(myfile.peek() == std::ifstream::traits_type::eof()) 

Apperantly, файл открыт в режиме fstream в строке:

fstream myfile; myfile.open("CustomerFile.txt"); 

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

Я пробовал один из моих собственных методов, который всегда работает, и он также работал на ваш код. Я заменил следующие строки кода:

if (myfile.peek() == std::ifstream::traits_type::eof()) 
    { 
     myfile << name + delimitor + password + delimitor + phonenbr << endl; 
    } 

С этими линиями:

myfile.seekg (0, ios::end); 
    int length = myfile.tellg(); 

    if (length == 0) 
    { 
     myfile << name + delimitor + password + delimitor + phonenbr << endl; 
    } 

Первая линия myfile.seekg (0, ios::end); Получает расстояние между 2 точками, указанными в скобках. 0 и ios :: end являются самоочевидными; 0 - это начало файла, а ios :: end - конец файла.

Вторая строка int length = myfile.tellg(); хранит значение, указанное в указанной строке в переменной int, называемой длиной. Длина - это количество символов, которые «курсор» должен был бы перемещать, чтобы получить от начала до конца этого файла (попробуйте представить курсор как мигающую вещь, подобную той, что находится в Microsoft Word, которая находится перед словом, которое вы ввод, кроме здесь, вы не можете видеть курсор в текстовом файле, перемещающемся от начала до конца).

Это, если условие довольно простое. Если длина равна нулю, это означает, что курсор должен переместить 0 пунктов, чтобы получить от начала файла до конца файла, а затем записать все, что вы хотите, в этот файл. Эта техника работала (по крайней мере, для меня).

На боковой ноте есть несколько других областей, где ваш код может улучшиться. Например, почему вы добавили, если-заявление:

if (!myfile.is_open()) 
{ 
    myfile.open("CustomerFile.txt", ios::out); 
} 

Этот код представляет собой повторение этих строк кода:

fstream myfile; myfile.open("CustomerFile.txt"); 

.Open() команда уже выполняет Условный оператор Я указал. Если файл, указанный в open(), найден, тогда он откроет этот файл; иначе он будет продолжать создавать этот новый файл. Следовательно, этот if-statement является избыточным и должен быть удален, поскольку он потребляет ненужную мощность процессора и замедляет работу вашей программы (но не много, но вы скоро поймете, что каждая миллисекунда рассчитывает на запуск вашего кода, а эффективность - ключевой). Я бы рекомендовал вам удалить этот if-statement.

Другая проблема - ваши 3 переменные, которые вы принимаете для ввода. Учитывая, что это строки, почему вы используете метод cin >>? Использование cin будет принимать только первое слово в вашем предложении; в вашей следующей строке:

cout << "Name of the customer: "; cin >> name; 

Если ввести John Doe, это будет только сохранить Джон имя переменного, и она будет двигаться «Doe» к следующему входным переменному, которая является паролем в вашем случае. Если нет другого cin, тогда он будет игнорировать слова после пробела. Поэтому используйте следующую строку для всех точек входа:

getline(cin, name); 

Эта функция получит все слова и пространства как одного предложения до точки вы попали Enter, в отличие от КИН, что будет только первое слово и игнорировать остальная часть предложения.

И, наконец, ваш номер телефона должен иметь тип int. Я оставлю это для вас, чтобы исправить в соответствии с вашим требованием.

Надеюсь, я ответил на ваш вопрос и надеялся, что мои советы будут полезны. Удачи!


EDIT: Еще один момент, о котором я пропустил ваш код, заключался в том, что ваш цикл while работает для каждой строки. Это означает, что он будет проверять имя конкретного клиента в каждой отдельной строке файла. Это не то, что вы хотите. Вы хотите прочитать каждую строку в файле, НО, если вы найдете клиента, вы хотите завершить работу, не продолжая следующую строку. Кроме того, вы хотите распечатать только сообщение об ошибке ПОСЛЕ того, как вы прочитали весь файл, а не только одну строку.

else 
{ 
    int check = 0; 
    while (getline(myfile, buffer)) 
    { 
     if (CheckIfCustomerExist(buffer, name, phonenbr) == true) 
     { 
      cout << "Customer already exist" << endl; 
      check = 1; 
      break; 
     } 
    } 
    if (check == 0) 
    { 
     myfile << name + delimitor + password + delimitor + phonenbr << endl; 
     cout << "Customer insert in the file " << endl; 
    } 
} 

Что делает этот фрагмент кода, так это то, что он проходит через каждую строку, проверяя клиента в этой строке. Если эта строка имеет запись клиента, то она устанавливает контрольное значение типа int в 1 из 0, и оператор break завершает цикл while. После прочтения всего файла он переходит к if-statement. В этом утверждении, если контрольная переменная все еще имеет 0, это означает, что у файла не было клиента, в котором новая запись будет добавлена ​​в файл.

Кроме того, я сказал, что номер_телефона должен быть значением int. Я беру это как можно дальше и получаю от других пользователей StackOverflow, номер телефона лучше всего подходит как строковое значение, так как его формат может не храниться должным образом в качестве значения int (например, 0059875 будет сохранен как 59875).

+0

Второй вызов 'open' охватывает случай сбоя« файл не существует », не охватываемый первым вызовом' open'. Добавленный 'ios :: out' заставит создать файл, если он не существует. Так что это повторение, но второй звонок правильный. – user4581301

+0

Хорошая оценка имени клиента. Не согласитесь, что номер телефона является 'int'. Вероятно, он должен содержать строку и проверяться на правильное количество цифр и что все входные символы являются цифрами. Например, 'int' не будет обрабатывать предшествующие 0. – user4581301

+0

Собственно, это правда @ user4581301, я не рассматривал этот аспект номера телефона. –