getline() до конца ввода C

#c #linux

#c #linux

Вопрос:

Вот мой код. Предполагается, что он получает ввод до конца ввода и помещает этот ввод в строковые данные. Затем предполагается маркировать ввод, используя разделитель «#«. Затем я повторно вызываю свою функцию nexttoken() для сохранения токенов в переменных.

 std::istream_iterator<char> it(std::cin);
std::istream_iterator<char> end;
std::string data(it,end);
std::string delimiter = "#";
StringTokenizer strtok(data,delimiter);
std::string t1      = strtok.nextToken();
std::string t2      = strtok.nextToken();
  

Все это работает, когда я передаю файл через командную строку следующим образом:

 program.exe <testcase1.txt
  

testcase1.txt

 S A B #
S -> a A #
S -> z B #
A -> b B c B #
B -> d A #
##
  

вывод

 S A B 
a: 1
b: 1
c: 1
d: 1
z: 1
  

все проверяется и работает.

Моя проблема заключается в следующем: когда я нажимаю run в своей IDE, я могу ввести ввод вручную, но когда я это делаю, нет способа заставить программу принять ввод, кроме как если я нажмуctrl-z. Эта проблема также сохраняется в Linux, когда я передаю файл через терминал, он просто зависает там, позволяя мне вводить бесконечные строки.

вот уменьшенная версия моего кода, которая учитывает только 3 токена и проверяет только a, b и c

main.cpp

 #include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
#include <cstddef>
#include "StringTokenizer.h"

int countSubstring(const std::stringamp; str, const std::stringamp; sub)
{
    if (sub.length() == 0) return 0;
    int count = 0;
    for (size_t offset = str.find(sub); offset != std::string::npos;
     offset = str.find(sub, offset   sub.length()))
    {
          count;
    }
    return count;
}

int main(int argc, char* argv[1])
{

int task;

if (argc < 2)
{
    std::cout << "Error: missing argumentn";
    return 1;
}
task = atoi(argv[1]);
switch(task){

    case 0:
        {
std::istream_iterator<char> it(std::cin);
std::istream_iterator<char> end;
std::string data(it,end);
std::string delimiter = "#";
StringTokenizer strtok(data,delimiter);

   int a = 0;
   int b = 0;
   int c = 0;

//reading the first token and puting it in tk1
std::string t1      = strtok.nextToken();
std::string tk1(t1);
tk1.erase(0, tk1.find_first_not_of(" "));
tk1.erase(tk1.find_last_not_of(" ") 1);

// token 2 and 3 are different because 1 is always the same format
std::string t2      = strtok.nextToken();
std::string tk2(t2);

            if(countSubstring(tk2,"a") > 0)
    {
        a = a   1;
    }
            if(countSubstring(tk2,"b") > 0)
    {
         b=b    1;
    }
            if(countSubstring(tk2,"c") > 0)
    {
         c=c    1;
    }

std::string t3      = strtok.nextToken();
std::string tk3(t3);
if(countSubstring(tk3,"a") > 0)
    {
        a = a   1;
    }
            if(countSubstring(tk3,"b") > 0)
    {
         b=b    1;
    }
            if(countSubstring(tk3,"c") > 0)
    {
         c=c    1;
    }

// this is where the output is
std::cout << tk1 << std::endl;


    if(a > 0)
  {
      std::cout << "a: " << a <<std::endl;
  }
    if(b > 0)
  {
      std::cout << "b: " << b <<std::endl;
  }
    if(c > 0)
  {
      std::cout << "c: " << c <<std::endl;
  }

          }
  break;
  //////////////////////////////////////////////////
    case 1:
        break;
    case 2:
        break;

    default:
        std::cout << "Error: unrecognized task number " << task << "n";
        break;
}
return 0;
}
  

StringTokenizer.h

 #ifndef INCLUDE_STRINGTOKENIZER_H
#define INCLUDE_STRINGTOKENIZER_H


#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>


class StringTokenizer
{
public:
   StringTokenizer(const std::stringamp; _str, const std::stringamp; _delim);
  ~StringTokenizer(){};


   std::string nextToken();
   std::string nextToken(const std::stringamp; delim);


private:

