如何用C++11的正则表达式统计一个字符串中的单词数?

时间:2023-01-07 10:54:44
如题
自己写了一段,不是很满意,请教各位,有没有更好的方法?

#include <iostream>
#include <regex>
#include <string>
#include <fstream>
using namespace std;

int countword(string& str)
{
try

int n = 0;
smatch m;
regex ex("\\b\\w*\\b");  
 
while(regex_search(str,m,ex))

++n;
str = m.suffix().str();
}
return n;
}
catch (regex_error e)
{
cout<<e.what()<<endl;
}
}
bool readfile(const string name,string& str)
{
ifstream in(name);
if(in.is_open())
{
char line[255] = "";
while(true)
{
in.getline(line,256);
str += line;
if(in.good())
{
str += '\n';
}
else
{
break;
}
}
return true;
}
else
{
return false;
}
}
int main(int argc,char* argv[])
{
if(2 != argc)
{
cout<<"argument error. eg. count.exe demo.txt"<<endl;
}
string str("");
if(readfile(argv[1],str))
{
cout<<str<<endl;
int n = countword(str);
cout<<"there are "<<n<<" words in "<<argv[1]<<endl;
}

return 0;
}

49 个解决方案

#1


不錯了。。。 如何用C++11的正则表达式统计一个字符串中的单词数?

#2


晕,都isfream了还用Char数组,自己给自己挖坑啊。

#3


如何用C++11的正则表达式统计一个字符串中的单词数?在C#里面估计一句就够了

#4


C++同样只需要一句的。

#5


典型的简单问题复杂化啊

#6


引用 4 楼 taodm 的回复:
C++同样只需要一句的。

哪一句?

#7


引用 5 楼 akirya 的回复:
典型的简单问题复杂化啊

你有更加简单的方法?能不能教教我?

#8


引用 2 楼 taodm 的回复:
晕,都isfream了还用Char数组,自己给自己挖坑啊。

不用char数组用什么?

#9


引用 8 楼 TouchStoneStudio 的回复:
引用 2 楼 taodm 的回复:晕,都isfream了还用Char数组,自己给自己挖坑啊。
不用char数组用什么?

都 #include<string> 了

#10


引用 7 楼 TouchStoneStudio 的回复:
引用 5 楼 akirya 的回复:典型的简单问题复杂化啊
你有更加简单的方法?能不能教教我?


#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hello.c" , "r" );
fscanf( fp , "%*[^a-zA-Z]" );
while( fscanf( fp , "%*[a-zA-Z]%*[^a-zA-Z]" ) >-1 )
{
n++;
}
fclose(fp);
printf( "%d\n" , n );
return 0 ;
}

#11


引用 10 楼 akirya 的回复:
引用 7 楼 TouchStoneStudio 的回复:引用 5 楼 akirya 的回复:典型的简单问题复杂化啊
你有更加简单的方法?能不能教教我?

#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hello.c" , "r" );
fscanf( fp , "%*[^a-zA……


#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hello.c" , "r" );
fscanf( fp , "%*[^a-zA-Z]" );
while( fscanf( fp , "%*[a-zA-Z]%*[^a-zA-Z]" ) >-1 )
{
n++;
}
fclose(fp);
printf( "%d\n" , n );
return 0 ;
}


#12


引用 11 楼 akirya 的回复:
引用 10 楼 akirya 的回复:引用 7 楼 TouchStoneStudio 的回复:引用 5 楼 akirya 的回复:典型的简单问题复杂化啊
你有更加简单的方法?能不能教教我?

#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hello.c" , "r" );
fsc……


嗯,确实非常简洁
我想再请教一下
fscanf( fp , "%*[a-zA-Z]%*[^a-zA-Z]" )
其中的%*[a-zA-Z]%*[^a-zA-Z]是什么意思?

另外,如果这个单词是由于连字符连接而成的,该如何处理啊?
比如,is-a是一个单词,而不是两个 

#13


while (instream >> word)
  count++
??

#14


引用 13 楼 ForestDB 的回复:
while (instream >> word)
  count++
??

这样很不准确
比如“i hate C++ , and you ?”
可能会统计出7个单词

#15


规则是人定的
is-a既可以是一个单词,也可以是两个单词。

#16


为什么要正则式啊?一个很简单事情。

#17


这个问题复杂度奇高!
试统计以下句子的单词数:
◆That's a problem!
◆这个问题复杂度奇高!
◆이 문장에서 몇 단어?
◆بعض الكلمات في هذه الجملة؟
如何用C++11的正则表达式统计一个字符串中的单词数?

#18


引用 15 楼 ForestDB 的回复:
规则是人定的
is-a既可以是一个单词,也可以是两个单词。

