Member 13707321 Ответов: 2

Как я могу читать аргументы командной строки в любом порядке?


My school assignment is a bank account program that can run in two modes, interactive and bash mode. Right now I'm working on bash mode and need to accept command line arguments to open a database account (just a text file with info for different accounts, like names, account numbers, passwords, and balance) and perform actions like deposit, transfer funds, etc. However, we have to be able to accept these commands in any order the user passes. For instance, if you wanted to deposit something, you would run something like ./bankacct /OTestDB.txt /A12bw3 /Pabc123 /D300.00 where /O opens the database file that stores user info, /A and /P check the account number and password as a way of "signing in", and /D deposits 300 dollars to the account. We've yet to learn classes, and the program is mostly based in using pointers and structures. Additionally, we're only allowed to use c-strings, no string class, but I don't think that has a bearing on this part. So my problem is trying to figure out how to get ./bankacct /OTestDB.txt /A12bw3 /Pabc123 /D300.00 in the same way as ./bankacct /D300.00 /Pabc123 /A12bw3 /OTestDB.txt

Что я уже пробовал:

My first approach to this problem was to set a switch statement that would call the relevant function and just run it through a for loop to cover every argument. But if a user doesn't open the database file first, then I don't have the arguments to pass to a function to "log in" and choose the account. So with my current approach, it seems like the order of command line arguments would need to be constant. So any tips on where to get started would be great, as right now I'm imagining having a set order in which I take arguments and just reading through my argument array with a for loop for each argument until I get to the argument I need. Here's some of my relevant functions:

/* -----------------------------------------------------------------------------
 FILE NAME:         bankacct_v4.cpp
 DESCRIPTION:       A program that allows manipulation of a bank account database
 USAGE:             ./bankacct_v4
 COMPILER:          GNU g++ compiler on Linux
 NOTES:
 ----------------------------------------------------------------------------- */

#include "bankacct_v4.h"

/* -----------------------------------------------------------------------------
 FUNCTION:          main()
 DESCRIPTION:       controller for the code I guess ADD MORE LATER
 RETURNS:           0
 NOTES:				if statement to switch between command line and interactive
 					int argc                //number of parameters on command line
                    char *argv[]            //an array of pointers to C-strings
 ------------------------------------------------------------------------------- */
int main(int argc, char * argv[])
{
	bool flag = true;
	int num_acct = 0;	//This is what you will access with output so you don't print a bajillion blank accounts
    account* user = new account[100];

	if (argc > 1)
	{
		for (int i = 0; i < argc; ++i)
	{
		cout << "Argument " << i << " : " << left << setw(16) <<  argv[i] << endl;
		if (i) commands(argv[i], i);
		else cout << endl;
	}
	}
	else
		{
                    cout << endl;
		}

	delete[] user;
	
    cout << endl;
    cout << "Programmed by: " << PROGRAMMER_NAME << " -- ";
    cout << __DATE__ << "  " __TIME__ << endl;
    cout << endl;
    
    return 0;
}
/*-----------------------------------------------------------------------------

FUNCTION NAME:     void read_file
PURPOSE:           read in file as designated by command line
RETURNS:           nothing
NOTES:             
----------------------------------------------------------------------------- */
void read_file(char* file, account* user, int &num_accts)
{
	ifstream infile(file);

	if (!infile)
		cout << "\nError opening file: " << file << "\n\n";
	else
	{
		cout << "Input file: " << file << endl;
		while(infile)	//won't proceed if end of file WHY DOES THIS WORK
		{
		create(user, cout, infile, 0, num_accts); 
		}
		infile.close();
	}

}
/* -----------------------------------------------------------------------------
FUNCTION:          check_arg()
DESCRIPTION:       checks command line args
RETURNS:           Nothing
NOTES:
----------------------------------------------------------------------------- */
char check_arg(char arg[])
{	
	char buf[100];
	const char SLASH = '/';
	char valid_options[] = "?ACDFILMNOPRSTW";
	char *p = valid_options;

	bool first_char = arg[0] == SLASH; 	//Test 1st argument's first character

	bool second_char = false;

	for (; *p != 0; ++p) 				//Test 1st arg's 2nd character
	{
		second_char = arg[1] == *p;
		if (second_char == true) break;
	}

	if (!first_char || !second_char)
		cout << "Invalid argument" << endl;

	else
	{
	cout << "Option: " << *p;

	if (arg[2] != 0)
	{
	strcpy(buf, arg+2);
	cout << " Value: " << buf;
	}
	cout << endl;
	}
	return *p;
}

/* -----------------------------------------------------------------------------
 FUNCTION:          void create()
 DESCRIPTION:       creates a bank account
 RETURNS:           Nothing
 NOTES:             Use ostream object instead of cout/cin
 					Can I use a loop instead of asking for each individual input?
 					Array of structures
 ------------------------------------------------------------------------------- */

