在字符串中查找一个字符的所有出现次数

5
我有一些逗号分隔的字符串,需要从中提取值。问题是这些字符串的大小永远不固定。所以我决定遍历逗号分组并读取它们之间的内容。为了做到这一点,我编写了一个函数,在示例字符串中返回每个出现位置。
这样做聪明吗?这被认为是糟糕的代码吗?
#include <string>
#include <iostream>
#include <vector>
#include <Windows.h>

using namespace std;

vector<int> findLocation(string sample, char findIt);

int main()
{
    string test = "19,,112456.0,a,34656";
    char findIt = ',';

    vector<int> results = findLocation(test,findIt);
    return 0;
}

vector<int> findLocation(string sample, char findIt)
{
    vector<int> characterLocations;
    for(int i =0; i < sample.size(); i++)
        if(sample[i] == findIt)
            characterLocations.push_back(sample[i]);

    return characterLocations;
}

1
对我来说,这是完美的。虽然会有很多C++程序员说“为什么要重新发明轮子”和“使用那个函数,不要自己编写”。无论如何,我不在乎他们,我不知道你怎么想。但是,你的代码有一个小问题。i不应该达到sample.length(),所以你的for循环条件应该是i < sample.length() - Shahbaz
是的,我刚刚修复了那个问题。另外,它需要使用 .size() 而不是 length。 - lodkkx
如果您之后要拆分字符串,您可能需要查看这个问题 - Kurt Stutsman
@Shahbaz:你刚刚证明了“不要重复造轮子”的观点。为什么要冒着引入漏洞的风险,当已经有一个解决方案存在并且已经经过多年的漏洞排除呢? - Michael Price
5个回答

14
vector<int> findLocation(string sample, char findIt)
{
    vector<int> characterLocations;
    for(int i =0; i < sample.size(); i++)
        if(sample[i] == findIt)
            characterLocations.push_back(sample[i]);

    return characterLocations;
}

按照目前的编写方式,这个代码将只返回一个包含字符本身的整数表示的向量,而不是它们的位置,如果我正确理解了您的问题,您实际上需要的是位置。

请替换此行:

characterLocations.push_back(sample[i]);

使用这行代码:

characterLocations.push_back(i);

那样应该能够给你所需要的向量。


1
哦,是的。好发现!我忘记了那个。 - lodkkx
5
请将顶行更改为 vector<int> findLocation(const string& sample, char findIt)。我们不想复制字符串。 - Totonga

6
如果我在审查这个代码,我会认为你真正想做的是将字符串分词,而且已经有好的方法可以做到这一点。我见过最好的方法是使用boost::tokenizer。它允许您指定字符串的分隔符,然后提供一个漂亮的迭代器接口来遍历每个值。
using namespace boost;
string sample = "Hello,My,Name,Is,Doug";
escaped_list_seperator<char> sep("" /*escape char*/, ","/*seperator*/, "" /*quotes*/)

tokenizer<escaped_list_seperator<char> > myTokens(sample, sep)

//iterate through the contents
for (tokenizer<escaped_list_seperator<char>>::iterator iter = myTokens.begin();
     iter != myTokens.end();
     ++iter)
{
    std::cout << *iter << std::endl;
}

输出:

Hello
My
Name
Is
Doug

如果您不想依赖boost,您也可以像this answer中所述使用istringstreamgetline。从那个答案中有些复制:
std::string str = "Hello,My,Name,Is,Doug";
std::istringstream stream(str);
std::string tok1;

while (stream)
{
    std::getline(stream, tok1, ',');
    std::cout << tok1 << std::endl;
}

输出:

 Hello
 My
 Name
 Is
 Doug

这可能不是您直接询问的内容,但我认为它涉及到您试图解决的总体问题。

那么,在大小约束并不重要的情况下,使用boost和其他第三方库会更好吗? - lodkkx
@chronoz 可能吧,这取决于你最终是否需要更多的东西。此外,解决一般问题的代码在我看来更简单,因为它已经为您完成了。另外 - 我添加了一个使用 std::getline 的示例,这是另一种不依赖于 boost 的惯用分词方式。 - Doug T.
1
@DougT。你刚才看了那两段代码,敢告诉我boost看起来更好吗!!! - Shahbaz
1
当我运行带有 getline() 的代码片段时,最后一个单词会被打印两次。它会打印这六个单词:Hello My Name Is Doug Doug。 - Sadman Sakib
这个答案中有关于该 bug 的解决方案。最后一个单词被打印了两次,这是因为 getline 已经到达文件末尾,但 tok 仍然保持不变。https://stackoverflow.com/questions/57458608/stdgetline-read-the-last-string-twice - Azzurro94
显示剩余2条评论

0

如果您的目的是找到出现的索引,则以下代码将更有效,因为在 c++ 中传递对象作为参数会导致对象被复制,这是不安全且效率较低的。特别是在此情况下,返回向量是最糟糕的实践,因此将其作为参数引用给出会更好。

#include <string>
#include <iostream>
#include <vector>
#include <Windows.h>

using namespace std;

vector<int> findLocation(string sample, char findIt);

int main()
{

    string test = "19,,112456.0,a,34656";
    char findIt = ',';

    vector<int> results;
    findLocation(test,findIt, results);
    return 0;
}

void findLocation(const string& sample, const char findIt, vector<int>& resultList)
{
    const int sz = sample.size();

    for(int i =0; i < sz; i++)
    {
        if(sample[i] == findIt)
        {
            resultList.push_back(i);
        }
    }
}

为什么返回一个向量不好? - lodkkx
1
@chronoz:实际上,在这种情况下返回向量并不那么糟糕,因为返回值优化将优化掉(可能昂贵的)复制。然而,STL的方式是将结果写入一个输出迭代器,其类型是模板参数。因此,函数的用户可以选择他想要的结果表示方式。 - Björn Pollex
1
亲爱的chronoz, 在c++中,局部变量是从堆栈空间初始化的。当函数结束时,它的堆栈空间被销毁(在您的情况下为characterLocations),因此c++返回返回值的副本,以便变量保持有效。在这种情况下,编译器可能会(或者不会,具体取决于编译选项)复制所有向量,这显然会减慢您的解决方案速度。 亲爱的Bjorn Pollex,你是对的(特别是stl方式确实是解决该问题的好方法),但我认为最好不要依赖于编译器行为,而是依靠自己的代码。 - hevi

0

我也觉得看起来不错,但是关于变量和类型的命名有一个评论。你把要返回的向量称为characterLocations,它的类型是int,但实际上你推回的是字符本身(类型为char),而不是它的位置。我不确定更大的应用是什么,但我认为传回位置会更有意义。或者使用更常规的字符串分词。


-1

它的智能程度也取决于您如何处理用逗号分隔的子字符串。在某些情况下,避免搜索和拆分,直接同时解析和处理字符串可能更好(例如更快,具有较小的内存要求),可能需要使用状态机。


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