一个简单的城市列表管理器






4.13/5 (10投票s)
一个用 C++ 编写的小程序。一个城市列表管理器。

引言
本文的主要目标是通过一个管理城市列表的小程序,向初学者展示 C++ 语言的一些基本方面。
背景
我将讲述一个关于这个程序范围的小故事。我认为了解某个事物为何被创造出来会更有趣。这个故事是虚构的。
露西在一个网络洗衣店工作,这家洗衣店负责上门收取脏衣服,并送回干净的衣服给客户。每个分店服务于自己的城市及其周边城市。现在,人们打电话请求服务,露西会在一本账本上核实请求服务的城市是否由某个洗衣分店提供服务。
在账本上搜索所服务的城市名称使得服务有点慢,客户需要等待。于是露西想,她可以使用一个软件来执行这个搜索,这样每次通话都能节省一些宝贵的时间。
露西向程序员解释说她需要这个程序具备以下功能:
添加:这个列表经常更新,因为有新的城市被服务,新的分店开业。因此,需要一个功能允许用户添加新城市。城市名称是唯一需要的信息。
编辑:需要一个选项来编辑城市名称,因为有时名称不正确,露西需要进行更正。
报告:这是一个城市列表,可以通过名称或部分名称搜索城市。这是露西接到电话时会使用的功能,需要核实来电者所在的城市是否提供服务。
删除:允许用户删除城市。有时分店关闭或某个特定城市不再提供服务。
有了这些信息,程序员开始工作。
Using the Code
程序员选择使用 C++ 语言和一些面向对象的概念。城市列表将保存在一个文本文件中。为解决这个问题而定义的类是:City、CityList 和 UserInterface。
City 是用于表示城市的类。它有一个属性来存储城市名称,还有一个方法以露西定义的特定格式显示城市名称。

name 属性是公共的,用于保存城市名称。ShowCity() 会在括号之间打印城市名称。括号之间将有 30 个字符。为了填补这两个括号之间的空字符,将使用点字符。
下面是 City 类的简单用法:
//
// City class usage sample
// 
#include "city.h"
.
.
.
City temp_city;
temp_city.name = "SAO_PAULO/SP";
temp_city.ShowCity();
结果将是。

打印城市名称的方法代码是
void City::ShowCity()
{
     std::cout << "["; 
     std::cout.width(32);
     std::cout.fill('.');
     std::cout << std::left << name;  
     std::cout << std::right;
     std::cout << "]" << std::endl;
}
CityList 是负责管理 City 对象列表的类。

CityList 类的属性是 city_list 和 filter
city_list:这是一个指向 City 对象的指针列表(STL),用于保存城市列表。这是一个公共属性。
list <City> city_list;
filter:这是一个字符串属性,包含用于过滤城市列表的城市名称或部分名称。
CityList 类的方法是
Add(new_city):此方法将指向 City 对象的指针添加到 CityList.city_list 中。new_city 参数是指向 City 对象的指针。
void CityList::Add(City *new_city)
{
     city_list.push_front(*new_city);
}
Show():此方法遍历 CityList.city_list,并为每个列表项调用 City.ShowCity() 方法。如果 filter 属性不为空,该方法将按城市名称搜索,以验证 City.name 是否包含 CityList.filter。此方法显示城市在列表中的位置索引。此索引对用户引用城市进行编辑或删除很有用。
void CityList::Show()
{
     list<City>::iterator i;
     int id = 1;     
     for(i = city_list.begin(); i != city_list.end(); i++)
     {
           if(i->name.find(filter) != std::string::npos)
           {
                     std::cout.width(4);
                     std::cout.fill('0');
                     std::cout << id << ".";
                     i->ShowCity();       
           }
           id++;
     }
}