void create(account* user, ostream &out, istream &in, bool mode, int &num_acct)
{
    if (mode) out << "Last Name? ";
    in >> user[num_acct].lname;	
    if (mode) out << "\nFirst Name? ";
    in >> user[num_acct].fname;
    if (mode) out << "\nMiddle Initial? ";
    in >> user[num_acct].mi;
    if (mode) out << "\nSocial Security Number? ";
    in >> user[num_acct].ssn;
    if (mode) out << "\nPhone Number? ";
    in >> user[num_acct].phone;
    if (mode) out << "\nBalance? ";
    in >> user[num_acct].bal;
    if (mode) out << "\nAccount Number? ";
    in >> user[num_acct].acctnum;
    if (mode) out << "\nPassword? ";
    in >> user[num_acct].acctpass;
    num_acct++;
}
/* -----------------------------------------------------------------------------
 FUNCTION:          void display()
 DESCRIPTION:       displays account info
 RETURNS:           Nothing
 NOTES:             Use ostream object instead of cout/cin
 					Does this one really need pass by ref? Copy should be fine.
 ------------------------------------------------------------------------------- */

void display(account* user, ostream &out, int num_acct)
{
    out << user[num_acct].lname << endl;
    out << user[num_acct].fname << endl;
    out << user[num_acct].mi << endl;
    out << user[num_acct].ssn << endl;
    out << user[num_acct].phone << endl;
    out << user[num_acct].bal << endl;
    out << user[num_acct].acctnum << endl;
    out << user[num_acct].acctpass << endl;
    out << endl;

}
/* -----------------------------------------------------------------------------
 FUNCTION:          void output()
 DESCRIPTION:       puts array of structures to output file
 RETURNS:           Nothing
 NOTES:             
 ------------------------------------------------------------------------------- */
 void output(account* user, int num_acct)
 {
 	ofstream outfile;
 	outfile.open("output.txt");
 	for (int i = 0; i < num_acct; i++)
 	{
 		display(user, outfile, i);
 	}
 	outfile.close();
 }
 /*-----------------------------------------------------------------------------

FUNCTION NAME:     void commands()
PURPOSE:           
RETURNS:           Nothing (void function)
NOTES:             ?ACDFILMNOPRSTW are valid. stuff ilke /F and /L could probably just be lines like user[i].fname = "value passed". How to take any order?
----------------------------------------------------------------------------- */
void commands(char argv[], int i)
{
	switch (check_arg(argv[i]))
		{
		case '?':
			//helpmenu();
			break;
		case 'A':
			//readaccountnumber(); ? or set var acctnum to this value?
			break;
		case 'C':
			//createpass();
			break;
		case 'D':
			//deposit();
			break;
		case 'F':
			//firstname(); assigns value of first name when creating new account
			break;
		case 'I':
			//output(); for displaying account info
			break;
		case 'L':
			//lastname(); assigns last name
			break;
		case 'M':
			//initial(); assigns middle initial
			break;
		case 'N':
			//phonenum(); assigns phone number
			break;
		case 'O':
			//read_file(char* file, account* user, int &num_accts); reads file info to array of structures
			break;
		case 'P':
			//readpassword(); as a part of "sign in" process
			break;
		case 'R':
			//reportfile(); prints database in form of report file
			break;
		case 'S':
			//ssn(); assigns social security number
			break;
		case 'T':
			//transfer(); (requires two /A and /P)
			break;
		case 'W':
			//withdraw(); 
			break;
}


Я также знаю, что не передаю правильные аргументы check_arg, когда вызываю его в командах. Функция работает, если я вызываю ее вместо команд в main, но я не знаю, как check_arg будет знать, что делать на самом деле делать с командами, так как это просто говорит о том, что это были за команды.

PIEBALDconsult

(Не читал ваш код.)
Я не уверен, что буду использовать указатели-скорее всего, простые целочисленные смещения, - но если это требование назначения, то это вряд ли сложнее.
Если вы разрешаете только один из любого конкретного типа аргументов (O, A, P и т. д.), то попробуйте использовать strstr, чтобы найти каждый возможный аргумент и (если таковой имеется) присвоить его адрес указателю на него.
http://www.cplusplus.com/reference/cstring/strstr/
Что-то вроде:
char *argA = strstr ( cmdline , "/A" ) ;

Именно поэтому операционная система никогда не должна пытаться анализировать командную строку и всегда позволять программе делать это самой.
Если вы используете Windows, попробуйте использовать функцию GetCommandLine, чтобы получить фактическую командную строку.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156(v=против 85).aspx

2 Ответов

Рейтинг:
2

CPallini

Я бы использовал что GNU обеспечивает (Синтаксический анализ аргументов программы (библиотека GNU C)[^Поскольку ваше назначение-это задание, оно не может быть жизнеспособной альтернативой.


Рейтинг:
0

OriginalGriff

Я бы сделал это так, чтобы разобрать команды дважды: сначала разбор отделяет "файлы" от "переключателей" и проверяет основы: один и только один файл плюс несколько переключателей.
Тогда все просто: откройте файл и обработайте swiches.