【问题标题】:Finding all occurrences of a character in a string查找字符串中所有出现的字符
【发布时间】:2011-10-11 13:26:34
【问题描述】:

我有需要从中提取值的逗号分隔字符串。问题是这些字符串永远不会是固定大小。所以我决定遍历逗号组并阅读中间的内容。为了做到这一点,我创建了一个函数,它返回每个出现在示例字符串中的位置。

这是一个聪明的方法吗?这被认为是坏代码吗?

#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;
}

【问题讨论】:

  • 对我来说这是完美的。虽然会有很多 C++ 程序员说“为什么要发明轮子”和“使用那个函数,不要自己写”。不管怎样,我不关心他们,我不了解你。但是,您的代码存在一个小问题。 i 不应该达到 sample.length(),所以你的 for 循环条件中应该有 i &lt; sample.length()
  • 是的,我刚刚解决了这个问题。此外,它需要是 .size() 而不是长度
  • 如果你以后要拆分字符串,你可能想看看this question
  • @Shahbaz:你刚刚证明了不要“重新发明轮子”的观点。当一个解决方案已经出现多年错误的可用解决方案时,为什么还要冒险将错误引入您的系统。

标签: c++ algorithm stl vector


【解决方案1】:
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;
}

正如目前所写的那样,如果我正确阅读了您的问题,这将只返回一个包含字符本身的 int 表示形式的向量,而不是它们的位置,这是您真正想要的。

替换这一行:

characterLocations.push_back(sample[i]);

用这一行:

characterLocations.push_back(i);

这应该会给你你想要的向量。

【讨论】:

  • 哦,是的。接得好!我忘记了。
  • 请把第一行改为vector findLocation(const string& sample, char findIt)。我们不想复制字符串
【解决方案2】:

如果我正在查看此内容,我会看到此内容并假设您真正想要做的是对字符串进行标记,并且已经有很好的方法可以做到这一点。

我见过的最好的方法是使用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,您也可以使用 getlineistringstream,如 this answer。从那个答案中复制一些:

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 和其他第三方库会更好吗?
  • myTkoens 应该是 myTokens&gt;&gt; 应该是 &gt; &gt;(除非 OP 使用 C++11)。我无法测试这段代码,但你知道如何处理两个或多个连续的分隔符吗?
  • 基本上我需要 12 项 csv 字符串中的两个值。使用我的函数并将字符串分配给我的函数返回的索引的子字符串不是更容易吗?
  • @chronoz 也许,取决于您最终是否需要的不仅仅是这两个项目。除了解决一般问题的代码之外,IMO 更简单,因为它已经为您完成了。另外——我添加了一个使用 std::getline 的示例,这是另一种不依赖于 boost 的标记化方式。
  • @DougT。你看看那两段代码就敢告诉我 boost 更好看!!!
【解决方案3】:

对我来说也不错,一条评论是关于变量和类型的命名。你调用你要返回的向量characterLocations,它的类型是int,而实际上你是在推回字符本身(类型为char)而不是它的位置。我不确定更大的应用程序是做什么的,但我认为将位置传回更有意义。或者做一个更千篇一律的字符串标记。

【讨论】:

    【解决方案4】:

    如果您的目的是查找出现的索引,那么下面的代码将更有效,因为在 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);
            }
        }
    }
    

    【讨论】:

    • 为什么返回向量不好?
    • @chronoz:实际上,在这种情况下,返回向量并没有那么糟糕,因为返回值优化会优化掉(可能很昂贵的)副本。但是,STL 方式是将结果写入类型为模板参数的输出迭代器。因此,函数的用户可以选择他想要结果的表示形式。
    • 亲爱的 chronoz,在 c++ 中,局部变量是从堆栈空间启动的。当一个函数结束时,它的堆栈空间正在被破坏(在你的情况下是 characterLocations),因为这个原因 c++ 返回了返回值的副本,因此变量将保持有效。在这种情况下,编译器可能(或可能不取决于编译选项)复制所有向量,这显然会减慢您的解决方案。亲爱的 Bjorn Pollex,您是对的(尤其是 stl 方式确实是解决该问题的好方法),但在我看来,最好不要依赖编译器行为,而是依赖您自己的代码。
    【解决方案5】:

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

    【讨论】:

      猜你喜欢
      • 2012-10-12
      • 1970-01-01
      • 1970-01-01
      • 2013-03-22
      • 1970-01-01
      • 1970-01-01
      • 2012-11-15
      相关资源
      最近更新 更多