關於c++11與c風格路徑拼接的速度對比

c++11的std庫中沒有提供路徑拼接的功能

比如我需要將  "d:\\temp\\robin"  和   "..\\config.ini" 路徑拼接,

這裡用c++11的stingstream實現一個

string& replace_str(string& str, const string& to_replaced, const string& newchars)
{
    for (string::size_type pos(0); pos != string::npos; pos += newchars.length())
    {
        pos = str.find(to_replaced, pos);
        if (pos != string::npos)
            str.replace(pos, to_replaced.length(), newchars);
        else
            break;
    }
    return   str;
}
 
// windows
std::string combinePath(const char *dir, const char* name)
{    
    //printf("%s  +  %s\n", dir, name);
    if (dir == nullptr || name == nullptr)
        return "";
    string temp = dir;
    replace_str(temp, "/", "\\");
    std::stringstream oss(temp);
    std::deque<std::string> vecDir;
    std::deque<std::string> vecName;
 
    string part;
    while (std::getline(oss, part, '\\'))
    {
        if (part.length() == 0)
            continue;
        vecDir.emplace_back(part);
    } 
 
    temp = name;
    replace_str(temp, "/", "\\");
    oss.clear();
    oss.str(temp);
    while (std::getline(oss, part, '\\'))
    {
        if (part.length() == 0)
            continue;
        vecName.emplace_back(part);
    }
 
    int index = 0;
    while (index < vecName.size())
    {
        if (vecName[0] == ".")
        {
            vecName.pop_front();
        }
        //else if (vecName[0].find("..") != vecName[0].npos)
        else if (vecName[0] == "..")
        {
            vecName.pop_front();
            if (vecDir.size() > 1)
                vecDir.pop_back();
        }
        else
        {
            vecDir.emplace_back(vecName[0]);
            vecName.pop_front();
        }
    }
 
    oss.clear();
    oss.str("");
    for (int i=0; i<vecDir.size(); i++)
    {
        oss << vecDir[i];
        if (vecDir.size() == 1 || i < (vecDir.size() - 1))
        {
            oss << "\\";
        }
    } 
    return oss.str();
}

測試方法:

void test1()
{
    cout << combinePath("d:\\temp\\robin\\", "..\\demo\\config.ini") << endl;
    cout << endl;
 
    cout << combinePath("d:", "..\\demo\\config.ini") << endl;
    cout << endl;
 
    cout << combinePath("d:\\temp\\robin", "../demo/config.ini") << endl;
    cout << endl;
 
    cout << combinePath("d:\\temp\\robin", "./config.ini") << endl;
    cout << endl;
 
    cout << combinePath("d:\\temp\\robin", "config.ini") << endl;
    cout << endl;
 
    cout << combinePath("d:\\temp\\robin\\", "\\config.ini") << endl;
    cout << endl;
}

測試發現,release使用O2優化,能平均在2~4微秒左右,還是不夠快啊,

用c重新實現一遍

// 字符串范圍內,逆向查找
const char *  strrfind(const char *begin, const char *end, const char c)
{
    const char * buf = end;
    while (buf >= begin)
    {
        if (*buf == c)
            return buf;
        buf--;
    }
    return nullptr;
}
size_t  combinePathC(char ** buffer, const char * dir, const char *name)
{
    int n1 = strlen(dir);
    int n2 = strlen(name);
    size_t len =  n1 + n2 + 2;
    char *buf = new char[len];
    *buffer = buf;
 
    memcpy(buf, dir, n1);
    size_t len1 = n1;
 
    // 末尾保證有一個"\"
    if (buf[n1-1] == '\\')
    {
        len1 = n1;
    }
    else
    {
        buf[n1] = '\\';
        len1 = n1+1;        
    }
    buf[len1] = '\0';
 
    // len1++示當前拼接的長度
    size_t index = 0;
    char * rPart = (char *)name;
    size_t len2 = n2;
    while (index < n2)   // 使用index向後滑動,直到末尾
    {
        // 滑動後當前位置
        rPart = (char *)name + index;
        char * tmp = (char *)strchr(rPart, '\\');
        if (tmp == nullptr)   // end here
        {
            // 拼接剩下的
            len2 = n2 - index;
            memcpy(buf + len1, rPart, len2);
            len1 += len2;
            buf[len1] = '\0';
            len1++;
 
            break;
        }
 
        // 當前找到的長度
        len2 = tmp - rPart;
        if (len2 == 0)     // 遇到 "\config.ini",去掉1個字符
        {
            index += 1;
        }
        else if (len2 == 1 && rPart[0] == '.')   // 遇到 ".\config.ini",去掉2個字符
        {
            index += 2;
        }
        else if (len2 >= 2 && rPart[0] == '.')  // 遇到 "..\config.ini", "..x\config.ini"去掉3個字符,末尾去掉一個目錄
        {
            index += len2 + 1;
            const char * back = strrfind(buf, buf + len1 - 2 , '\\');   // 從末尾的"\"前一個字符開始找
            if (back == nullptr)
            {
                // dir 當前是這樣的情況,"d:\”
            }
            else if ((back - dir) == 2)  // dir 當前是這樣的情況,"d:\temp\”
            {
                len1 = 3;
                buf[3] = '\0';
            }
            else                        // dir 當前是這樣的情況,"d:\temp\test1\”
            {
                len1 = back - buf + 1;
                buf[len1] = '\0';
            }
        }
        else    //  遇到首字符不為點 "x\config.ini",  
        {
            index += len2 + 1;
            memcpy(buf + len1, name + index, len2 + 1);
            len1 += len2 + 1;
        }
    } 
    return len1;
}

測試一下:

    char * buf = nullptr;
    size_t len; 
    
    len = combinePathC(&buf, "d:\\temp\\robin\\", "config.ini");
    cout << buf << endl;
 
    len = combinePathC(&buf, "d:\\temp\\robin", "config.ini");
    cout << buf << endl;*/
 
    len = combinePathC(&buf, "d:\\temp\\robin", ".\\config.ini");
    cout << buf << endl;
 
    len = combinePathC(&buf, "d:\\temp\\robin", "..\\config.ini");
    cout << buf << endl;
 
    len = combinePathC(&buf, "d:\\temp\\robin", "...\\config.ini");
    cout << buf << endl;

測試發現,平均在0.2微秒左右;

打完收功! 以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: