按字符拆分字符串?

24
如何将类似于"102:330:3133:76531:451:000:12:44412"的字符串通过":"字符进行拆分,并将所有数字放入一个整数数组中(数字序列始终为8个元素长度)?最好不使用外部库,如boost。
此外,我想知道如何在处理字符串之前删除不需要的字符,例如"$"和"#"?

5
@CaptainObvlious,那不是一个充分的评论 - 请指定您正在使用的“精灵魔法”版本。 - Glenn Teitelbaum
@johny 差不多是同样的问题,不过我猜 OP 也不知道如何将字符串转换为数字。 - Lander
那老牌的strtok呢? - Einheri
9个回答

20

stringstream 可以完成这些操作。

  1. 将字符串拆分并存储到 int 数组中:

    string str = "102:330:3133:76531:451:000:12:44412";
    std::replace(str.begin(), str.end(), ':', ' ');  // replace ':' by ' '
    
    vector<int> array;
    stringstream ss(str);
    int temp;
    while (ss >> temp)
        array.push_back(temp); // done! now array={102,330,3133,76531,451,000,12,44412}
    
  2. 在处理字符串之前,从中删除不必要的字符,例如$#,就像上面处理:一样。

PS:上面的解决方案仅适用于不包含空格的字符串。如果要处理包含空格的字符串,请参考此处,该方法基于std::string::find()std::string::substr()


当我尝试运行你的代码时,出现了以下错误:(没有匹配这些操作数的运算符"==", 操作数类型为:char == const char [2])(无法将 "const char *" 类型的值分配给 "char" 类型的实体) - user2705775
@user2705775 已修复。 :) - herohuyongtao
你可以使用std::replace(str.begin(), str.end(), ':', ' ');来替换冒号,而不是手动进行替换。 - phuclv
5
这是一个非常具体的答案,如果字符串包含空格,则无法正常工作。 - Martin Massera
@MartinMassera 感谢您指出这一点。更新答案以整合此内容。谢谢。 - herohuyongtao

11
在C语言中,通常的做法是使用像其他人已经回答过的那样使用strtok。但是,strtok不像C++的风格,并且还存在安全隐患。在C++中标准的做法是使用std::istringstream
std::istringstream iss(str);
char c; // dummy character for the colon
int a[8];
iss >> a[0];
for (int i = 1; i < 8; i++)
    iss >> c >> a[i];

如果输入总是像这样具有固定数量的令牌,那么sscanf可能是另一个简单的解决方案。

std::sscanf(str, "%d:%d:%d:%d:%d:%d:%d:%d", &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8);

8

我以前也写过像这样的代码,并在Stack Overflow上找到了一个有关使用分隔符拆分字符串的问题。这是原始问题:链接

您可以结合std::stoi来构建向量。

std::vector<int> split(const std::string &s, char delim) {
    std::vector<int> elems;
    std::stringstream ss(s);
    std::string number;
    while(std::getline(ss, number, delim)) {
        elems.push_back(std::stoi(number));
    }
    return elems;
}

// use with:
const std::string numbers("102:330:3133:76531:451:000:12:44412");
std::vector<int> numbers = split(numbers, ':');

这里是一个工作的ideone示例


有没有一种简单的方法将它存储在数组中而不是向量中? - user2705775
@user2705775 std::copy(vector.begin(), vector.end(), std::begin(array)) - user1508519
1
@user2705775,将其存储在数组中的问题在于,在完全解析字符串之前,您似乎不知道字符数。如果您确实需要它,可以按照remyabel的方法进行,但我不太明白其中的意义。 - Lander

1

真的!没有精灵魔法

这个问题也在这里得到了解答

#include <cstring>
#include <iostream>
#include<cstdlib>
#include<vector>

int main() 
{
    char input[100] = "102:330:3133:76531:451:000:12:44412";
    char *token = std::strtok(input, ":");
    std::vector<int> v;

    while (token != NULL) {
        v.push_back( std::strtol( token, NULL, 10 ));
        token = std::strtok(NULL, ":");
    }

    for(std::size_t i =0 ; i < v.size() ; ++i)
        std::cout << v[i] <<std::endl;
}

演示 这里


1
为了删除字符“#”和“$”,您可以使用标准算法std::remove_if。但是请注意,如果存在以下字符串“12#34”,则删除“#”后,您将获得“1234”。如果您需要结果字符串看起来像“12 34”或“12:34”,那么最好使用std::replace_if而不是std::remove_if
下面是执行任务的示例代码。您需要包含头文件。
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>

int main()
{
    char s[] = "102:$$330:#3133:76531:451:000:$12:44412";

    std::cout << s << std::endl;

    char *p = std::remove_if( s, s + std::strlen( s ), 
        []( char c ) { return ( c == '$' || c == '#' ); } );
    *p = '\0';

    std::cout << s << std::endl;

    const size_t N = 8;
    int a[N];

    p = s;
    for ( size_t i = 0; i < N; i++ )
    {
        a[i] = strtol( p, &p, 10 );
        if ( *p == ':' ) ++p;
    }

    for ( int x : a ) std::cout << x << ' ';
    std::cout << std::endl;
}

输出结果是

102:$$330:#3133:76531:451:000:$12:44412
102:330:3133:76531:451:000:12:44412
102 330 3133 76531 451 0 12 44412

0

这里有一种方法...虽然不是最聪明的,但很容易编写(8次重复几乎需要循环)。这种解析方法非常有用,所以学习起来很好。 !(iss >> c) 确保字符串中没有尾随的非空白字符。

std::istringstream iss(the_string);
char c;
int n[8];
if (iss >> n[0] >> c && c == ':' &&
    iss >> n[1] >> c && c == ':' &&
    iss >> n[2] >> c && c == ':' &&
    iss >> n[3] >> c && c == ':' &&
    iss >> n[4] >> c && c == ':' &&
    iss >> n[5] >> c && c == ':' &&
    iss >> n[6] >> c && c == ':' &&
    iss >> n[7] && !(iss >> c))
    ...

1
为什么要检查 c == ':' - Beta
@Beta:通常认为检查输入是否符合预期是一个合理的做法...如果冒号丢失,这可能意味着输入中还有其他错误。例如,如果有人传递了“3.14 12.78 999.1 38.6”,则会解析为[3 14 2 78 99 1 8 6]。否则。(我真的很厌倦看到S.O.问题,如果人们正确检查输入和输出,就可以找到I/O问题)。 - Tony Delroy
我遇到了一个错误:“没有匹配这些操作数的运算符“>>”。操作数类型为:std::istringstream >> int” - user2705775
@user2705775:你有包含 #include <sstream> 吗? - Tony Delroy

0
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] ="102:330:3133:76531:451:000:12:44412";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str,":");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, ":");
  }
  return 0;
}

0

另一种解决方案是使用C++11中的正则表达式功能。

#include <algorithm>
#include <iostream>
#include <iterator>
#include <ostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>

int main()
{
    const std::string s = "102:330:3133:76531:451:000:12:44412";

    // Replace each colon with a single space
    const std::regex pattern(":");
    const std::string r = std::regex_replace(s, pattern, " ");

    std::istringstream ist(r);

    std::vector<int> numbers;
    std::copy(std::istream_iterator<int>(ist), std::istream_iterator<int>(),
        std::back_inserter(numbers));

    // We now have a vector of numbers

    // Print it out
    for (auto n : numbers)
    {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    return 0;
}

0
你可以使用 strtok() 函数来分割字符串,可能需要在 while 循环中使用。
当你得到单个字符串后,可以使用 atoi(xxx) 将其转换为整数。

为什么不应该使用atoi()函数? - phuclv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接