计算文件中的关键字

时间:2010-12-15 09:30:28

标签: c++

我正在开发一个程序,它会加载自己的.cpp文件并找到一些指定的关键字。问题是,如果字符串更长,我不知道如何识别字符串中的单词。 具体的例子: 我们正在寻找一个int,但如果有写的函数会怎么样:

int main(int argc, char* argv[])

?如果我把它作为一个字符串加载,它将是s1 =“int”,s2 =“main(int”等等,但是当我比较keywordInt =“int”和s2 =“main(int”,它们是不一样。我尝试了string :: find函数,但是,有人可以写一个代码“int countSheep(int,int)”,然后再找到一个int。

你能帮助我吗?

更新:嗯,我感觉非常愚蠢,但请...我有点找不到或理解那些词法分析器/解析器/语法荧光笔...而且我甚至不知道该找什么。我试图找到一些人来做的工作 - 将字符串标记并识别它是什么类型,但我仍然失败。你能再给我一个带头吗?

8 个答案:

答案 0 :(得分:3)

解析(任何)编程语言语法与“匹配单词”非常不同,但实际上在您的代码中找到您所寻找的信息是必要的:

int internalClass::interestingFunc(int arg1,
    internalClass::typeId intId, unsigned int b);    // int fun arg, intId unused

在此说明中,语言关键字 int出现三次,子串int出现九次,空格分隔的单词int你找到两次,空格/括号/逗号分隔的单词int你会发现四次。

所有这些必须要区分,而这不是通过简单的字符串拆分发生的。需要一个能够理解C / C ++结构的源代码解析器。

以下stackoverflow条目提供了一些线索:https://stackoverflow.com/questions/2318347

答案 1 :(得分:2)

您可以将起始索引作为可选的第二个参数传递给string::find

// Counts the number of occurrences of `keyword` in `str`.
static size_t 
count_keyword (const std::string& str,
               const std::string& keyword)
{
  size_t pos = 0, count = 0;
  const size_t search_len = keyword.length();
  pos = str.find (keyword, pos);
  while (pos != std::string::npos)
    {
      ++count;
      pos = str.find (keyword, pos + search_len);
    }
  return count;
}

// test
int
main (int argc, char** argv)
{
  std::cout << count_keyword (argv[1], argv[2]) << '\n';
  return 0;
}

$ ./test "int main(int, char**)" int
=> 2
$ ./test "int main(int, char**)" char
=> 1
$  ./test "int countSheep(int,int)" int
=> 3

答案 2 :(得分:0)

来自示例here

find()可以选择一个可选的位置参数。因此,最好的办法就是在给定行的循环中调用find(),直到找不到该关键字为止。

答案 3 :(得分:0)

如果遇到“(”,“)”或“,”则拆分字符串。 您可以在比较期间执行此操作:如果找到不需要的字符,请拆分字符串并将其添加到列表的后面。

答案 4 :(得分:0)

你可以在这里使用一个简单的正则表达式(\ w +)+,你就得到了一个id列表。

#define BOOST_REGEX_MATCH_EXTRA
#include <boost/regex.hpp>
#include <string>
#include <iostream>

int main()
{
    const boost::regex ids("(("
           "(int|long|void)" // id to search
           "|(\\w+)" // any other id
           "|(\\W+)" // nonword
           "+)+)");

    std::string line = "int main(long p_var, int p_v2)";
    std::map<std::string, int> wordsCount;
    boost::smatch result;
    if (boost::regex_search(line, result, ids, boost::match_extra))
    {
//     for (unsigned i = 1; i < result.size(); i++)
       {
//         std::cout << "i: " << i << ", res=" << result[i] << "|" << std::endl;
           for(unsigned j = 0; j < result.captures(3).size(); ++j)
           {
              std::cout << "Num:" << j << ", res=" << result.captures(3)[j] << "|" << std::endl;
              wordsCount[result.captures(3)[j]]++;
           }
       }
    }
    for (std::map<std::string, int>::const_iterator it = wordsCount.begin(); it != wordsCount.end(); ++it)
»       std::cout << "Number of '" << it->first << "' : " << it->second << std::endl;
}

将打印

Num:0, res=int|
Num:1, res=long|
Num:2, res=int|
Number of 'int' : 2
Number of 'long' : 1

答案 5 :(得分:0)

正如Muxecoid在评论中所说,这实际上取决于你是否希望计算任何单词的出现或者想要在“纯粹的代码”中限制自己(通过反对评论或字符串文字)。

int main(int argc, char* argv[])
{
  // int argc: the number of arguments
  // ^^^ ?

  std::string integration = "integration of int: " + argv[1];
  //                                        ^^^ ?
}

还有\的问题。一行\

std::string mySoLong\
  Identifier = "";

如果您想查看任何出现次数:

  • 如果以\
  • 结尾,则将该行与其后续行连接起来
  • 在每一行上迭代使用find
  • 为每次“点击”检查前一个和下一个字符不能出现在标识符([a-zA-Z0-9_] ...以及可能是$)中以避免“子串”效应

如果你想“更聪明”,那么你需要解析实际的C ++代码。你也不需要一个完整的解析器。只是可以跳过注释和字符串文字的东西就足够了,所以无论你是尝试使用现有的解析器还是自己编写代码,都可以选择。

可能还有一些通用的解析器可以被教导识别注释和字符串文字,并且足以完成你的任务,Notepad ++使用这样的解析器进行着色。

答案 6 :(得分:0)

#include <cctype>
#include <iostream>
#include <set>

int main()
{
    std::set<std::string> keywords;
    keywords.insert("int");
    keywords.insert("double");
    keywords.insert("while");
    keywords.insert("if");
    keywords.insert("else");
    // add the rest...

    int num_keywords = 0;

    std::string input;
    if (getline(std::cin, input, '\0'))
    {
        for (int i = 0; i < input.size(); ++i)
        {
            char c = input[i];
            if (isalpha(c) || c == '_')
            {
                // entering identifier...
                int from = i;
                while (++i < input.size() &&
                       (isalnum(c = input[i]) || c == '_'))
                    ;
                // std::cout << "? " << input.substr(from, i - from) << '\n';
                if (keywords.find(input.substr(from, i - from)) != keywords.end())
                    ++num_keywords;
            }
            else if (c == '"' || c == '\'')
            {
                // skip over string and character literals
                while (++i < input.size() &&
                        input[i] != c)
                    if (input[i] == '\\')
                        ++i;
            }
            else if (c == '/' && (i + 1 < input.size()) && input[i+1] == '/')
                while (++i < input.size() && input[i] != '\n')
                    ;
            // TODO: add case for /* too... 
        }
    }

    std::cout << num_keywords << '\n';
}

答案 7 :(得分:0)

这里的麻烦是你没有对你想要阅读的内容有一个很好的定义。

在这种情况下,我会定义一个代表标识符的类 然后定义运算符&gt;&gt;准确读入一个标识符并删除所有其他字符。然后你可以在常规算法中使用它:

您需要的标题:

#include <istream>
#include <string>
#include <map>
#include <algorithm>
#include <iostream>

简单的识别类

class Ident
{
    public:
        // If used where a std::string is needed
        // the object auto converts itself into a string
        // very usful.
        operator std::string const&() const { return data;}
    private:
        // The identifer is just a string. 
        // That is read by the appropriate operator >>
        friend std::istream& operator>>(std::istream& str, Ident& dest);
        std::string data;
};

运营商&gt;&gt;读取下一个标识符。

std::istream& operator>>(std::istream& str, Ident& dest)
{
    char x;
    //
    // Ignore any input characters that are not identifier.
    for(x = str.get(); !::isalnum(x); x= str.get())
    {
        if (!str)
            return str; // If we reach EOF exit
    }

    // We have the first letter.
    // Reset the identifier. Then loop to append
    dest.data   = "";
    do
    {
        dest.data += x;
        x = str.get();
    }
    while(str && ::isalnum(x));

    // done
    return str;
}

将它拼接在一起的主要示例。

int main()
{
    std::map<std::string,int>   count;

    // Use Ident just like you would a std::string
    // But because we have defined a special operator >>
    // it will enter the loop only after each identifier it reads.
    Ident     word;
    while(std::cin >> word)
    {
        count[word]++;
    }

    // Quick loop to print the results and show it worked correctly.
    for(std::map<std::string,int>::iterator loop = count.begin(); loop != count.end(); ++loop)
    {
        std::cout << loop->first << " = " << loop->second << "\n";
    }
}