现在是 C++ 方法。这是使用面向对象的习语和现代 C++ 算法。
我们有数据和方法,它们以某种方式属于一起。为此,C++ 中有类(结构)。因此,您可以定义一个具有成员变量和方法的类,该类可以与类变量一起使用。一切都作为一个对象工作。
另外。该类知道如何读取或打印其值。只有班级应该知道这一点。这种智慧被封装了。
接下来,我们要搜索嵌入在字符串某处的有趣数据。该字符串始终包含某种模式。在您的情况下,您有 3 个整数和一个字符串作为有趣的数据,以及介于两者之间的一些分隔符,无论它们是什么。
为了匹配这些模式并搜索字符串中有趣的部分,C++ 提供了std::regex。它们非常强大,因此定义起来有点复杂。
在下面的示例中,我将使用const std::regex re(R"((\d+).*?(\d+).*?(\d+).*?([\w_]+))");。这定义了 4 组子匹配(在括号中)和介于两者之间的东西。所以任何分隔符、空格或任何可能的东西。
如果您想更严格,只需更改模式即可检测源数据中的错误。见const std::regex re(R"(\[(\d+)\,\ (\d+)\]\-(\d+)\-([\w_]+))");。这是一种更严格的方法。如果出现错误,将不会读取输入文件。或仅以有效数据开头。
请看下面的例子:
#include <string>
#include <regex>
#include <iterator>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <ios>
#include <iomanip>
std::istringstream testFile{ R"([1, 1]-3-Big_City
[1, 2] - 3 - Big_City
[1, 3] - 3 - Big_City
[2, 1] - 3 - Big_City
[2, 2] - 3 - Big_City
[2, 3] - 3 - Big_City
[2, 7] - 2 - Mid_City
[2, 8] - 2 - Mid_City
[3, 1] - 3 - Big_City
[3, 2] - 3 - Big_City
[3, 3] - 3 - Big_City
[3, 7] - 2 - Mid_City
[3, 8] - 2 - Mid_City
[7, 7] - 1 - Small_City)" };
const std::regex re(R"((\d+).*?(\d+).*?(\d+).*?([\w_]+))");
struct CityData
{
// Define the city's data
int xCoordinate{};
int yCoordinate{};
int cityId{};
std::string cityName{};
// Overload the extractor operator >> to read and parse a line
friend std::istream& operator >> (std::istream& is, CityData& cd) {
// We will read the line in this variable
std::string line{};
// Read the line and check, if it is OK
if (std::getline(is, line)) {
// Find the matched substrings
std::smatch sm{};
if (std::regex_search(line, sm, re)) {
// An convert them to students record
cd.xCoordinate = std::stoi(sm[1]);
cd.yCoordinate = std::stoi(sm[2]);
cd.cityId = std::stoi(sm[3]);
cd.cityName = sm[3];
}
else {
is.setstate(std::ios::failbit);
}
}
return is;
}
friend std::ostream& operator << (std::ostream& os, const CityData& cd) {
return os << cd.xCoordinate << ' ' << cd.yCoordinate << ' ' << cd.cityId;
}
};
constexpr int MinimumArrayDimension = 8;
int main()
{
// Define the variable cityData with the vectors range constructor. Read complete input file and parse data
std::vector<CityData> cityData{ std::istream_iterator<CityData>(testFile),std::istream_iterator<CityData>() };
// The following we are doing, because we want to print everything with the correct width
// Read the maximum x coordinate
const int maxRow = std::max(std::max_element (
cityData.begin(),
cityData.end(),
[](const CityData & cd1, const CityData & cd2) { return cd1.xCoordinate < cd2.xCoordinate; }
)->xCoordinate, MinimumArrayDimension);
// Read the maximum y coordinate
const unsigned int maxColumn = std::max(std::max_element(
cityData.begin(),
cityData.end(),
[](const CityData & cd1, const CityData & cd2) { return cd1.yCoordinate < cd2.yCoordinate; }
)-> yCoordinate, MinimumArrayDimension);
// Read the maximum city
const unsigned int maxCityID = std::max_element(
cityData.begin(),
cityData.end(),
[](const CityData & cd1, const CityData & cd2) { return cd1.cityId < cd2.cityId; }
)->cityId;
// Get the number of digits that we have here
const int digitSizeForRowNumber = maxRow > 0 ? (int)log10((double)maxRow) + 1 : 1;
const int digitSizeForColumnNumber = std::max(maxColumn > 0 ? (int)log10((double)maxColumn) + 1 : 1,
maxCityID > 0 ? (int)log10((double)maxCityID) + 1 : 1);
// Lambda function for printing the header and the footer
auto printHeaderFooter = [&]() {
std::cout << std::setw(digitSizeForColumnNumber) << "" << " #";
for (int i = 0; i <= (maxColumn+1)* (digitSizeForColumnNumber+1); ++i)
std::cout << '#';
std::cout << "#\n";
};
// Print the complete map
std::cout << "\n\n";
printHeaderFooter();
// Print all rows
for (int row = maxRow; row >= 0; --row) {
// Ptint the row number at the beginning of the line
std::cout << std::setw(digitSizeForColumnNumber) << row << " # ";
// Print all columns
for (int col = 0; col <= maxColumn; ++col)
{
// Find the City ID for the given row (y) and column (x)
std::vector<CityData>::iterator cdi = std::find_if(
cityData.begin(),
cityData.end(),
[row, col](const CityData & cd) { return cd.yCoordinate == row && cd.xCoordinate == col; }
);
// If we could find nothing
if (cdi == cityData.end()) {
// Print empty space
std::cout << std::setw(digitSizeForColumnNumber) << "" << ' ';
}
else {
// Print the CityID
std::cout << std::right << std::setw(digitSizeForColumnNumber) << cdi->cityId << ' ';
}
}
// Print the end of the line
std::cout << "#\n";
}
printHeaderFooter();
// Print the column numbers
std::cout << std::setw(digitSizeForColumnNumber) << "" << " ";
for (int col = 0; col <= maxColumn; ++col)
std::cout << std::right << std::setw(digitSizeForColumnNumber) << col << ' ' ;
// And, end
std::cout << "\n\n\n";
return 0;
}
请注意:main 读取文件并显示输出。
而且,因为我不能在 SO 上使用文件,所以我从“std::istringstream”读取数据。这和从文件中读取是一样的。