2012-09-09 126 views
4

我正在尝试编写这个程序,它将员工数据库保存在一个随机访问文件中,它必须具有添加员工和删除员工的功能(通过写入记录中的所有空间)。这是迄今为止我所拥有的,但它并不完全正确。在阅读员工时,它读取正确记录的工资,但读取下一条记录的名称。另外,当我删除最后一条记录并在该记录中添加一名员工时,我无法查看员工信息,但我得到一个异常错误。随机访问文件无法正常工作

我不在这里寻找解决方案,只是在正确的方向轻推。谢谢。

#include "stdafx.h" 
#include <iostream> 
#include <iomanip> 
#include <fstream> 
#include <sstream> 
#include "ccc_empl.h" 

using namespace std; 

const int NEWLINE_LENGTH = 2; 
const int RECORD_SIZE = 30 + 10 + NEWLINE_LENGTH; 

/** 
    converts a string to a floating-point value 
    @param s a string representing a floating-point value 
    @return the equivalent floating-point value 
*/ 
double string_to_double(string s) 
{ 
    istringstream instr(s); 
    double x; 
    instr >> x; 
    return x; 
} 


/* 
    reads an employee record from the input file 
    @param e the employee 
    @param in the file to read from 
*/ 
Employee read_employee(istream& in) 
{ 
string line; 
getline(in, line); 
string input_name = line.substr(0, 30); 
double input_salary = string_to_double(line.substr(30, 10)); 
Employee e(input_name, input_salary); 
return e; 
} 

/* 
gets input for an Employee object 
@param input_name the name of the employee 
@param input_salary the salary of the employee 
@param e the Employee object 
@return returns the Employee object 
*/ 
Employee input_employee() 
{ 
string input_name; 
cout << "Name: "; 
cin.ignore(); 
getline(cin, input_name); 
cout << "Salary: "; 
double input_salary; 
cin >> input_salary; 
Employee e(input_name, input_salary); 
return e; 
} 

/** 
    adds an employee record to a file 
    @param e the employee record to write 
    @param out the file to write to 
*/ 
void add_employee(Employee e, ostream& out) 
{ 
out << e.get_name() 
    << setw(30) 
    << fixed << setprecision(2) 
    << e.get_salary(); 
} 

/** 
    removes an employee record from a file 
    @param e the employee record to remove 
    @param out the file to remove from 
*/ 
void remove_employee(ostream& out) 
{ 
out << " " << setw(42) << fixed << setprecision(2) << " \n"; 
} 

int main() 
{ 
    cout << "Please enter the data file name: "; 
    string filename; 
    cin >> filename; 
    fstream fs; 
    fs.open(filename); 

    fs.seekg(0, ios::end); // Go to end of file 
    int nrecord = fs.tellg()/RECORD_SIZE; // determine number of records in the file 
    int menu_input = 0; 
    string input_name; 
    double input_salary = 0; 

    while (menu_input != 4) 
    { 
    cout << "Please enter the record to update: (0 - " << nrecord - 1 << ") exit to quit "; 
    int pos = 0; 
    cin >> pos; 
    if(cin.fail()) 
    { 
     cout << "Exiting..." << endl; 
     system("pause"); 
     return 0; 
    } 

    // menu for user input 
    cout << "\nWhat action would you like to perform?" << endl; 
    cout << "Add employee.....1" << endl; 
    cout << "Remove employee..2" << endl; 
    cout << "View employee....3" << endl;  
    cin >> menu_input; 

    switch(menu_input) 
    { 
    case 1: fs.seekg(pos * RECORD_SIZE, ios::beg); 
     add_employee(input_employee(), fs); 
     break;   
    case 2: fs.seekg(pos * RECORD_SIZE, ios::beg); 
     remove_employee(fs); 
     break; 
    case 3: fs.seekg(pos * RECORD_SIZE, ios::beg); 
     cout << "\nName: " << read_employee(fs).get_name() << "Salary: " << read_employee(fs).get_salary() << endl << endl; 
     break; 
    default: cout << "Invalid entry" << endl; 
     break; 
    } 
    } 

    fs.close(); 
    system("pause"); 
    return 0; 
} 

好的,这里是新的和改进的(好吧,我认为是)代码。我遇到的唯一问题是添加员工时,我可以让程序将其添加到第一个打开的记录中,但如果没有打开的记录,我似乎无法将其添加到文件没有搞乱添加到第一个空记录。我的意思是,如果有空记录,它会将该员工添加到记录中,但如果没有空记录,则不会将该员工添加到文件末尾。如果我添加代码以添加到文件末尾,会发生以下两件事之一:要么添加到空记录中,要么请求另一名员工并将其添加到最后,要么只是跳过空记录并添加到文件结尾。

不知道我在做什么错在这里,但任何提示将不胜感激。

#include "stdafx.h" 
#include <iostream> 
#include <iomanip> 
#include <fstream> 
#include <sstream> 
#include "ccc_empl.h" 

using namespace std; 

const int NEWLINE_LENGTH = 2; 
const int RECORD_SIZE = 30 + 10 + NEWLINE_LENGTH; 

/** 
converts a string to a floating-point value 
@param s a string representing a floating-point value 
@return the equivalent floating-point value 
*/ 
double string_to_double(string s) 
{ 
    istringstream instr(s); 
    double x; 
    instr >> x; 
    return x; 
} 


