C ++ Как преобразовать подстроку в int - PullRequest
0 голосов
/ 09 января 2019

Что выводится на консоль:

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

Я не знаю, почему я получаю значения, которые возвращаются, поскольку они случайные, и для меня нет никакого смысла в том, как я получил эти значения.

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Вместо того, чтобы читать строку, а затем использовать 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" будет соответствовать вводу типа «игнорировать».

Чтобы это исправить, мы могли бы добавить небольшой цикл примерно так:

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 и компания рассматривают любые пробелы в строке формата как директиву, позволяющую пропустить серию всех последовательных пробелов во входных данных. Вы можете (конечно) изменить это так, чтобы он действовал как scanf и компания, если хотите, но такое поведение удивляет довольно многих людей, поэтому я думаю, это, вероятно, лучше, чем оно есть.

0 голосов
/ 09 января 2019

Изменение

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 имеет два аргумента: положение и длина подстроки.

То же самое относится и к вашим целям.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...