把 c++当成脚本语言使用

工作中,经常需要批量解析文本。涉及到功能点主要包括:目录递归、文件读写、字符串解析。功能点不多,但很少有人用 C++做这方面的工作,一般认为脚本语言更适合。因为脚本语法表达更简洁,标准库更全面。C++是门严肃的语言,要写冗长的声明,标准库也不全,需要调用平台 API 完成工作,太繁琐。

这几年,C++标准频繁更新,情况不一样了。C++14 增加了 filesystem 标准库,语法也做了简化,几乎可以用来做为脚本语言。

  • 语法简化了,支持 auto 关键字,不用再写很长的 iterator 声明。
  • 目录递归功能,原来只能使用平台相关的 API,现在可以使用 filesystem 库。PS:在 Windows 平台上,MSVC 从 2015 版本开始支持。cygwin g++ 5.3 未支持。mingw g++才更新到 4.9.2 版本,也不支持。
#include <filesystem>
using namespace experimental::filesystem;

void IteratorDir(const path &p)
{
    for (const auto &iter : directory_iterator(p))
    {
        if (is_directory(iter.status()))
        {
            IteratorDir(iter.path());
        }
        else if (is_regular_file(iter.status()))
        {
            if (iter.path().extension() == ".vcproj")
            {
                // cout << iter.path().filename() << endl;
                ParseVcproj(iter.path());
            }
            else if (iter.path().extension() == ".sln")
            {
                SaveSlnContent(iter.path());
            }
        }
    }
}
  • 文件读写一直还是比较方便的
void SaveSlnContent(const path &p)
{
    ifstream file(p.string().c_str());
    ostringstream ss;
    ss << file.rdbuf();
    m_SlnContent = ss.str();
}
  • 字符串解析功能,各种语言差异不大
void ParseSln()
{
    ofstream out("R:\\a.txt");
    out << "digraph G {" << endl
        << "\trankdir = BT;" << endl
        << endl;

    size_t seg = 0;
    while ((seg = m_SlnContent.find("vcproj\", \"", seg)) != string::npos)
    {
        string curProjId = m_SlnContent.substr(seg + 10, 38);
        string curProjName = m_projectIdMap[curProjId];

        size_t seg2 = m_SlnContent.find("EndProject\n", seg);
        string tmp = m_SlnContent.substr(seg + 48, seg2 - seg - 48);
        seg = seg2;

        size_t pos = 0;
        while ((pos = tmp.find("= {", pos)) != string::npos)
        {
            string dependProjId = tmp.substr(pos + 2, 38);
            string dependProjName = m_projectIdMap[dependProjId];
            if (dependProjName.size() && curProjName.size())
                out << "\t\"" << curProjName << "\" -> \"" << dependProjName
                    << "\"" << endl;
            pos += 38;
        }
    }
    out << "}" << endl;
}
  • 编译问题。Sublime Text 3 里,自带 C++编译支持,但仅支持 g++,不支持 MSVC。添加以下配置后,可以支持 msvc 编译。写完代码后,按 C-S-b 调出编译菜单,选择 msvc Run ,直接编译运行,下方弹出窗口显示运行结果。以后再编译运行时,按 C-b 就可以了,不需要再次选择。
{
    "cmd": ["vcvars32.bat", "&", "cl", "/EHsc", "${file}", "1>&2"],
    "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
    "working_dir": "${file_path}",
    "selector": "source.c, source.cpp, source.c++",
    // By default cl is not in your PATH, so add it to your path (preferably)
    // or uncomment "path" and check that it has correct value
    // "path": "C:\\Windows\\System32;C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin",
    // this also will set path for vcvars32.bat
    "shell": true, // Without this sublime has hard times to parse "&" in out command line

    "variants":
    [
        {
            "name": "Run",
            "cmd": ["vcvars32.bat", "&", "cl", "/EHsc", "${file}", "&", "${file_path}/${file_base_name}.exe"]
        }
    ]
}

总结,filesystem 经常用到的函数如下:

#include <filesystem>
using namespace experimental::filesystem;
path();
path.string();
for (const auto &iter : diretory_iterator(path)) {
    iter.path();
    iter.status();
}