简体   繁体   English

如何在 C++ 中将字符串转换为向量

[英]How to convert string to vector in C++

If I have a string (from user) of "{1, 2, 3, 4, 5}", how would I convert that to a vector of {1, 2, 3, 4, 5} in C++?如果我有一个字符串(来自用户)“{1, 2, 3, 4, 5}”,我如何将其转换为 C++ 中的 {1, 2, 3, 4, 5} 向量?

I tried to get a string from the user by我试图通过以下方式从用户那里获取一个字符串

vector<int> input;
cin >> input;

but I got error:但我得到了错误:

./main.cpp:124:9: error: invalid operands to binary expression ('std::istream' (aka 'basic_istream<char>') and 'vector<int>')
    cin >> user_input;

A suggestion: convert your string into an input stream.建议:将您的字符串转换为输入 stream。
Try something like this:尝试这样的事情:

const std::string   input_data="{1, 2, 3, 4, 5}";
std::istringstream  input_stream(input_data);
char                c; // Used for ignoring characters.  
std::vector<int>    database;
int                 number;

// Ignore the first brace
input_stream >> c;

// Read in the first number
input_stream >> number;
database.push_back(number);

// Read in the separator;
input_stream >> c;


// Read in the next number
input_stream >> number;
database.push_back(number);

// ...

The test for the ending brace or end of input are left as an exercise for the OP.结束大括号或输入结束的测试留作 OP 的练习。 :-) :-)

I have this utility function I use to stream in specific characters like { and }我有这个实用程序 function 我使用特定字符 stream,例如{}

template<class e, class t>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e& cliteral) {
        e buffer;  //get buffer
        in >> buffer; //read data
        if (buffer != cliteral) //if it failed
                in.setstate(in.rdstate() | std::ios::failbit); //set the state
        return in;
}

And with that, you can use an istream_iterator to stream the int s that the user types directly into the vector:有了它,您可以使用istream_iterator来 stream 用户直接输入向量的int

std::cin >> '{';
std::vector<int> input(std::istream_iterator<int>(std::cin), {});
std::cin >> '}';

So, this is where a library of useful functions comes in. I keep quite a few.所以,这就是有用函数库的用武之地。我保留了很多。

First, we'll need something to range over a container (such as a string):首先,我们需要一些东西来覆盖容器(例如字符串):

#include <utility>

template <typename Iterator>
struct ranger : public std::pair <Iterator, Iterator>
{
  ranger( Iterator begin, Iterator end = Iterator() ) : std::pair <Iterator, Iterator> { begin, end } { }
  Iterator begin() { return this->first;  }
  Iterator end  () { return this->second; }
};

Next we'll want something to make iterating over a string with regular expressions easier:接下来我们想要一些东西来使使用正则表达式迭代字符串更容易:

#include <regex>
#include <string>

struct re_ranger : public std::regex, public ranger <std::sregex_iterator>
{
  template <typename RegEx>
  re_ranger( const RegEx& re, const std::string& s )
  : std::regex( re )
  , ranger( std::sregex_iterator( s.begin(), s.end(), *this ) )
  { }
};

And we will naturally want to have the ability to turn a string like "7" into an integer like 7 :我们自然希望能够将像"7"这样的字符串变成像7这样的 integer :

#include <optional>
#include <sstream>
#include <string>

template <typename T>
auto string_to( const std::string & s )
{
  T value;
  std::istringstream ss( s );
  return ((ss >> value) and (ss >> std::ws).eof())
    ? value
    : std::optional<T> { };
}

This makes selecting and converting the numbers in a string to a vector of integers stupidly simple:这使得选择字符串中的数字并将其转换为整数向量非常简单:

#include <iostream>
#include <vector>