那现在的规则就是将is-a定义成一个单词,用你的fscanf该如何解决呢?

#19


引用 16 楼 wcg_jishuo3 的回复:
为什么要正则式啊?一个很简单事情。

很简单吗?
不如把你的方案写出来,大家参考参考?

#20


引用 17 楼 zhao4zhong1 的回复:
这个问题复杂度奇高!
试统计以下句子的单词数:
◆That's a problem!
◆这个问题复杂度奇高!
◆이 문장에서 몇 단어?
◆بعض الكلمات في هذه الجملة؟

哦,倒不一定要针对不同的语种
单单就英文而言,该如何解决呢?

#21


distance(sregex_iterator(。。。), sregex_iterator())

#22


那我的规则就是is-a是两个单词。

#23


引用 12 楼 TouchStoneStudio 的回复:
引用 11 楼 akirya 的回复:引用 10 楼 akirya 的回复:引用 7 楼 TouchStoneStudio 的回复:引用 5 楼 akirya 的回复:典型的简单问题复杂化啊
你有更加简单的方法?能不能教教我?

#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hel……

#include <stdio.h>
int main ( )
{
    int n=0;
    FILE* fp = fopen( "hello.c" , "r" );
    fscanf( fp , "%*[^a-zA-Z-]" );
    while( fscanf( fp , "%*[a-zA-Z-]%*[^a-zA-Z-]" ) >-1 )
    {
        n++;
    }
    fclose(fp);
    printf( "%d\n" , n );
    return 0 ;
}

#24


c/c++中有一个很古老的函数 strtok()就是干这个用的。

这么简单的情况就不要用正则表达式了,自己写一个循环也没多难。

int countword(char const* s)
{
    int n = 0;
    while(true)
    {
        while( isspace(*s) ) ++s;
        if( !isgraph(*s++)) break;
        while( isgraph(*s) ) ++s;
        ++n;
    }
    return n;
}

#25


补充一点,如果需要考虑标点符号的话,可以用strtok的delim参数,也可以把上例中的判断条件换成相应的is...()函数。(提示:可能要用到ispunct()和isalnum()。)

#26


引用 24 楼 dingqiang107 的回复:
c/c++中有一个很古老的函数 strtok()就是干这个用的。

这么简单的情况就不要用正则表达式了,自己写一个循环也没多难。

