【问题标题】:How to separate comma delimited values without using vectors in C++?如何在不使用 C++ 中的向量的情况下分隔逗号分隔值?
【发布时间】:2017-07-10 16:20:29
【问题描述】:

我正在使用 C++ 编写 ATM 机器代码。我知道最好使用二进制文件和类,以便每个帐户在文件中具有完全相同的大小,即使它是空的。我正在寻找非常非常简单的东西。

我在解析.txt 文件中的逗号分隔值时遇到问题。有时我得到值,有时我只得到第一个值。我花了几天的时间来弄清楚它的解决方案。我搜索了互联网和stackoverflow,几乎每个答案都使用了向量或其他东西。

我没有使用strcmp 进行数组比较,而是创建了自己的函数来比较字符。

从代码的外观来看,我似乎做了很多,只是需要一点点推动。请看看我的代码,让我知道我哪里错了。我想消除语义错误!并修改以下代码以实现预期结果。

#include<iostream>
#include<conio.h>
#include<fstream>
#include<cstring>
#include<string>
#include<sstream>
#include<stdlib.h>

using namespace std;

void checkBalance();
void createAccount();
void deposit();
void withdraw();
void transfer();
void login();
void mainScreen();
void menuScreen();
// For file handling
void saveAccountToFile();
bool checkAccountExists(char*);
void loadAccount(char *);
void loadBeneficiary(char*);

bool compare(char *, char *);

const char * FILE_NAME = "accounts.txt";

char username[50];
char pin[5];
double balance = 0.0;

// Beneficiary Variables

char b_username[50];
char b_pin[5];
double b_balance = 0.0;

void menuScreen()
{
    char opt;
    do {
    system("cls");
    cout << "\n\n\t\t CMD - ATM";
    cout << "\n\n\tMENU";
    cout << "\n\n\t01. Check Balance";
    cout << "\n\n\t02. Withdraw";
    cout << "\n\n\t03. Deposite";
    cout << "\n\n\t04. Transfer to Another account";
    cout << "\n\n\t05. Logout";
    opt = _getch();
    switch (opt)
    {
    case '1':
        checkBalance();
        break;
    case '2':
        withdraw();
        break;
    case '3':
        deposite();
        break;
    case '4':
        transfer();
    case '5':
        cout << "\n\nThanks for using CMD ATM. Press any key to exit...";
        _getch();
        exit(1);
    default:
        cout << "\a\n\nIncorrect input. Press any key to try agian!";
        _getch();
    }
} while (opt != '5');
}

void mainScreen() 
{
char opt;
do {
    system("cls");
    cout << "\n\n\t\t CMD - ATM";
    cout << "\n\n\tMENU";
    cout << "\n\n\t01. Create Account";
    cout << "\n\n\t02. Login";
    cout << "\n\n\t03. Exit";
    opt = _getch();
    switch (opt)
    {
    case '1':
        createAccount();
        break;
    case '2':
        login();
        break;
    case '3':
        exit(1);
    default:
        cout << "\a\n\nIncorrect input. Press any key to try agian!";
        _getch();
    }
} while (opt != '3');
}

void saveAccountToFile() {

ofstream outFile;
outFile.open(FILE_NAME, ios::app);
if (outFile.is_open()) {
        outFile << username << ',' <<
            pin << ',' << balance << "\n";
}
outFile.close();
}

bool checkAccountExists(char * userName)
{   
int i = 0;

char temp[50] = {'0'};
ifstream inFile;
string line;
inFile.open(FILE_NAME);

if (inFile.is_open()) {
    while (getline(inFile, line)) {
        stringstream ss(line);
        string value;
        while (getline(ss, value, ',')) {
            if (i == 0) {
                strcpy(temp,value.c_str());
                if(compare(userName, temp)){
                    cout << "closing";
                    getchar();
                    inFile.close();
                    return true;
                }
                i = 1;
            }
            else if (i == 1) {
                i = 2;
            }
            else if (i == 2) {
                i = 0;
            }       
        }
    }
    inFile.close();
}
return false;
}


void loadAccount(char * userName)
{

int i = 0;
ifstream inFile;
string line;
inFile.open(FILE_NAME);

if (inFile.is_open()) {
    while (getline(inFile, line)) {
        stringstream ss(line);
        string value;
        while (getline(ss, value, ',')) {
            if (i == 0) {
                strcpy(username, value.c_str());
                i = 1;
            }
            else if (i == 1) {
                strcpy(pin, value.c_str());
                i = 2;
            }
            else if (i == 2) {
                balance = atof(value.c_str());
                i = 0;
            }
            if(compare(username,userName))
            {
                inFile.close();
                break;
            }       
        }
    }
    inFile.close();
}
}

void loadBeneficiary(char * userName)
{

int i = 0;
ifstream inFile;
string line;
inFile.open(FILE_NAME);

if (inFile.is_open()) {
    while (getline(inFile, line)) {
        stringstream ss(line);
        string value;
        while (getline(ss, value, ',')) {
            if (i == 0) {
                strcpy(b_username, value.c_str());
                i = 1;
            }
            else if (i == 1) {
                strcpy(b_pin, value.c_str());
                i = 2;
            }
            else if (i == 2) {
                b_balance = atof(value.c_str());
                i = 0;
            }   
            if(strcmp(b_username, userName)){
                inFile.close();
                break;
            }   
        }
    }
    inFile.close();
}
}