Save():此方法遍历 CityList.city_list 并将文本保存到一个名为 city.txt 的文本文件中。此文件是此应用程序的数据库,并与程序位于同一文件夹中。
void CityList::Save()
{
     std::ofstream s("city.txt", std::ios::trunc);
     
     list<City>::iterator i;
     
     for(i = city_list.begin(); i != city_list.end(); i++)
     {     
           s << i->name << std::endl;       
           
     };
     
     s.close();
}

Load():此方法扫描文本文件行,创建 City 对象并使用 CityList.Add() 方法将其添加到 CityList 中。此方法在程序启动时用于加载初始城市列表。
void CityList::Load()
{
     std::ifstream s("city.txt");
     std::string aux = "";
    
     s >> aux;           
     while(!s.eof()) 
     {
         City *city = new City();
         city->name = aux;
         Add(city);            
         s >> aux;           
     };
     
     s.close();
}
Del(del_pos):它使用 del_pos 参数提供的位置从 CityList.city_list 中移除一个元素。用户查看由 CityList.Show() 显示的城市位置,其中第一个列表索引为 1(一)(但初始列表索引为零)。CityList.Del 方法负责删除正确的索引。
void CityList::Del(int del_pos)
{   
     list<City>::iterator i;
     i = city_list.begin();
     int aux = 0;
     
     while(i != city_list.end() && aux < del_pos - 1)
     {     
           i++;
           aux++;
           
     };
     
     city_list.erase(i);
}
Edit(edit_pos, new_name):它更改列表中 edit_pos 参数位置的 City 对象名称。与用户选择要删除的城市方式相同,用户也可以选择要更新的城市。用户还需要通过 new_name 参数告知新名称。
void CityList::Edit(int edit_pos, std::string new_name)
{   
     list<City>::iterator i;
     i = city_list.begin();
     int aux = 0;
     
     while(i != city_list.end() && aux < edit_pos - 1)
     {     
           i++;
           aux++;
           
     };
     
     i->name = new_name;
}
UserInterface 类负责与用户进行通信,向用户呈现屏幕和输入,以便用户对城市列表执行操作。

Header():此方法用于在与用户交互的屏幕中显示标题。此方法允许通过在一个点进行更改,使标题的任何更改都能反映在整个系统中。
void UserInterface::Header()
{
     std::cout << "City" << std::endl;
     std::cout << "----" << std::endl << std::endl;     
}
MainMenu(CityList):它接收一个 CityList 指针,该指针在程序执行期间用于保持城市列表的更新。此方法显示主菜单,并负责一个等待用户命令的循环。命令选项有
- [1] 新城市:添加一个新城市;
- [2] 报告:显示城市列表;
- [6] 退出:关闭程序。

void UserInterface::MainMenu(CityList *c)
{
 int option = 0;
 
 while(option != 6)
 {
     system("cls");
     Header();
     std::cout << "[1] New city" << std::endl;
     std::cout << "[2] Report" << std::endl;
     std::cout << "[6] Exit" << std::endl << std::endl;
     std::cout << "Enter 1-6 option:";
    
      std::cin >> option;
      switch(option)
      {
       case 1:
            frmNew(c);
            option = 0;
            break;
       case 2:
            frmReport(c);
            option = 0;
            break;
      }
 };
};
frmNew(CityList):这是用于显示用户界面并供用户输入新城市名称的方法。

void UserInterface::frmNew(CityList *c)
{
     system("cls");     
     std::string cityName = "";
     Header();
     std::cout << "City name: ";
     std::cin >> cityName;     
     
     City *city = new City();
     city->name = cityName;
     c->Add(city);
     std::cout << std::endl;
     system("pause");    
};
frmReport(CityList):此方法根据当前过滤器显示城市列表。(CityList.filter - 名称中包含此文本的城市)。此方法执行一个循环以等待用户命令。

frmReport 方法向用户显示城市在列表中的位置,指示哪个城市将接收命令:编辑(选项 2)或删除(选项 3)。选项 1 用于清除当前过滤器。

