根据代码行从文本文件中获取函数名称

Get function name from text file base on line of code

我有一个名为 'MyCpp.cpp' 的 C++ 源代码,它位于:C:\MyCpp.cpp

...
5 string CplusplusWarningFunction()
6 {
7   int a = 69;
8   int b = a + 1;
9   a = b;
10  b = 69;
11  return "42 is the answer";
12 }
...

现在我想写一个像这样的 C# 函数

void CodeAnalyzer()
{
    string path = @"C:\MyCpp.cpp";
    int line = 11;
    IEnumerable<string> loc = File.ReadLines(path);

    string code = loc.ElementAt(line-1);//Yes, it is 'return "42 is the answer";', good job!
    string functionFullName = "???";//This suppose to be the string 'CplusplusWarningFunction()', but How to do it???
    MessageBox.Show(string.Format("The code {0} at line {1} is in function {2}",code,line,functionFullName));
}

如何获取函数名来替换字符串“???”在上面的 C# 代码中?

我知道这是可以做到的,因为 MS 的 FxCop 可以做到(FxCop 分析编译后的目标代码,而不是原始源代码)。好吧,如果编译后的目标代码可以完成,为什么不使用原始源代码。

而Visual Studio可以像下图这样,希望有办法访问Visual Studio API:

感谢阅读。

鉴于只有一个功能,每一行你都可以尝试

//do this in a foreach loop that will iterate through every line
string functionName;
if (line.Split(' ').Where(x => x.Contains("()") && !x.Contains(".")).Count() > 0)
{
    functionName = line.Split(' ').Where(x => x.Contains("()") && !x.Contains(".")).First();
}

现在这将适用于您的示例代码,但这也是非常 初级的,我相信在很多情况下这都行不通。不过,这可能不是一个糟糕的起点。

我想出了这样的代码,到目前为止还不错。

class CppFunction
{
    public string FunctionName { get; set; }
    public int StartLine { get; set; }
    public int EndLines { get; set; }
}

List<CppFunction> AnalyzeCpp(string path)
{
    List<CppFunction> lstCppFunc = new List<CppFunction>();

    IEnumerable<string> loc = File.ReadLines(path, encode);

    string[] locNoCom = RemoveComment(loc);
    RemoveIfdefineDebug(locNoCom);
    int level = 0;

    CppFunction crtFunc = null;
    int lineCount = 0;
    StringBuilder builder = new StringBuilder();
    bool startName = false;
    string builderToString;
    string lastLine = "";
    foreach (string line in locNoCom)
    {
        lineCount++;
        if (string.IsNullOrWhiteSpace(line))
        {
            lastLine = line;
            continue;
        }

        if (level <= 0)
        {
            if (line.Contains('('))
            {
                crtFunc = new CppFunction();
                if (line.Trim().IndexOf('(') == 0)
                    builder.Append(lastLine);
                builder.AppendLine(line);
                crtFunc.StartLine = lineCount;
                startName = true;
            }
            if (startName)
            {
                builderToString = builder.ToString();
                if (line != builderToString.Replace("\r\n",string.Empty))
                    builder.AppendLine(line);
                if (line.Contains(')'))
                {
                    startName = false;
                    if (crtFunc != null)
                        crtFunc.FunctionName = builder.ToString();
                    builder.Clear();
                }
            }
        }

        if(line.Contains('{'))
        {
            foreach(char c in line)
            {
                if (c == '{')
                    level++;
            }
        }
        if(line.Contains('}'))
        {
            foreach (char c in line)
            {
                if (c == '}')
                    level--;
            }
            if (crtFunc != null && level <= 0)
            {
                crtFunc.EndLines = lineCount;
                lstCppFunc.Add(crtFunc);
                crtFunc = null;
                level = 0;
            }
        }
        lastLine = line;
    }

    return lstCppFunc;
}

现在我们有了函数列表及其起始行、结束行,当我们得到代码行时,我们可以检查它是否在哪个函数之间,BAM - 我们得到了函数行。

编辑: 我们还需要删除评论以增加正义度

string[] RemoveComment(IEnumerable<string> loc)
{
    string[] line = loc.ToArray();
    bool startComment = false;
    int startComPos=0;
    int endComPos=-1;
    int count = line.Length;
    string comment;

    bool mistakeComment;

    int multiCommentStart, multiCommentEnd;

    for(int i=0;i<count;i++)
    {
        if (string.IsNullOrWhiteSpace(line[i]))
            continue;
        if (line[i].Contains("//"))
        {
            mistakeComment = false;
            if(line[i].Contains("*//*"))//Case mistake /**//**/ with //
            {
                if ((line[i].IndexOf("//") - line[i].IndexOf("*//*")) == 1)
                {
                    mistakeComment = true;
                }
            }

            if(!mistakeComment)
            {
                comment = line[i].Substring(line[i].IndexOf("//"));
                line[i] = line[i].Replace(comment, string.Empty);
            }
        }
        if(line[i].Contains("/*"))
        {
            startComment = true;
            startComPos = line[i].IndexOf("/*");
            endComPos = -1;
        }
        else
        {
            startComPos = 0;
        }

        if (startComment)
        {
            if(!string.IsNullOrEmpty(line[i]))
            {
                if (line[i].Contains("*/"))
                {
                    startComment = false;
                    endComPos = line[i].IndexOf("*/", startComPos);
                }
                else
                    endComPos = -1;
                if (endComPos == -1)
                {
                    comment = line[i].Substring(startComPos);
                    line[i] = line[i].Replace(comment, string.Empty);
                }
                else
                {
                    comment = line[i].Substring(startComPos, endComPos - startComPos + 2);
                    line[i] = line[i].Replace(comment, string.Empty);
                }
            }
        }

        if (line[i].Contains("/*"))
        while((multiCommentStart = line[i].IndexOf("/*")) >= 0 &&
            (multiCommentEnd = line[i].IndexOf("*/")) >= 0 &&
            multiCommentEnd > multiCommentStart)
        {
            comment = line[i].Substring(multiCommentStart, multiCommentEnd - multiCommentStart + 2);
            line[i] = line[i].Replace(comment, string.Empty);
        }
    }
    return line;
}

哦,我们还需要删除调试代码

void RemoveIfdefineDebug(string[] linesCode)
{
    bool startRemove = false;
    for(int i=0;i<linesCode.Length;i++)
    {
        if(startRemove)
        {
            if (linesCode[i].Contains("#endif"))
            {
                startRemove = false;
            }
            else
                linesCode[i] = string.Empty;
        }
        if (linesCode[i].Contains("#ifdef "))
        {
            startRemove = true;
        }
    }
}

最后是主函数

string GetCodeAndFunctionName(string path, int line)
{
    List<CppFunction> lstCppFunc = AnalyzeCpp(path);
    foreach(CppFunction func in lstCppFunc)
    {
        if(func.EndLines >= line && func.StartLine <= line)
        {
            return func.FunctionName;
        }
    }
    return "x";
}