【问题标题】:Parsing a string into an array for C++将字符串解析为 C++ 的数组
【发布时间】:2020-06-16 23:08:20
【问题描述】:

如何在 C++ 中将字符串解析为数组?

例如:"[1:2:3:4]cd/dvd PLDS DVD-RW DU8A6SH DU53 /dev/sr0"

我想在方括号[] 内获取一个整数数组。所以数组包含{1, 2, 3, 4}

以下是我编写的代码,但我不确定这是否是最有效的方法。

std::string test = "[1:2:3:4]cd/dvd  PLDS  DVD-RW DU8A6SH DU53  /dev/sr0";
int begin = test.find("[");
begin = begin + 1;
std::string sub = test.substr(begin,7);
std::replace(sub.begin(), sub.end(), ':', ' ');

std::vector<int> arr;
std::stringstream ss(sub);
int temp;
while (ss >> temp)
     arr.push_back(temp);

注意:"[" 之前不会出现某些内容。 "[" 将永远存在。这些数字将始终是一个数字。方括号内总是有四个整数。 "]" 将永远存在。文字总是跟在"]" 之后。

【问题讨论】:

  • 编辑您的问题并提供更详细的问题陈述。 [ 之前会有什么东西吗? [ 会一直存在吗?数字总是1个字符吗?分隔符是否总是:] 会一直存在吗?文字会一直跟随]吗?
  • 数组会有固定数量的元素,不是吗?在这种情况下,您甚至不需要/想要一个整数向量,而是一个 std::array,或者更好 - 具有有意义的字段名称的结构/类。
  • 在 [.是的, [ 将在场。是的,数字将永远是一位数。是的 ] 总是在场。文本始终跟在 ] 之后
  • 正如@einpoklum 所问的,数字是否总是4?你真的想要std::vector&lt;int&gt; 吗?也许其他选择可能会更好。无论如何,请编辑您的问题并在此处添加其他详细信息,不要将详细信息留在 cmets 上。
  • 是的。方括号内将有四个数字。我将编辑我的问题。谢谢@Costantino Grana

标签: c++ visual-c++ c++17


【解决方案1】:

由于您对substr() 的大小进行硬编码,这意味着数字具有固定的位置和宽度,在这种情况下您可以简单地使用它:

#include <string>
#include <array>

std::string test = "[1:2:3:4]cd/dvd  PLDS  DVD-RW DU8A6SH DU53  /dev/sr0";

std::array<int, 4> arr; // or: int arr[4]; pre-C++11
for(int i = 1, j = 0; i < 9; i += 2, ++j)
    arr[j] = test[i]-'0';

Live Demo

但是,如果不是这种情况,则不要假设结束 ] 的位置。使用find() 定位,例如:

std::string test = "[1:2:3:4]cd/dvd  PLDS  DVD-RW DU8A6SH DU53  /dev/sr0";

std::string::size_type begin = test.find('[') + 1;
std::string::size_type end = test.find(']', begin);
std::string sub = test.substr(begin, end - begin);

std::vector<int> arr;
std::istringstream iss(sub);
int temp;

while (iss >> temp) {
    arr.push_back(temp);
    iss.ignore();
}

Live Demo

如果您使用的是 C++17 或更高版本,则可以使用 std::string_viewstd::from_chars() 来减少内存开销,因此您不必为 sub 或 @ 分配单独的 std::string 987654333@,甚至可以使用std::istringstream,例如:

std::string test = "[1:2:3:4]cd/dvd  PLDS  DVD-RW DU8A6SH DU53  /dev/sr0";
std::string_view sv(test.data(), test.size());

std::string_view::size_type begin = sv.find('[') + 1;
std::string_view::size_type end = sv.find(']', begin);
std::string_view sub = sv.substr(begin, end - begin);

std::vector<int> arr;
const char *pBegin = sub.data(), *pEnd = pBegin + sub.size();
int temp;

while (pBegin < pEnd)
{
    auto [ptr, ec] = std::from_chars(pBegin, pEnd, temp);
    if (ec != std::errc()) break;
    arr.push_back(temp);
    pBegin = ptr + 1;
}

Live Demo