int main()
{
  std::string input = "{1, 2, 3, 4, 5}";
  std::vector<int> xs;
  
  for (auto m : re_ranger( "[[:digit:]]+", input ))
    xs.emplace_back( *string_to<int>(m.str()) );

Since we are converting one way, we might as well be able to convert the other way.既然我们正在转换一种方式,我们也可以转换另一种方式。 Here's the freebie:这是免费赠品:

#include <sstream>
#include <string>

template <typename Iterator>
std::string join( Iterator begin, Iterator end, const std::string & separator = " " )
{
  std::ostringstream ss;
  if (begin != end)
  {
    ss << *begin++;
    while (begin != end)
      ss << separator << *begin++;
  }
  return ss.str();
}

template <typename Container>
std::string join( const Container & xs, const std::string & separator = " " )
{
  using std::begin;
  using std::end;
  return join( begin( xs ), end( xs ), separator );
}

Now we can finish off main() :现在我们可以完成main()

#include <iostream>
#include <numeric>
#include <vector>

int main()
{
  std::string input = "{1, 2, 3, 4, 5}";
  std::vector<int> xs;
  
  for (auto s : re_ranger( "[[:digit:]]+", input ))
    xs.emplace_back( *string_to<int>( s.str() ) );
  
  std::cout << join( xs, "+" ) 
    << " = " << std::accumulate( xs.begin(), xs.end(), 0 ) << "\n";
}

Output: Output:

1+2+3+4+5 = 15

PS.附言。 You should get user input as a string:您应该将用户输入作为字符串:

int main()
{
  std::string input;
  std::cout << "input? ";
  getline( std::cin, input );

Here is another way of parsing the input.这是另一种解析输入的方法。 It uses a helper type to expect a specific character in the input stream (and eat it).它使用辅助类型来期望输入 stream 中的特定字符(并吃掉它)。

#include <cctype>
#include <iostream>

struct expect
{
  char c;
  explicit expect( char c ) : c{c} { }
};

std::istream & operator >> ( std::istream & ins, const expect & c )
{
  if (!std::isspace( (unsigned char)c.c )) ins >> std::ws;
  if (ins.peek() == c.c) ins.get();
  else ins.setstate( std::ios::failbit );
  return ins;
}

Now we can write our input function. I'll overload >> for a vector of integers:现在我们可以写输入 function。我将重载>>以获得整数向量:

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

std::istream & operator >> ( std::istream & ins, std::vector<int> & xs )
{
  int x;
  xs.clear();
  if (!(ins >> expect( '{' ))) return ins;
  while (ins >> x)
  {
    xs.emplace_back( x );
    ins >> expect( ',' );
  }
  ins.clear();
  ins >> expect( '}' );
  return ins;
}

Notice how the function works: it expects specific input.注意 function 是如何工作的:它需要特定的输入。 If that input fails at any given time the stream will be set to a fail state and the function will return.如果该输入在任何给定时间失败,stream 将设置为失败 state,function 将返回。 Now we can use it much like I think you had planned in your question:现在我们可以像我认为您在问题中计划的那样使用它:

int main()
{
  std::string input = "{1, 2, 3, 4, 5}";
  
  std::vector<int> xs;
  std::istringstream ss( input );
  ss >> xs;
  
  for (int x : xs)
    std::cout << x << " ";
  std::cout << "\n";
}

Helper functions for the win!胜利的助手功能!


PS.附言。 There is a companion function/class named accept which does nearly the same thing as expect .有一个名为accept的伴随函数/类,它做的事情几乎与expect相同。 It just doesn't set the fail state if the desired character is not next in the input stream.如果所需字符不在输入 stream 中的下一个,它不会设置失败 state。

struct accept
{
  char c;
  explicit accept( char c ) : c{c} { }
};

std::istream & operator >> ( std::istream & ins, const accept & c )
{
  if (!std::isspace( (unsigned char)c.c )) ins >> std::ws;
  if (ins.peek() == c.c) ins.get();
  return ins;
}

These two functions are the basis for a lot of very useful parsing powers.这两个函数是许多非常有用的解析能力的基础。

C++ has since over 10 years a dedicated functionality for that. C++ 已经有超过 10 年的专用功能。 It is the它是

std::sregex_token_iterator

And since it is existing, and easy to handle, it should be used.既然它已经存在,而且易于处理,就应该使用它。 The result is very often a one liner.结果通常是一个班轮。

Please look at one potential solution:请看一个可能的解决方案:

#include <iostream>
#include <vector>
#include <regex>
#include <string>
#include <algorithm>

const std::regex re{ R"(\d+)" };

int main() {
    std::string s{ "{1, 2, 3, 4, 5}" };

    std::vector<int> result{};

    std::transform(std::sregex_token_iterator(s.begin(), s.end(), re), {}, std::back_inserter(result), [](const std::string& v) {return std::stoi(v); });

    for (const int i : result) std::cout << i << ' ';
}

If you additionally want to validate the input, you can also use a regex .如果您还想验证输入,您还可以使用regex

Then the solution would look like this:那么解决方案将如下所示:

#include <iostream>
#include <vector>
#include <regex>
#include <string>
#include <algorithm>

const std::regex re{ R"(\d+)" };
const std::regex rv{ R"(\{(\d+\,)*\d+\})" };

int main() {
    // Read string and check input
    if (std::string s{}; std::getline(std::cin, s) and std::regex_match(s, std::regex{ R"(\{(\d+\,)*\d+\})" })) {

        std::vector<int> result{};

        // Extract intgers
        std::transform(std::sregex_token_iterator(s.begin(), s.end(), re), {}, std::back_inserter(result), [](const std::string& v) {return std::stoi(v); });

        // Show debug output
        for (const int i : result) std::cout << i << ' ';
    }
    else std::cerr << "\n***Error: Invald input\n";
}

Input check can be relaxed with other regex es.使用其他regex可以放宽输入检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM