当前位置:  开发笔记 > 编程语言 > 正文

如何在C++中对字符串进行标记?

如何解决《如何在C++中对字符串进行标记?》经验,为你挑选了16个好方法。

Java有一个方便的拆分方法:

String str = "The quick brown fox";
String[] results = str.split(" ");

有没有一种简单的方法在C++中执行此操作?



1> Ferruccio..:

该升压标记生成器类可以使这种相当简单的事情:

#include 
#include 
#include 
#include 

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

    char_separator sep(", ");
    tokenizer< char_separator > tokens(text, sep);
    BOOST_FOREACH (const string& t, tokens) {
        cout << t << "." << endl;
    }
}

针对C++ 11进行了更新:

#include 
#include 
#include 

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

    char_separator sep(", ");
    tokenizer> tokens(text, sep);
    for (const auto& t : tokens) {
        cout << t << "." << endl;
    }
}


@puk - 这是C++头文件的常用后缀.(比如C头的`.h`)
要小心`char_separator`构造函数的第三个参数(`drop_empty_tokens`是默认值,替代是`keep_empty_tokens`).

2> Adam Pierce..:

这是一个非常简单的:

#include 
#include 
using namespace std;

vector split(const char *str, char c = ' ')
{
    vector result;

    do
    {
        const char *begin = str;

        while(*str != c && *str)
            str++;

        result.push_back(string(begin, str));
    } while (0 != *str++);

    return result;
}


这不是"最佳"答案,因为它仍然使用字符串文字,它是普通的C常量字符数组.我相信提问者问他是否可以标记后者引入的"字符串"类型的C++字符串.

3> Konrad Rudol..:

您可以使用该std::string::find方法轻松构建简单的案例.但是,看看Boost.Tokenizer.这很棒.Boost通常有一些非常酷的字符串工具.


可悲的是,并非所有项目都能提供增强功能.我将不得不寻找一个非提升的答案.
并非每个项目都对"开源"开放.我在受到严格监管的行业工作.真的,这不是问题.这只是生活中的一个事实.Boost并非随处可用.
@NonlinearIdeas另一个问题/答案根本不是关于开源项目.*any*项目也是如此.也就是说,我当然理解MISRA C这样的限制标准,但是你知道你无论如何都要从头开始构建所有东西(除非你碰巧找到一个兼容的库 - 很少见).无论如何,重点不在于"Boost不可用" - 而是您有特殊要求,几乎*任何*通用答案都不适合.
@Dmitry什么是"STL barf"?!并且整个社区都非常赞成更换C预处理器 - 事实上,有提议要做到这一点.但是你建议使用PHP或其他语言反而是向后迈出了一大步.

4> Mark..:

使用strtok.在我看来,没有必要建立一个围绕标记化的类,除非strtok没有为你提供你需要的东西.它可能没有,但是在用C和C++编写各种解析代码的15年多来,我总是使用strtok.这是一个例子

char myString[] = "The quick brown fox";
char *p = strtok(myString, " ");
while (p) {
    printf ("Token: %s\n", p);
    p = strtok(NULL, " ");
}

一些警告(可能不适合您的需要).字符串在此过程中被"销毁",这意味着EOS字符内嵌在分隔符中.正确使用可能需要您创建字符串的非const版本.您还可以在解析时更改分隔符列表.

在我看来,上面的代码比为它编写一个单独的类要简单得多,也更容易使用.对我来说,这是该语言提供的那些功能之一,并且它干净利落地完成了它.它只是一个"基于C"的解决方案.这很合适,很简单,而且你不必编写很多额外的代码:-)


不是我不喜欢C,但是strtok不是线程安全的,你需要确定你发送它的字符串包含一个空字符以避免可能的缓冲区溢出.
有strtok_r,但这是一个C++问题.
@tloach:在MS C++编译器中,strtok是线程安全的,因为在TLS(线程本地存储)上创建了内部静态变量(实际上它是编译器所依赖的)
@ahmed:线程安全意味着不仅仅是能够在不同的线程中运行该函数两次.在这种情况下,如果在strtok运行时修改了线程,则可以在整个strtok运行期间使字符串有效,但是strtok仍然会因为字符串发生变化而变得混乱,它现在已经超过了null字符,并且它将会继续读取内存,直到它获得安全违规或找到空字符.这是原始C字符串函数的问题,如果您没有指定遇到问题的某个长度.
strtok需要一个指向非const null终止的char数组的指针,这个数组不是在c ++代码中找到的常见生物...你最喜欢从std :: string转换成这个的方法是什么?

5> user35978..:

另一种快速方法是使用getline.就像是:

stringstream ss("bla bla");
string s;

while (getline(ss, s, ' ')) {
 cout << s << endl;
}

如果你愿意,你可以做一个简单的split()方法返回一个vector,这是非常有用的.


这很好但是必须记住,通过这样做,不考虑默认分隔符'\n'.这个例子可行,但是如果你使用的是:while(getline(inFile,word,''))其中inFile是包含多行的ifstream对象,你将得到funnny结果..
我在使用这种技术时遇到问题,字符串中的0x0A字符使得while循环过早退出.否则,这是一个简单而快速的解决方案.

6> KeithB..:

您可以使用流,迭代器和复制算法直接执行此操作.

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
  std::string str = "The quick brown fox";

  // construct a stream from the string
  std::stringstream strstr(str);

  // use stream iterators to copy the stream to the vector as whitespace separated strings
  std::istream_iterator it(strstr);
  std::istream_iterator end;
  std::vector results(it, end);

  // send the vector to stdout.
  std::ostream_iterator oit(std::cout);
  std::copy(results.begin(), results.end(), oit);
}


@Vadi:因为编辑其他人的帖子非常具有侵入性.@pheze:我更喜欢让`std`这样我知道我的对象来自哪里,这只是一种风格问题.
尽管"std ::"前缀令人恼火或丑陋,但最好将它们包含在示例代码中,以便完全清楚这些函数的来源.如果他们打扰你,那么在你窃取这个例子并将其声称为你自己之后用"使用"替换它们是微不足道的.
是的!他说的话!最佳实践是使用std前缀.毫无疑问,任何大型代码库都会拥有自己的库和命名空间,并且当您开始导致命名空间冲突时,使用"using namespace std"会让您头疼.
我发现那些std ::恼人的阅读..为什么不使用"使用"?
我理解你的理由,我认为如果它适合你,它实际上是一个很好的选择,但从教学的角度来看,我实际上同意pheze.更容易阅读和理解一个完全外来的例子,例如在顶部使用"using namespace std",因为它需要较少的工作来解释以下行...特别是在这种情况下,因为一切都来自标准库.您可以通过一系列"使用std :: string;"来轻松阅读和显示对象的来源.特别是因为功能太短了.
用户 - 因为有人可能会复制此示例,我们不希望他们使用'使用':)
@pheze:先生,为什么不编辑而不是抱怨?

7> Mr.Ree..:

没有进攻的乡亲,但对于这样一个简单的问题,你在做事情的方式太复杂了.使用Boost有很多原因.但对于这个简单的事情,就像用20#雪橇击中苍蝇一样.

void
split( vector & theStringVector,  /* Altered/returned value */
       const  string  & theString,
       const  string  & theDelimiter)
{
    UASSERT( theDelimiter.size(), >, 0); // My own ASSERT macro.

    size_t  start = 0, end = 0;

    while ( end != string::npos)
    {
        end = theString.find( theDelimiter, start);

        // If at end, use length=maxLength.  Else use length=end-start.
        theStringVector.push_back( theString.substr( start,
                       (end == string::npos) ? string::npos : end - start));

        // If at end, use start=maxSize.  Else use start=end+delimiter.
        start = (   ( end > (string::npos - theDelimiter.size()) )
                  ?  string::npos  :  end + theDelimiter.size());
    }
}

例如(对于Doug的案例),

#define SHOW(I,X)   cout << "[" << (I) << "]\t " # X " = \"" << (X) << "\"" << endl

int
main()
{
    vector v;

    split( v, "A:PEP:909:Inventory Item", ":" );

    for (unsigned int i = 0;  i < v.size();   i++)
        SHOW( i, v[i] );
}

是的,我们可以让split()返回一个新的向量,而不是传入一个.包装和重载是微不足道的.但是根据我正在做的事情,我经常发现重新使用预先存在的对象而不是总是创建新对象会更好.(只要我不忘记在中间清空矢量!)

参考:http://www.cplusplus.com/reference/string/string/.

(我最初写的回答Doug的问题:基于分隔符的C++字符串修改和提取(已关闭).但是由于Martin York用指针关闭了这个问题......我只是概括了我的代码.)


为什么要定义一个只在一个地方使用的宏.你的UASSERT如何比标准断言更好.将比较拆分成3个令牌,除了需要更多的逗号之外别无其他需要.
呃,为什么`std :: string`类不包含split()函数?

8> w.b..:

使用regex_token_iterators 的解决方案:

#include 
#include 
#include 

using namespace std;

int main()
{
    string str("The quick brown fox");

    regex reg("\\s+");

    sregex_token_iterator iter(str.begin(), str.end(), reg, -1);
    sregex_token_iterator end;

    vector vec(iter, end);

    for (auto a : vec)
    {
        cout << a << endl;
    }
}


这应该是排名靠前的答案.这是在C++> = 11中执行此操作的正确方法.

9> Raz..:

Boost具有强大的分割功能:boost :: algorithm :: split.

示例程序:

#include 
#include 

int main() {
    auto s = "a,b, c ,,e,f,";
    std::vector fields;
    boost::split(fields, s, boost::is_any_of(","));
    for (const auto& field : fields)
        std::cout << "\"" << field << "\"\n";
    return 0;
}

输出:

"a"
"b"
" c "
""
"e"
"f"
""



10> sivabudh..:

我知道您要求提供C++解决方案,但您可能会认为这有用:

Qt的

#include 

...

QString str = "The quick brown fox"; 
QStringList results = str.split(" "); 

在这个例子中,优于Boost的优势在于它是一个一对一的映射到你的帖子的代码.

在Qt文档中查看更多内容



11> vzczc..:

这是一个示例tokenizer类,可以执行您想要的操作

//Header file
class Tokenizer 
{
    public:
        static const std::string DELIMITERS;
        Tokenizer(const std::string& str);
        Tokenizer(const std::string& str, const std::string& delimiters);
        bool NextToken();
        bool NextToken(const std::string& delimiters);
        const std::string GetToken() const;
        void Reset();
    protected:
        size_t m_offset;
        const std::string m_string;
        std::string m_token;
        std::string m_delimiters;
};

//CPP file
const std::string Tokenizer::DELIMITERS(" \t\n\r");

Tokenizer::Tokenizer(const std::string& s) :
    m_string(s), 
    m_offset(0), 
    m_delimiters(DELIMITERS) {}

Tokenizer::Tokenizer(const std::string& s, const std::string& delimiters) :
    m_string(s), 
    m_offset(0), 
    m_delimiters(delimiters) {}

bool Tokenizer::NextToken() 
{
    return NextToken(m_delimiters);
}

bool Tokenizer::NextToken(const std::string& delimiters) 
{
    size_t i = m_string.find_first_not_of(delimiters, m_offset);
    if (std::string::npos == i) 
    {
        m_offset = m_string.length();
        return false;
    }

    size_t j = m_string.find_first_of(delimiters, i);
    if (std::string::npos == j) 
    {
        m_token = m_string.substr(i);
        m_offset = m_string.length();
        return true;
    }

    m_token = m_string.substr(i, j - i);
    m_offset = j;
    return true;
}

例:

std::vector  v;
Tokenizer s("split this string", " ");
while (s.NextToken())
{
    v.push_back(s.GetToken());
}



12> Parham..:

这是一个简单的仅限STL的解决方案(约5行!)std::find,std::find_first_not_of它使用并处理分隔符的重复(例如空格或句点),以及前导和尾随分隔符:

#include 
#include 

void tokenize(std::string str, std::vector &token_v){
    size_t start = str.find_first_not_of(DELIMITER), end=start;

    while (start != std::string::npos){
        // Find next occurence of delimiter
        end = str.find(DELIMITER, start);
        // Push back the token found into vector
        token_v.push_back(str.substr(start, end-start));
        // Skip all occurences of the delimiter to find new start
        start = str.find_first_not_of(DELIMITER, end);
    }
}

试试吧直播!


这是一个很好的但我认为你需要使用find_first_of()而不是find()才能使多个分隔符正常工作.
使用find_first_not_of查找起始位置时,将跳过@ user755921多个分隔符.

13> dbr..:

pystring是一个小型库,它实现了一堆Python的字符串函数,包括split方法:

#include 
#include 
#include "pystring.h"

std::vector chunks;
pystring::split("this string", chunks);

// also can specify a separator
pystring::split("this-string", chunks, "-");



14> DannyK..:

我发布了类似问题的答案.
不要重新发明轮子.我使用了许多库,我遇到的最快和最灵活的是: C++ String Toolkit Library.

这是一个如何使用它的例子,我已经发布了stackoverflow上的其他地方.

#include 
#include 
#include 
#include 

const char *whitespace  = " \t\r\n\f";
const char *whitespace_and_punctuation  = " \t\r\n\f;,=";

int main()
{
    {   // normal parsing of a string into a vector of strings
       std::string s("Somewhere down the road");
       std::vector result;
       if( strtk::parse( s, whitespace, result ) )
       {
           for(size_t i = 0; i < result.size(); ++i )
            std::cout << result[i] << std::endl;
       }
    }

    {  // parsing a string into a vector of floats with other separators
       // besides spaces

       std::string s("3.0, 3.14; 4.0");
       std::vector values;
       if( strtk::parse( s, whitespace_and_punctuation, values ) )
       {
           for(size_t i = 0; i < values.size(); ++i )
            std::cout << values[i] << std::endl;
       }
    }

    {  // parsing a string into specific variables

       std::string s("angle = 45; radius = 9.9");
       std::string w1, w2;
       float v1, v2;
       if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
       {
           std::cout << "word " << w1 << ", value " << v1 << std::endl;
           std::cout << "word " << w2 << ", value " << v2 << std::endl;
       }
    }

    return 0;
}



15> 小智..:

检查此示例.它可能会帮助你..

#include 
#include 

using namespace std;

int main ()
{
    string tmps;
    istringstream is ("the dellimiter is the space");
    while (is.good ()) {
        is >> tmps;
        cout << tmps << "\n";
    }
    return 0;
}



16> Jim In Texas..:

MFC/ATL有一个非常好的标记器.来自MSDN:

CAtlString str( "%First Second#Third" );
CAtlString resToken;
int curPos= 0;

resToken= str.Tokenize("% #",curPos);
while (resToken != "")
{
   printf("Resulting token: %s\n", resToken);
   resToken= str.Tokenize("% #",curPos);
};

Output

Resulting Token: First
Resulting Token: Second
Resulting Token: Third

推荐阅读
路人甲
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有