【讨论】:

  • 对于你写的第二个选项,你为什么不需要这个“std::replace(sub.begin(), sub.end(), ':', ' ')”?还是 istringstream 只是以某种方式获取整数?我是新手,所以任何帮助都会很好。
  • @Br0sk1 因为operator&gt;&gt; 在遇到不属于正在读取的类型的字符时停止读取。这包括空格。在我的示例中,operator&gt;&gt; 将在到达 : 字符或字符串末尾时停止读取。随后对iss.ignore() 的调用用于在再次调用operator&gt;&gt; 之前跳过该: 字符。
  • 现在更有意义了!非常感谢
【解决方案2】:

鉴于您添加的约束,您可以试试这个:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <vector>

int main() {
    std::string test = "[1:2:3:4]cd/dvd  PLDS  DVD-RW DU8A6SH DU53  /dev/sr0";

    std::vector<int> v(4);
    {
        char open_square_bracket, colon1, colon2, colon3, close_square_bracket;
        std::stringstream ss(test);
        ss >> open_square_bracket >> v[0] >> colon1 >> v[1] >> colon2 >> v[2] >> colon3 >> v[3] >> close_square_bracket;
    }

    copy(begin(v), end(v), std::ostream_iterator<int>(std::cout, ", "));
    std::cout << "\n";

    return 0;
}

当然,重要的部分是我放在块语句中的部分,以突出显示它。这使您还可以检查字符是否是它们应该是的。否则,如果您 100% 确定格式正确,则可以将其简化为

    std::vector<int> v(4);
    char c;
    std::stringstream ss(test);
    ss >> c >> v[0] >> c >> v[1] >> c >> v[2] >> c >> v[3] >> c;

你也可以将它包装在一个 for 循环中,但如果它总是 4 个元素,那何必呢?

当然,在这种情况下可以使用std::array&lt;&gt;

    std::array<int, 4> v;
    char c;
    std::stringstream ss(test);
    ss >> c >> v[0] >> c >> v[1] >> c >> v[2] >> c >> v[3] >> c;

ideone 上运行它。

【讨论】:

  • 非常感谢@Costantino Grana!我是 C++ 新手,我想了解有关字符串流的更多信息。 stringstream 是否知道 char open_square_bracket, ... 是 '[' 、 ':' 和 ']'?另外,你有什么理由在 std::vector v(4) 下面有 { }?
  • 不,正如我稍后写的那样,这只是为了检查是否是open_square_bracket=='[',然后是colon1==':' 等等。 {} 只是为了突出感兴趣的部分,并避免让变量 css 比需要的寿命更长。
  • @Br0sk1 "stringstream 是否知道 char open_square_bracket, ... 是 '[' 、 ':' 和 ']'?" - 不,它不知道.只是您要求它读取char,然后读取int,然后读取char,等等。由您来验证每个char 读取是你所期望的。
  • 此外,所有std::istream 版本都读取整数,直到下一个不能成为整数一部分的字符(空格、字母或标点符号)。所以当他们到达: 时,他们会停下来。所以下一个&gt;&gt; c 提取了: 字符。
  • @CostantinoGrana 谢谢!我从这个车队中学到了很多!
【解决方案3】:
int main()
{
    std::string str = "[1:2:3:4]aaa";
    std::vector<int> str_chars = {};

    for (unsigned int i = 0; i < str.size(); ++i)
    {
        char curr = str.at(i);
        if (str.at(i) == '[')
        {
            str_chars.push_back((str.at(i + 1) - '0'));
        }
        else if ((str.at(i) == ':'))
        {
            str_chars.push_back(str.at(i + 1) - '0');
        }
    }

    for (int i = 0; i < str_chars.size(); ++i)
    {
        printf("%d ", str_chars.at(i));
    }

    getchar();
    return 0;
}

【讨论】:

  • 把vector改成vector会不会有问题?
  • 您需要执行以下操作将字符转换为整数:char c = '1'; int c2 = c - '0'
  • 好吧,我已经编辑了代码给你整数而不是字符
  • 重复使用str.at(...) 太过分了,改用str[...]。此外,如果 i 位于字符串的最后一个字符处,str.at(i + 1) 将引发异常,因为循环在到达 ] 时不会停止
  • 确实,我只是在 1 分钟内完成了它。我也相信在做边界检查,所以我自己更频繁地使用它,但是 [] 也有效
猜你喜欢
  • 1970-01-01
  • 2012-11-23
  • 2011-12-06
  • 2017-02-02
  • 1970-01-01
  • 2013-03-30
  • 2022-06-24
  • 2010-10-03
  • 1970-01-01
相关资源
最近更新 更多