int countword(char const* s)
{
    int n = 0;
    while(true)
    {
        while( isspace(*s) ) ++s;
        i……

我怎么感觉用正则表达式实现起来还要简单一些??

#28


引用 26 楼 TouchStoneStudio 的回复:
我怎么感觉用正则表达式实现起来还要简单一些?? ……


不在于简单,而是可读性和易维护,代价就是效率低。看用在什么场合了。

实际上对于这么小的例子,用正则表达式实现并不简单。杀鸡用牛刀不一定好用。

#29


引用 28 楼 dingqiang107 的回复:
引用 26 楼 TouchStoneStudio 的回复:我怎么感觉用正则表达式实现起来还要简单一些?? ……

不在于简单,而是可读性和易维护,代价就是效率低。看用在什么场合了。

实际上对于这么小的例子,用正则表达式实现并不简单。杀鸡用牛刀不一定好用。

用正则很简单,只是lz把代码写的很麻烦而已

#30


引用 29 楼 akirya 的回复:
用正则很简单,只是lz把代码写的很麻烦而已 ……


你23楼的代码没考虑全各种情况,都写全了也不少。

比如说你没考虑数字,而且当文件以字母结束时的结果是错的。

#31


It's a hand-me-down.
↑这句里面到底有几个单词?

#32


LZ的问题激发了我的想象。
让我们从TCPL中的序章的例子聊起:

# include <stdio.h>
# define IN 0
# define OUT 1

int main()
{
    int c;
    int n = 0;
    int status = OUT;

    while ((c = getchar()) != EOF)
    {
        if (c == ' ' || c == '\t' || c == '\n')
        {
            if (status == IN)
                status = OUT;
        }
        else
        {
            if (status == OUT)
            {
                status = IN;
                n++;
            }
        }
    }

    printf("%d\n", n);
    return 0;
}

很明显这里判断所谓一个单词的方法,就是由<space><tab><newline>充当分隔符,它们所隔开的,就是词;注意,在这种情况,非<space><tab><newline>字符,都会认为是单词的一部分,显然离LZ的“规则”很远,但是这个“规则”很朴素,适合讨论问题。
暂时顿一顿,看看C++的版本:

# include <iostream>
# include <string>

using namespace std;

int main()
{
    string word;
    int n;

    while (cin >> word)
        n++;

    cout << n << endl;

    return 0;
}

显然C++的抽象层次高了许多,但明显,这里的“规则”并无发生明显的变化,cin >> word默认情况下,也是按上述“空白”规则来划分单词的。

由于本人C++水平有限,不便以C++展开讨论,所以将上述C++版本,转成了一个C的版本,好讨论:

# include <stdio.h>

int is_separator(int c)
{
    return c == ' ' || c == '\t' || c == '\n';
}

int get_word(FILE * input)
{
    int c;
    while ((c = fgetc(input)) != EOF && is_separator(c))
        ;
    if (c == EOF)
        return 0;
    else
    {
        ungetc(c, input);
        while ((c = fgetc(input)) != EOF && !is_separator(c))
            ;
        if (c != EOF)
            ungetc(c, input);
        return 1;
    }
}

int main()
{
    int n = 0;

    while (get_word(stdin))
    {
        n++;
    }

    printf("%d\n", n);
    return 0;
}

好,我们看到,c == ' ' || c == '\t' || c == '\n'这样一个逻辑判断,已经从第一个例子中的if语句,单独抽象出来了。
我们再重构一下,也许更容易理解:

int is_separator(int c)
{
    static const char separators[] = " \t\n";
    return strchr(separators, c) != NULL;
}

好,现在我们有了一个谓词is_separator,那么显然!is_separator就是单词部分。
我们可以很方便的通过separators的定义,来构造规则了。

那么这和第一个例子还有LZ的问题之间有什么关系呢?
个人的理解,第一个例子演示了基本的状态机,而后面C++和C的例子,显然是更高层次的抽象,特别是最后一个例子,我们通过构造一个“集合”,来构造规则,当然这个“集合”很朴素,直接用到了C的基本类型;再高级点,其实就是re了,re提供了更高级的抽象和弹性,来构造我们想要的“规则”,但基本想法,应该是差不多了,就是状态机了。
至于scanf的[]还有strtok之类,从某种程度来讲,都是状态机不同层次的一个抽象。
最后至于Unicode,可以认为是char的延伸,即规则复杂了,“集合”更大更难构造了,但状态机的想法,还是不变的。

以上,就是我想聊的东西,很肤浅,算是一点个人的心得。
万宗同源,这也许就是我喜欢C和第一个例子的原因吧。

如果给出separators[] = ",.? \t\n"
“i hate C++ ,and you ?”
也能给出5个单词了。

当然,LZ还能给出更多的反例,但是归根到底,就是规则和谓词的构造了,比如separators太多,我们可以定义chars集合,它的补集即为separators集合(谓词取反操作)。
不管怎样,总是可以暴力列举所有的字符的集合。

以上所有的讨论,都是建立在“给定一个字符,它要么是单词的一部分,要么是分隔符”这样一个基础规则上,不涉及到语义,否则“我讨厌C++,你呢?”有几个单词? 如何用C++11的正则表达式统计一个字符串中的单词数?

#33


引用 29 楼 akirya 的回复:
引用 28 楼 dingqiang107 的回复:引用 26 楼 TouchStoneStudio 的回复:我怎么感觉用正则表达式实现起来还要简单一些?? ……

不在于简单,而是可读性和易维护,代价就是效率低。看用在什么场合了。

实际上对于这么小的例子,用正则表达式实现并不简单。杀鸡用牛刀不一定好用。
用正则很简单,只是lz把代码写的很麻烦而已
      ……

请教,用正则表达式如何更好地实现??

#34


引用 30 楼 dingqiang107 的回复:
引用 29 楼 akirya 的回复:用正则很简单,只是lz把代码写的很麻烦而已 ……

你23楼的代码没考虑全各种情况,都写全了也不少。

比如说你没考虑数字,而且当文件以字母结束时的结果是错的。

啊?还有这个问题?差点采用了

#35


引用 31 楼 zhao4zhong1 的回复:
It's a hand-me-down.
↑这句里面到底有几个单词?

按照三个单词来计算,连字符连接的算一个

#36


引用 35 楼 TouchStoneStudio 的回复:
引用 31 楼 zhao4zhong1 的回复:
It's a hand-me-down.
↑这句里面到底有几个单词?
按照三个单词来计算,连字符连接的算一个

只要是以标点或空格分隔开的都应计算为一个单词?

#37


何必那么烦呢?
不知这个如何?
#include <iostream>
#include <string>
#include <ctype.h>
#include <string.h>

using std::cout;
using std::endl;
using std::string;

template<class T>
int WordNum(const T& str,int cef)
{
bool isnum=true;
int i;
int wordnum=0;

//跳过空格和非字母字符 
for( i=0; (i < cef) && (!isalpha(str[i])); i++ );

//开始计算
for( ; i < cef; i++ )
{
if( (!isalpha(str[i])) && isnum )
{
wordnum++;
isnum=false;
}
else if( isalpha(str[i]) )
isnum=true;
else
continue;
}

if( isnum )
wordnum++;
return wordnum; 
}

int main(int argc,char** argv)
{
string str;
int n;
str="fkfn fn bv,b,  b kvmbmvmm......fhfjd";

cout<<str<<"\nword num is=";
n=WordNum(str,str.size());
cout<<n<<endl;

char str1[]="hello word   !";
cout<<str1<<"\nword num is=";
n=WordNum(str1,strlen(str1));
cout<<n<<endl;

return 0;

#38


引用 30 楼 dingqiang107 的回复:
引用 29 楼 akirya 的回复:用正则很简单,只是lz把代码写的很麻烦而已 ……

你23楼的代码没考虑全各种情况,都写全了也不少。

比如说你没考虑数字,而且当文件以字母结束时的结果是错的。

那是不知道具体需求到底是什么而已,加上数字,也就改改正则表达式就行了,不会增加什么代码。

#39


引用 32 楼 ForestDB 的回复:
LZ的问题激发了我的想象。
让我们从TCPL中的序章的例子聊起:
C/C++ code?123456789101112131415161718192021222324252627282930# include <stdio.h># define IN 0# define OUT 1 int main(){    int c;    int n = 0;    int ……


分析得太详细了
谢谢你
我能不能把你这篇文章转载到我的blog,作为这问题的一些补充?
谢谢

#40


引用 36 楼 Only_phantasy 的回复:
引用 35 楼 TouchStoneStudio 的回复:引用 31 楼 zhao4zhong1 的回复:
It's a hand-me-down.
↑这句里面到底有几个单词?
按照三个单词来计算,连字符连接的算一个
只要是以标点或空格分隔开的都应计算为一个单词?

我的理解是这样
但是,标点有很多种啊,不太好处理

#41


引用 38 楼 oniisama 的回复:
引用 30 楼 dingqiang107 的回复:引用 29 楼 akirya 的回复:用正则很简单,只是lz把代码写的很麻烦而已 ……

你23楼的代码没考虑全各种情况,都写全了也不少。

比如说你没考虑数字,而且当文件以字母结束时的结果是错的。
那是不知道具体需求到底是什么而已,加上数字,也就改改正则表达式就行了,不会增加什么代码。


需求,比如就是简单实现Office Word的字数统计

#42


请便。 如何用C++11的正则表达式统计一个字符串中的单词数?

#43


引用 42 楼 ForestDB 的回复:
请便。

谢谢 

#44


引用 33 楼 TouchStoneStudio 的回复:
引用 29 楼 akirya 的回复:引用 28 楼 dingqiang107 的回复:引用 26 楼 TouchStoneStudio 的回复:我怎么感觉用正则表达式实现起来还要简单一些?? ……

不在于简单,而是可读性和易维护,代价就是效率低。看用在什么场合了。

实际上对于这么小的例子,用正则表达式实现并不简单。杀鸡用牛刀不一定好用。
用正则很简单,只是……



#include <algorithm>
#include <iostream>
#include<regex>
#include<fstream>

using namespace std;


int main( int argc, char* argv[] )
{
ifstream ifs( "ReadMe.txt" );
std::string s;
int n=0;
while( getline( ifs , s ) )
{
n += distance( sregex_iterator( s.begin() , s.end() , regex("(([a-zA-Z]+-[a-zA-Z]+)|([a-zA-Z]+))(?:\\b)") ) , sregex_iterator() ); 
}
cout<< n <<endl;
return 0;
}

#45


倒!一口大鲜血啊,竟然用getline,没看过effective stl啊。

#46


其实仅仅是统计单词数的话

ifstream ifs("file1.txt");
int wordCount=count_if(istream_iterator<string>(ifs),istream_iterator<string>(),[](const string &s){return s!=",";});

#47


引用 46 楼 oniisama 的回复:
其实仅仅是统计单词数的话
C/C++ code?12ifstream ifs("file1.txt");int wordCount=count_if(istream_iterator<string>(ifs),istream_iterator<string>(),[](const string &amp;s){return s!=",";});

这个只是无视逗号和空格,其他符号一律看作单词

#48


这个数字也当做单词

ifstream ifs("file1.txt");
regex r("\\w+");
int wordCount=count_if(istream_iterator<string>(ifs),istream_iterator<string>(),[&](const string &s){return regex_search(s,r);});

#49


引用 45 楼 taodm 的回复:
倒!一口大鲜血啊,竟然用getline,没看过effective stl啊。

如何用C++11的正则表达式统计一个字符串中的单词数?
赶紧找出effective stl看了下


#include <algorithm>
#include <iostream>
#include<regex>
#include<fstream>
 
using namespace std;
 
 
int main( int argc, char* argv[] )
{
string s( istreambuf_iterator<char>( ifstream( "ReadMe.txt" ) ) ,  istreambuf_iterator<char>() );
cout<< distance( sregex_iterator( s.begin() , s.end() 
, regex("(([a-zA-Z]+-[a-zA-Z]+)|([a-zA-Z]+))(?:\\b)") ) , sregex_iterator() ) <<endl;
    return 0;
}

#1


不錯了。。。 如何用C++11的正则表达式统计一个字符串中的单词数?

#2


晕,都isfream了还用Char数组,自己给自己挖坑啊。

#3


如何用C++11的正则表达式统计一个字符串中的单词数?在C#里面估计一句就够了

#4


C++同样只需要一句的。

#5


典型的简单问题复杂化啊

#6


引用 4 楼 taodm 的回复:
C++同样只需要一句的。

哪一句?

#7


引用 5 楼 akirya 的回复:
典型的简单问题复杂化啊

你有更加简单的方法?能不能教教我?

#8


引用 2 楼 taodm 的回复:
晕,都isfream了还用Char数组,自己给自己挖坑啊。

不用char数组用什么?

#9


引用 8 楼 TouchStoneStudio 的回复:
引用 2 楼 taodm 的回复:晕,都isfream了还用Char数组,自己给自己挖坑啊。
不用char数组用什么?

都 #include<string> 了

#10


引用 7 楼 TouchStoneStudio 的回复:
引用 5 楼 akirya 的回复:典型的简单问题复杂化啊
你有更加简单的方法?能不能教教我?


#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hello.c" , "r" );
fscanf( fp , "%*[^a-zA-Z]" );
while( fscanf( fp , "%*[a-zA-Z]%*[^a-zA-Z]" ) >-1 )
{
n++;
}
fclose(fp);
printf( "%d\n" , n );
return 0 ;
}

#11


引用 10 楼 akirya 的回复:
引用 7 楼 TouchStoneStudio 的回复:引用 5 楼 akirya 的回复:典型的简单问题复杂化啊
你有更加简单的方法?能不能教教我?

#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hello.c" , "r" );
fscanf( fp , "%*[^a-zA……


#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hello.c" , "r" );
fscanf( fp , "%*[^a-zA-Z]" );
while( fscanf( fp , "%*[a-zA-Z]%*[^a-zA-Z]" ) >-1 )
{
n++;
}
fclose(fp);
printf( "%d\n" , n );
return 0 ;
}


#12


引用 11 楼 akirya 的回复:
引用 10 楼 akirya 的回复:引用 7 楼 TouchStoneStudio 的回复:引用 5 楼 akirya 的回复:典型的简单问题复杂化啊
你有更加简单的方法?能不能教教我?

#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hello.c" , "r" );
fsc……


嗯,确实非常简洁
我想再请教一下
fscanf( fp , "%*[a-zA-Z]%*[^a-zA-Z]" )
其中的%*[a-zA-Z]%*[^a-zA-Z]是什么意思?

另外,如果这个单词是由于连字符连接而成的,该如何处理啊?
比如,is-a是一个单词,而不是两个 

#13


while (instream >> word)
  count++
??

#14


引用 13 楼 ForestDB 的回复:
while (instream >> word)
  count++
??

这样很不准确
比如“i hate C++ , and you ?”
可能会统计出7个单词

#15


规则是人定的
is-a既可以是一个单词,也可以是两个单词。

#16


为什么要正则式啊?一个很简单事情。

#17


这个问题复杂度奇高!
试统计以下句子的单词数:
◆That's a problem!
◆这个问题复杂度奇高!
◆이 문장에서 몇 단어?
◆بعض الكلمات في هذه الجملة؟
如何用C++11的正则表达式统计一个字符串中的单词数?

#18


引用 15 楼 ForestDB 的回复:
规则是人定的
is-a既可以是一个单词,也可以是两个单词。

那现在的规则就是将is-a定义成一个单词,用你的fscanf该如何解决呢?

#19


引用 16 楼 wcg_jishuo3 的回复:
为什么要正则式啊?一个很简单事情。

很简单吗?
不如把你的方案写出来,大家参考参考?

#20


引用 17 楼 zhao4zhong1 的回复:
这个问题复杂度奇高!
试统计以下句子的单词数:
◆That's a problem!
◆这个问题复杂度奇高!
◆이 문장에서 몇 단어?
◆بعض الكلمات في هذه الجملة؟

哦,倒不一定要针对不同的语种
单单就英文而言,该如何解决呢?

#21


distance(sregex_iterator(。。。), sregex_iterator())

#22


那我的规则就是is-a是两个单词。

#23


引用 12 楼 TouchStoneStudio 的回复:
引用 11 楼 akirya 的回复:引用 10 楼 akirya 的回复:引用 7 楼 TouchStoneStudio 的回复:引用 5 楼 akirya 的回复:典型的简单问题复杂化啊
你有更加简单的方法?能不能教教我?

#include <stdio.h>
int main ( )
{
int n=0;
FILE* fp = fopen( "hel……

#include <stdio.h>
int main ( )
{
    int n=0;
    FILE* fp = fopen( "hello.c" , "r" );
    fscanf( fp , "%*[^a-zA-Z-]" );
    while( fscanf( fp , "%*[a-zA-Z-]%*[^a-zA-Z-]" ) >-1 )
    {
        n++;
    }
    fclose(fp);
    printf( "%d\n" , n );
    return 0 ;
}

#24


c/c++中有一个很古老的函数 strtok()就是干这个用的。

这么简单的情况就不要用正则表达式了,自己写一个循环也没多难。

int countword(char const* s)
{
    int n = 0;
    while(true)
    {
        while( isspace(*s) ) ++s;
        if( !isgraph(*s++)) break;
        while( isgraph(*s) ) ++s;
        ++n;
    }
    return n;
}

#25


补充一点,如果需要考虑标点符号的话,可以用strtok的delim参数,也可以把上例中的判断条件换成相应的is...()函数。(提示:可能要用到ispunct()和isalnum()。)

#26


引用 24 楼 dingqiang107 的回复:
c/c++中有一个很古老的函数 strtok()就是干这个用的。

这么简单的情况就不要用正则表达式了,自己写一个循环也没多难。

int countword(char const* s)
{
    int n = 0;
    while(true)
    {
        while( isspace(*s) ) ++s;
        i……

我怎么感觉用正则表达式实现起来还要简单一些??

#27


#28


引用 26 楼 TouchStoneStudio 的回复:
我怎么感觉用正则表达式实现起来还要简单一些?? ……


不在于简单,而是可读性和易维护,代价就是效率低。看用在什么场合了。

实际上对于这么小的例子,用正则表达式实现并不简单。杀鸡用牛刀不一定好用。

#29


引用 28 楼 dingqiang107 的回复:
引用 26 楼 TouchStoneStudio 的回复:我怎么感觉用正则表达式实现起来还要简单一些?? ……

不在于简单,而是可读性和易维护,代价就是效率低。看用在什么场合了。

实际上对于这么小的例子,用正则表达式实现并不简单。杀鸡用牛刀不一定好用。

用正则很简单,只是lz把代码写的很麻烦而已

#30


引用 29 楼 akirya 的回复:
用正则很简单,只是lz把代码写的很麻烦而已 ……


你23楼的代码没考虑全各种情况,都写全了也不少。

比如说你没考虑数字,而且当文件以字母结束时的结果是错的。

#31


It's a hand-me-down.
↑这句里面到底有几个单词?

#32


LZ的问题激发了我的想象。
让我们从TCPL中的序章的例子聊起:

# include <stdio.h>
# define IN 0
# define OUT 1

int main()
{
    int c;
    int n = 0;
    int status = OUT;

    while ((c = getchar()) != EOF)
    {
        if (c == ' ' || c == '\t' || c == '\n')
        {
            if (status == IN)
                status = OUT;
        }
        else
        {
            if (status == OUT)
            {
                status = IN;
                n++;
            }
        }
    }

    printf("%d\n", n);
    return 0;
}

很明显这里判断所谓一个单词的方法,就是由<space><tab><newline>充当分隔符,它们所隔开的,就是词;注意,在这种情况,非<space><tab><newline>字符,都会认为是单词的一部分,显然离LZ的“规则”很远,但是这个“规则”很朴素,适合讨论问题。
暂时顿一顿,看看C++的版本:

# include <iostream>
# include <string>

using namespace std;

int main()
{
    string word;
    int n;

    while (cin >> word)
        n++;

    cout << n << endl;

    return 0;
}

显然C++的抽象层次高了许多,但明显,这里的“规则”并无发生明显的变化,cin >> word默认情况下,也是按上述“空白”规则来划分单词的。

由于本人C++水平有限,不便以C++展开讨论,所以将上述C++版本,转成了一个C的版本,好讨论:

# include <stdio.h>

int is_separator(int c)
{
    return c == ' ' || c == '\t' || c == '\n';
}

int get_word(FILE * input)
{
    int c;
    while ((c = fgetc(input)) != EOF && is_separator(c))
        ;
    if (c == EOF)
        return 0;
    else
    {
        ungetc(c, input);
        while ((c = fgetc(input)) != EOF && !is_separator(c))
            ;
        if (c != EOF)
            ungetc(c, input);
        return 1;
    }
}

int main()
{
    int n = 0;

    while (get_word(stdin))
    {
        n++;
    }

    printf("%d\n", n);
    return 0;
}

好,我们看到,c == ' ' || c == '\t' || c == '\n'这样一个逻辑判断,已经从第一个例子中的if语句,单独抽象出来了。
我们再重构一下,也许更容易理解:

int is_separator(int c)
{
    static const char separators[] = " \t\n";
    return strchr(separators, c) != NULL;
}

好,现在我们有了一个谓词is_separator,那么显然!is_separator就是单词部分。
我们可以很方便的通过separators的定义,来构造规则了。

那么这和第一个例子还有LZ的问题之间有什么关系呢?
个人的理解,第一个例子演示了基本的状态机,而后面C++和C的例子,显然是更高层次的抽象,特别是最后一个例子,我们通过构造一个“集合”,来构造规则,当然这个“集合”很朴素,直接用到了C的基本类型;再高级点,其实就是re了,re提供了更高级的抽象和弹性,来构造我们想要的“规则”,但基本想法,应该是差不多了,就是状态机了。
至于scanf的[]还有strtok之类,从某种程度来讲,都是状态机不同层次的一个抽象。
最后至于Unicode,可以认为是char的延伸,即规则复杂了,“集合”更大更难构造了,但状态机的想法,还是不变的。

以上,就是我想聊的东西,很肤浅,算是一点个人的心得。
万宗同源,这也许就是我喜欢C和第一个例子的原因吧。

如果给出separators[] = ",.? \t\n"
“i hate C++ ,and you ?”
也能给出5个单词了。

当然,LZ还能给出更多的反例,但是归根到底,就是规则和谓词的构造了,比如separators太多,我们可以定义chars集合,它的补集即为separators集合(谓词取反操作)。
不管怎样,总是可以暴力列举所有的字符的集合。

以上所有的讨论,都是建立在“给定一个字符,它要么是单词的一部分,要么是分隔符”这样一个基础规则上,不涉及到语义,否则“我讨厌C++,你呢?”有几个单词? 如何用C++11的正则表达式统计一个字符串中的单词数?

#33


引用 29 楼 akirya 的回复:
引用 28 楼 dingqiang107 的回复:引用 26 楼 TouchStoneStudio 的回复:我怎么感觉用正则表达式实现起来还要简单一些?? ……

不在于简单,而是可读性和易维护,代价就是效率低。看用在什么场合了。

实际上对于这么小的例子,用正则表达式实现并不简单。杀鸡用牛刀不一定好用。
用正则很简单,只是lz把代码写的很麻烦而已
      ……

请教,用正则表达式如何更好地实现??

#34


引用 30 楼 dingqiang107 的回复:
引用 29 楼 akirya 的回复:用正则很简单,只是lz把代码写的很麻烦而已 ……

你23楼的代码没考虑全各种情况,都写全了也不少。

比如说你没考虑数字,而且当文件以字母结束时的结果是错的。

啊?还有这个问题?差点采用了

#35


引用 31 楼 zhao4zhong1 的回复:
It's a hand-me-down.
↑这句里面到底有几个单词?

按照三个单词来计算,连字符连接的算一个

#36


引用 35 楼 TouchStoneStudio 的回复:
引用 31 楼 zhao4zhong1 的回复:
It's a hand-me-down.
↑这句里面到底有几个单词?
按照三个单词来计算,连字符连接的算一个

只要是以标点或空格分隔开的都应计算为一个单词?

#37


何必那么烦呢?
不知这个如何?
#include <iostream>
#include <string>
#include <ctype.h>
#include <string.h>

using std::cout;
using std::endl;
using std::string;

template<class T>
int WordNum(const T& str,int cef)
{
bool isnum=true;
int i;
int wordnum=0;

//跳过空格和非字母字符 
for( i=0; (i < cef) && (!isalpha(str[i])); i++ );

//开始计算
for( ; i < cef; i++ )
{
if( (!isalpha(str[i])) && isnum )
{
wordnum++;
isnum=false;
}
else if( isalpha(str[i]) )
isnum=true;
else
continue;
}

if( isnum )
wordnum++;
return wordnum; 
}

int main(int argc,char** argv)
{
string str;
int n;
str="fkfn fn bv,b,  b kvmbmvmm......fhfjd";

cout<<str<<"\nword num is=";
n=WordNum(str,str.size());
cout<<n<<endl;

char str1[]="hello word   !";
cout<<str1<<"\nword num is=";
n=WordNum(str1,strlen(str1));
cout<<n<<endl;

return 0;

#38


引用 30 楼 dingqiang107 的回复:
引用 29 楼 akirya 的回复:用正则很简单,只是lz把代码写的很麻烦而已 ……

你23楼的代码没考虑全各种情况,都写全了也不少。

比如说你没考虑数字,而且当文件以字母结束时的结果是错的。

那是不知道具体需求到底是什么而已,加上数字,也就改改正则表达式就行了,不会增加什么代码。

#39


引用 32 楼 ForestDB 的回复:
LZ的问题激发了我的想象。
让我们从TCPL中的序章的例子聊起:
C/C++ code?123456789101112131415161718192021222324252627282930# include <stdio.h># define IN 0# define OUT 1 int main(){    int c;    int n = 0;    int ……


分析得太详细了
谢谢你
我能不能把你这篇文章转载到我的blog,作为这问题的一些补充?
谢谢

#40


引用 36 楼 Only_phantasy 的回复:
引用 35 楼 TouchStoneStudio 的回复:引用 31 楼 zhao4zhong1 的回复:
It's a hand-me-down.
↑这句里面到底有几个单词?
按照三个单词来计算,连字符连接的算一个
只要是以标点或空格分隔开的都应计算为一个单词?

我的理解是这样
但是,标点有很多种啊,不太好处理

#41


引用 38 楼 oniisama 的回复:
引用 30 楼 dingqiang107 的回复:引用 29 楼 akirya 的回复:用正则很简单,只是lz把代码写的很麻烦而已 ……

你23楼的代码没考虑全各种情况,都写全了也不少。

比如说你没考虑数字,而且当文件以字母结束时的结果是错的。
那是不知道具体需求到底是什么而已,加上数字,也就改改正则表达式就行了,不会增加什么代码。


需求,比如就是简单实现Office Word的字数统计

#42


请便。 如何用C++11的正则表达式统计一个字符串中的单词数?

#43


引用 42 楼 ForestDB 的回复:
请便。

谢谢 

#44


引用 33 楼 TouchStoneStudio 的回复:
引用 29 楼 akirya 的回复:引用 28 楼 dingqiang107 的回复:引用 26 楼 TouchStoneStudio 的回复:我怎么感觉用正则表达式实现起来还要简单一些?? ……

不在于简单,而是可读性和易维护,代价就是效率低。看用在什么场合了。

实际上对于这么小的例子,用正则表达式实现并不简单。杀鸡用牛刀不一定好用。
用正则很简单,只是……



#include <algorithm>
#include <iostream>
#include<regex>
#include<fstream>

using namespace std;


int main( int argc, char* argv[] )
{
ifstream ifs( "ReadMe.txt" );
std::string s;
int n=0;
while( getline( ifs , s ) )
{
n += distance( sregex_iterator( s.begin() , s.end() , regex("(([a-zA-Z]+-[a-zA-Z]+)|([a-zA-Z]+))(?:\\b)") ) , sregex_iterator() ); 
}
cout<< n <<endl;
return 0;
}

#45


倒!一口大鲜血啊,竟然用getline,没看过effective stl啊。

#46


其实仅仅是统计单词数的话

ifstream ifs("file1.txt");
int wordCount=count_if(istream_iterator<string>(ifs),istream_iterator<string>(),[](const string &s){return s!=",";});

#47


引用 46 楼 oniisama 的回复:
其实仅仅是统计单词数的话
C/C++ code?12ifstream ifs("file1.txt");int wordCount=count_if(istream_iterator<string>(ifs),istream_iterator<string>(),[](const string &amp;s){return s!=",";});

这个只是无视逗号和空格,其他符号一律看作单词

#48


这个数字也当做单词

ifstream ifs("file1.txt");
regex r("\\w+");
int wordCount=count_if(istream_iterator<string>(ifs),istream_iterator<string>(),[&](const string &s){return regex_search(s,r);});

#49


引用 45 楼 taodm 的回复:
倒!一口大鲜血啊,竟然用getline,没看过effective stl啊。

如何用C++11的正则表达式统计一个字符串中的单词数?
赶紧找出effective stl看了下


#include <algorithm>
#include <iostream>
#include<regex>
#include<fstream>
 
using namespace std;
 
 
int main( int argc, char* argv[] )
{
string s( istreambuf_iterator<char>( ifstream( "ReadMe.txt" ) ) ,  istreambuf_iterator<char>() );
cout<< distance( sregex_iterator( s.begin() , s.end() 
, regex("(([a-zA-Z]+-[a-zA-Z]+)|([a-zA-Z]+))(?:\\b)") ) , sregex_iterator() ) <<endl;
    return 0;
}