控制台显示的内容:
START(0,0)
GOAL(0,2)
ooox
xxoo
ooox
我希望能够将START
和GOAL
点存储为子字符串的整数,因为从外部文件读取了控制台上打印的内容。
我正在编写一个遍历2D网格的应用程序,其中x
代表被阻止的路径,o
代表未被阻止的路径。
我尝试使用.substr()
仅获取包含坐标对的字符串部分,并使用std::stoi()
将String类型转换为Int类型。
void Grid::loadFromFile(const std::string& filename){
std::string line, startPoint, goalPoint;
std::vector<std::string> grid;
int startX, startY, goalX, goalY;
std::ifstream file(filename);
if (!file.is_open()) return;
if (!std::getline(file, line)) return;
if (line.compare(0, 5, "START") != 0) return;
startPoint = line.substr(6,3);
startX = std::stoi(startPoint.substr(1,1));
startY = std::stoi(startPoint.substr(2,2));
if (!std::getline(file, line)) return;
if (line.compare(0, 4, "GOAL") != 0) return;
goalPoint = line.substr(5,3);
goalX = std::stoi(goalPoint.substr(1,1));
goalY = std::stoi(goalPoint.substr(2,2));
test = line.substr(7,1);
while (std::getline(file, line)) {
grid.push_back(line);
}
file.close();
std::cout << "Start: " << startPoint << "\n";
std::cout << "Goal: " << goalPoint << "\n";
std::cout << "Start X: " << startX << "\n";
std::cout << "Start Y: " << startY << "\n";
std::cout << "Goal X: " << goalX << "\n";
std::cout << "Goal Y: " << goalY << std::endl;
}
该代码的预期结果是打印正确的startX/Y
和goalX/Y
值。
我得到的结果:
Start: 0,0
Goal: 0,2
Start X: 1
Start Y: 162010192
Goal X: 0
Goal Y: 1543563378
我不知道为什么我会得到返回的值,因为它们是随机的,对我如何获得这些值没有意义。
答案 0 :(得分:3)
更改
startX = std::stoi(startPoint.substr(1,1));
startY = std::stoi(startPoint.substr(2,2));
到
startX = std::stoi(startPoint.substr(0,1));
startY = std::stoi(startPoint.substr(2,1));
请参见http://www.cplusplus.com/reference/string/string/substr/
substr
有两个参数:子字符串的位置和长度。
同样适用于您的目标得分。
答案 1 :(得分:2)
我不想读一个字符串,而是使用substr
来提取您关心的部分,而宁愿直接指定期望的输入格式。在C语言中,我可能会做这样的事情:
if (fscanf(infile, "START(%d,%d)", &startX, &startY) != 2)
// error in reading start point
if (fscanf(infile, "GOAL(%d,%d)", &goalX, &goalY) != 2)
// error reading goal point
在C ++中,至少在我看来,我们真的想编写的代码按此顺序排列:
input >> "START(">> startX >> "," >> startY >> ")\n";
input >> "GOAL(">> goalX >> "," >> goalY > ")\n";
因此,至少在我看来,问题是我们是否可以(如果可以的话)提供支持。答案是肯定的,我们可以做到(可能很明显-进行大量积累,然后说“对不起,但我们不能做到”,这毫无意义)。
为此,我们需要一个常量字符串的提取器。简单来说,它可能看起来像这样:
template <class charT>
std::basic_istream<charT> &operator>>(std::basic_istream<charT> &is, charT const *fmt) {
while (*fmt) {
if (*fmt != is.peek())
is.setstate(std::ios_base::failbit);
++fmt;
is.ignore(1);
}
return is;
}
因此,这基本上只是一次查看输入流中的一个字符,并将其与格式字符串中的当前字符进行比较。如果它们匹配,它只是从流中提取该字符并转到下一个字符。如果它们不匹配,它将设置失败位以表示提取失败。
这有一个缺点:流有一个skipws
位,如果已设置,我们希望在尝试执行其他任何操作之前先跳过空格。这可能应该在这里适用,因此类似infile >> "ignore"
的输入将匹配诸如“ ignore”的输入。
要解决此问题,我们可以添加一个小循环,如下所示:
while (std::isspace(is.peek()))
is.ignore(1);
...在尝试匹配字符串之前。不过,这还有另一个缺点:它始终使用当前的全局locale
-但是流可以充满自己的locale
,这应该适用于从该流读取数据。因此,要正确跳过空白,我们应该检索流的locale
,然后从该ctype
获取locale
构面,并使用它来确定某个东西是否为空白。不幸的是,执行此操作的代码比任何人都喜欢的代码更长,更复杂:
template <class charT>
std::basic_istream<charT> &operator>>(std::basic_istream<charT> &is, charT const *fmt) {
if (fmt == nullptr)
return is;
if (is.flags() & std::ios_base::skipws) {
std::locale const &loc = is.getloc();
if (std::has_facet<std::ctype<charT>>(loc)) {
auto const &ct = std::use_facet<std::ctype<charT>>(loc);
while (ct.is(std::ctype_base::blank, is.peek()))
is.ignore(1);
}
else
while (std::isspace(is.peek()))
is.ignore(1);
}
while (*fmt) {
if (*fmt != is.peek())
is.setstate(std::ios_base::failbit);
++fmt;
is.ignore(1);
}
return is;
}
至少到目前为止,我已经编写了此代码,因此它检索流的locale
,并使用其ctype
构面(如果有的话),但如果有locale
没有ctype方面(至少在理论上是可能的),它会退回到使用std::isspace
来确定某物是否为空白。可能存在争论的余地,那就是最好在这一点上失败,但我将这个问题再留一天。
完成后,我们可以阅读想要的内容:
int main() {
std::istringstream b("START(0, 0)\nGOAL(1,2)");
int startX, startY;
b >> "START(" >> startX >> "," >> startY >> ")";
std::cout << "start_x: " << startX << ", start_y: " << startY << "\n";
int goalX, goalY;
b >> "GOAL(" >> goalX >> "," >> goalY >> ")";
std::cout << "goal_x: " << goalX << ", goal_y: " << goalY << "\n";
}
请注意,此处的行为仍然与scanf
和公司的行为略有不同。特别是,这将在您指定的格式字符串的开头(如果设置了skipws
之前跳过空格,然后尝试匹配从字面上传递的字符串中的每个字符。
相比之下,scanf
和company将格式字符串中的任何空白都当作指令来跳过输入中所有连续空白的运行。当然,您可以(当然)将其更改为类似scanf
和公司的行为-但这种行为似乎使很多人感到惊讶,所以我认为是。