   std::string  token_str;
   std::string  delim;
};

#endif
  

StringTokenizer.cpp

 #include "StringTokenizer.h"

StringTokenizer::StringTokenizer(const std::stringamp; _str, const std::stringamp; _delim)
{
   if ((_str.length() == 0) || (_delim.length() == 0)) return;

   token_str = _str;
   delim     = _delim;

   /*
     Remove sequential delimiter
   */
   unsigned int curr_pos = 0;

   while(true)
   {
      if ((curr_pos = token_str.find(delim,curr_pos)) != std::string::npos)
      {
         curr_pos  = delim.length();

         while(token_str.find(delim,curr_pos) == curr_pos)
         {
            token_str.erase(curr_pos,delim.length());
         }
      }
      else
       break;
   }

   /*
     Trim leading delimiter
   */
   if (token_str.find(delim,0) == 0)
   {
      token_str.erase(0,delim.length());
   }

   /*
     Trim ending delimiter
   */
   curr_pos = 0;
   if ((curr_pos = token_str.rfind(delim)) != std::string::npos)
   {
      if (curr_pos != (token_str.length() - delim.length())) return;
      token_str.erase(token_str.length() - delim.length(),delim.length());
   }
}




std::string StringTokenizer::nextToken()
{
   if (token_str.length() == 0)
     return "";

   std::string  tmp_str = "";
   unsigned int pos     = token_str.find(delim,0);

   if (pos != std::string::npos)
   {
      tmp_str   = token_str.substr(0,pos);
      token_str = token_str.substr(pos delim.length(),token_str.length()-pos);
   }
   else
   {
      tmp_str   = token_str.substr(0,token_str.length());
      token_str = "";
   }
   return tmp_str;
}


std::string StringTokenizer::nextToken(const std::stringamp; delimiter)
{
   if (token_str.length() == 0)
     return "";

   std::string  tmp_str = "";
   unsigned int pos     = token_str.find(delimiter,0);

   if (pos != std::string::npos)
   {
      tmp_str   = token_str.substr(0,pos);
      token_str = token_str.substr(pos   delimiter.length(),token_str.length() - pos);
   }
   else
   {
      tmp_str   = token_str.substr(0,token_str.length());
      token_str = "";
   }

   return tmp_str;
}
  

1. Как мне изменить свой код, чтобы он прекратил поиск ввода, когда я закончу печатать? или когда он может видеть, что ## был введен? (## отмечает конец ввода)

2: Возможно ли это вообще с моим текущим кодом?

И Linux, и моя IDE компилируются с помощью g

Ответ №1:

Вы используете входные потоки из std::cid для чтения данных, которые остановятся только тогда, когда вы дойдете до конца файла, поэтому вам необходимо завершить ввод с Ctrl-zпомощью в Windows и Ctrl-dв Linux.

Самое простое изменение — это чтение построчно и обработка их независимо. Это позволит вам прочитать маркер завершения ## и не продолжать дальше (при условии, что маркер на самом деле равен двум # , за которыми следует новая строка).

 std::string line;
while (std::getline(std::cin, line)) {
   if (line == "##") break;
   // process a single line
}
  

Если нет гарантии, что за разделителем следует одна строка, вам может потребоваться читать символ за символом, но это маловероятно.

Комментарии:

1. Вот где я запутался, как мне включить это в свой существующий код? Должен ли я удалить свой std::istream_iterator<char> it(std::cin); std::istream_iterator<char> end; и std::string data(it,end); в пользу этого? и какой код будет включен в цикл while?

2. @ddog: внутри цикла вам потребуется обработка одной строки. Вызов data(it,end) считывает весь файл , который вам не нужен. Если вам не нужны строки, а просто # вы можете передать это как разделитель std::getline , который будет примерно эквивалентен тому, что вы tokenizer делаете.

3. @dribeas я получил его для вывода первой строки и ## остановил ввод. Как бы я прочитал каждую строку в переменную, чтобы я мог проверить ее с помощью countSubstring, а затем вывести ее? Нравится strtok(line,delimiter) then t1 = strtok.nextToken()