void login()
{
char tempName[50];
char tempPin[5];
cout << "\n\nEnter user name: ";
cin.getline(tempName, sizeof(tempName));
if (checkAccountExists(tempName)) {
    cout << "\n\nEnter user pin: ";
    cin.getline(tempPin,sizeof(tempPin));
    loadAccount(tempName);
    if (compare(tempPin, pin)) {
        cout << "\n\nLog in successfull!";
        cout << "\n\nPress any key to continue...";
        _getch();
        menuScreen();
    }else {
        cout << "User name or pin incorrect!";
        _getch();
    }
}
else {
    cout << "\n\nRecord not found. Press any key to continue...";
    _getch();
}

}

void checkBalance()
{
system("cls");
cout << "\n\n\tUser name = " << username;
cout << "\n\n\tBalance = " << balance;
_getch();
}

bool compare(char * msg1, char * msg2){
int count = 0;
int size = sizeof(msg1);
if(sizeof(msg2) == size){
    for(int i = 0; i < size; i++){
        if(msg1[i]==msg2[i]){
            count++;
        }
    }
    if(count == size)
        return true;
}

return false;
}

int main(int argc, char** argv) {

mainScreen();
getchar();
return 0;
}

TXT 文件

jhon,5155,99999.99
bot,4414,232323
theta,2111,34234

【问题讨论】:

  • 我认为大多数标准库也是不允许的,因为您似乎在避免它。
  • “二进制文件处理很容易”——一点也不;那里存在严重的潜在问题。这就是为什么经常使用文本文件的原因。
  • 另外,使用标准函数!它们经过优化,有时甚至可以内联,如果您担心的是性能(别担心!)。它们还使代码更简洁、更易读,最重要的是更可维护!然后你提到“语义错误”,你的意思是什么?你能详细说明一下吗?
  • 那么也许你应该阅读 Eric Lippert 的 How to debug small programs。解决此类问题的最佳方法之一是使用调试器。另一个值得考虑的好事情是简单。例如,不要使用当前循环来解析一行,而是一个接一个地使用三个单独的 getline 调用。它更简单、更易于阅读和理解,而且由于它更简单,代码中出现错误的可能性也会更低。

标签: c++ file-handling


【解决方案1】:

关于如何简化阅读循环,比如

while (getline(inFile, line)) {
    stringstream ss(line);
    string value;

    getline(ss, value, ',');
    string name = value;

    getline(ss, value, ',')
    string pin = value;

    getline(ss, value)
    double balance = stod(value);

    // Now use the name, pin and balance some way...
}

当您只需要名称时,您不需要第二次和第三次getline 呼叫,只需第一次。

还可以考虑使用结构来存储名称、引脚和余额,然后使用container(例如vector)来存储结构。

另外,为了简单起见,我建议您只阅读该文件一次。然后循环遍历向量来找到你需要的数据。

【讨论】:

  • 我在 OOP C++ 中使用了该程序的另一个版本并使用了向量。我将所有数据加载到矢量并对其进行操作。但你认为这样做是个好主意吗?每次都将整个数据加载到内存中或搜索文件是一种好习惯吗?
  • 如果合适的话,最好将整个文件加载到内存中,这样可以使代码更简单,或者您需要额外的速度。如果文件不适合内存,或者您的内存有限,可以更好地用于其他用途,或者它使代码更简单,则最好扫描文件。
【解决方案2】:

这是不使用stringstream的替代方法

while(getline(inFile,line,',')){

        strcpy(username, line.c_str());

        getline(inFile,line,',');
        strcpy(pin, line.c_str());

        getline(inFile,line,'\n');
        balance = atof(line.c_str());

        if(compare(username,userName)){
            inFile.close();
            break;
        }else 
            continue;

    }

【讨论】:

  • 为什么要混合 C 风格的字符串和 C++ 字符串?为什么要对将立即覆盖(或完全覆盖)的值使用全局变量?
  • @Useless 我知道这样做是不安全的。但是您也不能将 .txt 文件用于这样的实际程序,因此它只是用于学习目的。
  • 文本文件用于大量实际程序,作为配置或其他输入。只是在任何地方使用std::string,而不是混合C++ 字符串、字符数组、strcpy(...)c_str() 等,这并不是不安全的(嗯,可能是这样,但这不是我的反对意见)。它只是丑陋和过于复杂。这样做比在任何地方都使用std::string更难,而且我认为没有任何好处超过额外的复杂性。
  • 我同意你的看法。我使用了char array,以便可以比较`名称`和pin charcompare(char*,char*) 中的char 但即使在使用它之后,假设我有两个要比较的名称,我不会成功与John 和Johnn /约翰。
猜你喜欢
  • 2017-06-22
  • 1970-01-01
  • 2019-08-19
  • 1970-01-01
  • 2013-07-14
  • 1970-01-01
  • 2018-07-18
  • 2019-12-25
  • 1970-01-01
相关资源
最近更新 更多