选项 4 允许用户输入文本作为过滤器。例如,如果用户输入“PIRA”,则会显示“PIRACICABA”和“ITAPIRA”等城市。最后,选项 6 将您带回主菜单。
void UserInterface::frmReport(CityList *c)
{
     int option = 0;
     int aux = -1;
     std::string new_name = "";
     while (option != 6)
     {
          system("cls");     
          Header();
          c->Show();
          std::cout << std::endl;
          std::cout << std::endl << "[1] Clear Filter ";
          std::cout << std::endl << "[2] Edit ";
          std::cout << std::endl << "[3] Delete ";
          std::cout << std::endl << "[4] Search ";
          std::cout << std::endl << "[6] Exit" << std::endl << std::endl;
          std::cout << "Enter 1-6 option:";
           std::cin >> option;     
           switch(option)
           {
               case 1:
                    c->filter = "";
                    break;
               case 2:
                    std::cout << std::endl <<  "Type city pos to be update: ";
                    std::cin >> aux;                    
                    std::cout << std::endl <<  "Type new city name: ";
                    std::cin >> new_name;
                    
                    c->Edit(aux, new_name);
                    c->Save();                         
                    std::cout << std::endl << "Updated successfully!" << std::endl;
                    system("PAUSE");
                    break;           
               case 3:
                    std::cout << std::endl <<  "Type city pos to be removed: ";
                    std::cin >> aux;
                    if(Util::Confirm("Confirm this exclusion operation?"))
                    {
                         c->Del(aux);
                         c->Save();                         
                         std::cout << std::endl << "Removed successfully!" << std::endl;
                         system("PAUSE");
                    };
                    break;      
                case 4:
                    std::cout << std::endl <<  "Type city name or part to be searched: ";
                    std::cin >> c->filter;
                    break;      
           };    
     };
};
关注点
这篇文章可以扩展更多,但主要目标已经实现
- 完成(开始和结束)一个 C++ 程序。也许不像程序员所希望的那样优雅,但程序已经用他最喜欢的语言完成了。
- 撰写这篇描述程序的文章。经验丰富的程序员可能会觉得这篇文章是个玩笑,但对于初学者来说,这篇文章或许能带来一些启发。
- 这项工作的其他重要之处是学习了关于 C++ 的一些小知识std::cout.width(3); std::cout << "12345"; 如果我的第一个理论是正确的,这段代码显示的结果应该是:“123”。但结果是“12345”。 cout.width的想法是指定最大输出字符数,但在文本输出大于指定字符数的情况下,文本会完全显示。与 cout.fill 结合使用,可以告知哪个字符将填充文本的右侧或左侧。请记住,cout.width和cout.fill只影响下一个cout输出。//Define the width to next text output std::cout.width(10); //Define completation character std::cout.fill('-'); //Completation orientation (left) and text output std::cout << left <<"12345"; std::cout << std::endl; std::cout.width(10); std::cout.fill('*'); //Text to be affected "ABCDE" //Text does not affected by width and fill std::cout << right <<"ABCDE" << "XGGG"; std::cout << std::endl;上面示例的输出是  cout 示例 cout 示例这些功能用于在城市列表中获得更好的视觉效果,使它们对齐显示。 - - cout.width 和 cout.fill:乍一看,cout.width方法似乎是用来定义输出中要显示的字符数量。但这个理论是错误的。考虑下面的代码
- - std::npos:在 std::string中,有一个名为find的方法,它在字符串中搜索文本,如果找到,则返回找到文本的位置。有时文本未找到。要检查此条件(文本未找到),find 方法返回std::npos,它表示一个可能因实现而异的值。std::npos表示t_size的最大大小。
- - list.erase:erase方法允许从迭代器或一对迭代器中删除列表元素。与此不同,remove方法使用一个值来删除列表项。
 
- - cout.width 和 cout.fill:乍一看,
- 啊!露西也很高兴。她正在使用这个程序,并且已经安排了会议讨论改进。也许这次会议之后我们会有新文章的素材。
历史
第一版完成于:2012 年 7 月 6 日。