/* 
reads an employee record from the input file 
@param e the employee 
@param in the file to read from 
*/ 
Employee read_employee(istream& in) 
{ 
string line; 
getline(in, line); 
string input_name = line.substr(0, 30); 
double input_salary = string_to_double(line.substr(30, 10)); 
Employee e(input_name, input_salary); 
return e; 
} 

/* 
gets input for an Employee object 
@param input_name the name of the employee 
@param input_salary the salary of the employee 
@param e the Employee object 
@return returns the Employee object 
*/ 
Employee input_employee() 
{ 
string input_name; 
cout << "Name: "; 
cin.ignore(); 
getline(cin, input_name); 
cout << "Salary: "; 
double input_salary; 
cin >> input_salary; 
Employee e(input_name, input_salary); 
return e; 
} 

/** 
adds an employee record to a file 
@param e the employee record to write 
@param out the file to write to 
*/ 

void add_employee(Employee e, ostream& out) 
{ 
out << e.get_name() 
    << setw(10 + 30 - e.get_name().length()) 
    << fixed << setprecision(2) 
    << e.get_salary() << "\n"; 
} 

/** 
removes an employee record from a file 
@param e the employee record to remove 
@param out the file to remove from 
*/ 

void remove_employee(ostream& out) 
{ 
out << " " << setw(40) << fixed << setprecision(2) << " \n"; 
} 


int main() 
{ 
    cout << "Please enter the data file name: "; 
    string filename; 
    cin >> filename; 
    fstream fs; 
    fs.open(filename); 

    fs.seekg(0, ios::end); // Go to end of file 
    int nrecord = fs.tellg()/RECORD_SIZE; // determine number of records in the file 
    int menu_input = 1; 
    string input_name; 
    double input_salary = 0; 

    while (menu_input) 
    {  
    // menu for user input 
    cout << "\nWhat action would you like to perform?" << endl; 
    cout << "Add employee.....1" << endl; 
    cout << "Remove employee..2" << endl; 
    cout << "View employee....3" << endl; 
    cout << "Exit.............4" << endl; 
    cin >> menu_input; 
    if (menu_input == 4) 
    { 
     cout << "\nExiting..." << endl << endl; 
     system("pause"); 
     return 0; 
    } 
    // switch statment to perform selected menu_input task 
    switch(menu_input) 
    { 
    case 1: // adds an employee in the first empty record 
      // or at the end of the file if no records are empty   
      { 
       int count = 0; 
       string s; 
       for (int i = 0; i < nrecord; i++) 
       { 
        fs.seekp(count, ios::beg); 
        getline(fs, s); 
        if (isspace(s[0])) 
        { 
         fs.seekp(count, ios::beg); 
         add_employee(input_employee(), fs); 
         nrecord++; 
         break; 
        }else 
         count += 42; 
       } 
      }   
     break;   
    case 2: { 
      cout << "Please enter the record to remove: (0 - " << nrecord -1 << ") "; 
      int pos = 0; 
      cin >> pos; 
      fs.seekp(pos * RECORD_SIZE, ios::beg); 
      remove_employee(fs); 
      } 
     break; 
    case 3: { 
      cout << "Please enter the record to view: (0 - " << nrecord -1 << ") "; 
      int pos = 0; 
      cin >> pos; 
      fs.seekg(pos * RECORD_SIZE, ios::beg); 
      Employee e(read_employee(fs));   
      cout << "\nName: " << e.get_name() << "Salary: " << e.get_salary() << endl << endl; 
      } 
     break;  
    default: cout << "Invalid entry" << endl; 
     break; 
    } 
} 

fs.close(); 
system("pause"); 
return 0; 
} 
+0

顺便说一句,C++有像'stod'和'strtod'这样的将字符串转换为双精度的东西。不需要自己做。 – chris

+0

@chris我更喜欢,并建议,使用字符串流。 strtod的错误处理已经被严重打击了。在符合POSIX标准的编译器上可以正确使用它,但很难。并非所有的编译器符合POSIX标准:我的GCC标准库实现(在OS X上)使用无效的错误代码。 –

+0

@KonradRudolph,不错,我希望标准会采用boost的词法转换。我在一个问题中讨论'stoi'的确很愉快。 – chris

回答

1

您拨打read_employee(fs)两次。首先读取你想要的记录,第二个读取下一个(标准没有指定哪个是'第一个')。

+0

谢谢,我现在看到代码中特定部分的问题,非常感谢帮助。 –

0

看来你永远不会在add_employee中添加行结束符,并且read_employee被调用两次。只需缓存该函数的结果。

我知道这只是作业,但我认为你可以改进这个程序。

  1. 需要将数据库保留为运行时的文件吗?您可以将整个文件读入Employee数组,并在最后重写。它不会限制员工姓名的长度。

  2. 该程序不是跨平台的。您不应该依赖文件大小来确定数据库中的条目数,因为行终止符因操作系统而异。相反,请全部阅读并统计条目。

  3. 只有windows有'暂停'程序。为了达到同样的效果,只需读取stdin并在程序结束时忽略输入。

+0

我看到调用read_employee()的问题。我更改了代码,以便read_employee中的数据存储在临时Employee对象中,然后使用Employee方法显示数据。感谢您指出了这一点。 –

+0

我想我可以将所有的数据读入数组,编辑数据,然后写入文件,我会研究这个。不确定在作业到期之前我会完成它,但这对我来说是一个学习练习。感谢您的建议! –