带有空间的拆分字符串路径

Split string path with space

本文关键字:字符串 路径 拆分 空间      更新时间:2023-10-16

我正在编写一个程序,该程序应通过用户接收3个参数:file_upload" local_path" remote_path"

代码示例:

std::vector split(std::string str, char delimiter) {
   std::vector<string> v;
   std::stringstream src(str);
   std::string buf;
   while(getline(src, buf, delimiter)) {
       v.push_back(buf);
   }
   return v;
}
void function() {
   std::string input
   getline(std::cin, input);
   // user input like this: file_upload /home/Space Dir/file c:dirfile
   std::vector<std::string> v_input = split(input, ' ');
   // the code will do something like this
   if(v_input[0].compare("file_upload") == 0) {        
     FILE *file;
     file = fopen(v_input[1].c_str(), "rb");
     send_upload_dir(v_input[2].c_str());
     // bla bla bla
   }
}

我的问题是:第二参数和第三个参数是目录,然后它们可以包含名称的空格。如何使拆分函数不更改第二个参数的空间?

我认为将引号放入目录中,并创建一个函数以识别,但不能100%起作用,因为该程序具有其他功能,仅采用2个参数而不是三个参数。任何人都可以帮助吗?

编辑:/home/user/user/space dir/file.out&lt; - 带有空间名称的路径。

如果发生这种情况,则向量的大小大于预期,并且目录的路径将被打破..这不会发生。

向量将包含类似的内容:

vector [1] =/home/user/space

vector [2] = dir/file.out

我想要的是:

vector [1] =/home/home/user/space dir/file.out

由于您需要从单个字符串输入中接受三个值,因此这是编码的问题。

编码有时是通过对某些或所有字段施加固定宽度要求来完成的,但这显然是不合适的,因为我们需要支持可变宽度的文件系统路径和第一个值(这似乎是某种形式模式说明符)也可能是可变宽度。所以那是出来的。

这留下了4个可能的解决方案,用于可变宽度编码:


1:明确的定界符。

如果您可以选择一个保证永远不会在划界值中显示的分离器字符,则可以在其中拆分。例如,如果保证NUL永远不会成为模式值或路径值的一部分,那么我们可以做到这一点:

std::vector<std::string> v_input = split(input,'');

或管道字符:

std::vector<std::string> v_input = split(input,'|');

因此,必须给出这样的输入(对于管道字符):

file_upload|/home/user/Space Dir/file.out|/home/user/Other Dir/blah

2:逃脱。

您可以将代码写入迭代,并在分离器字符的未阐明实例上正确拆分。逃脱的实例不会被视为分离器。您可以参数化逃生字符。例如:

std::vector<std::string> escapedSplit(std::string str, char delimiter, char escaper ) {
    std::vector<std::string> res;
    std::string cur;
    for (size_t i = 0; i < str.size(); ++i) {
        if (str[i] == delimiter) {
            res.push_back(cur);
            cur.clear();
        } else if (str[i] == escaper) {
            ++i;
            if (i == str.size()) break;
            cur.push_back(str[i]);
        } else {
            cur.push_back(str[i]);
        } // end if
    } // end for
    if (!cur.empty()) res.push_back(cur);
    return res;
} // end escapedSplit()
std::vector<std::string> v_input = escapedSplit(input,' ','');

输入为:

file_upload /home/user/Space Dir/file.out /home/user/Other Dir/blah

3:引用。

您可以将代码写入迭代,并在分隔符字符的无引用实例上正确拆分。引用的实例将不会被视为分离器。您可以参数化报价字符。

这种方法的一种并发症是,除非引入逃避机制,否则不可能在引用范围内包含引用字符本身,类似于解决方案#2。一个共同的策略是允许重复报价字符逃脱它。例如:

std::vector<std::string> quotedSplit(std::string str, char delimiter, char quoter ) {
    std::vector<std::string> res;
    std::string cur;
    for (size_t i = 0; i < str.size(); ++i) {
        if (str[i] == delimiter) {
            res.push_back(cur);
            cur.clear();
        } else if (str[i] == quoter) {
            ++i;
            for (; i < str.size(); ++i) {
                if (str[i] == quoter) {
                    if (i+1 == str.size() || str[i+1] != quoter) break;
                    ++i;
                    cur.push_back(quoter);
                } else {
                    cur.push_back(str[i]);
                } // end if
            } // end for
        } else {
            cur.push_back(str[i]);
        } // end if
    } // end for
    if (!cur.empty()) res.push_back(cur);
    return res;
} // end quotedSplit()
std::vector<std::string> v_input = quotedSplit(input,' ','"');

输入为:

file_upload "/home/user/Space Dir/file.out" "/home/user/Other Dir/blah"

甚至只是:

file_upload /home/user/Space" "Dir/file.out /home/user/Other" "Dir/blah

4:长度值。

最后,您可以编写代码以在每个值之前进行长度,只抓住许多字符。我们可能需要固定宽度的长度指示符,或者在长度说明符后跳过划界字符。例如(注意:错误检查的光):

std::vector<std::string> lengthedSplit(std::string str) {
    std::vector<std::string> res;
    size_t i = 0;
    while (i < str.size()) {
        size_t len = std::atoi(str.c_str());
        if (len == 0) break;
        i += (size_t)std::log10(len)+2; // +1 to get base-10 digit count, +1 to skip delim
        res.push_back(str.substr(i,len));
        i += len;
    } // end while
    return res;
} // end lengthedSplit()
std::vector<std::string> v_input = lengthedSplit(input);

输入为:

11:file_upload29:/home/user/Space Dir/file.out25:/home/user/Other Dir/blah

几天前我也有类似的问题,然后这样解决:

首先,我已经创建了一个副本,然后用一些填充物替换副本中的引用字符串以避免白色空间,最后我根据副本的白空间索引将原始字符串拆分。

这是我的完整解决方案:

您可能还需要删除双引号,修剪原始字符串等:

#include <sstream>
#include<iostream>
#include<vector>
#include<string>
using namespace std;

string padString(size_t len, char pad)
{
    ostringstream ostr;
    ostr.fill(pad);
    ostr.width(len);
    ostr<<"";
    return ostr.str();
}
void splitArgs(const string& s, vector<string>& result)
{
    size_t pos1=0,pos2=0,len;
    string res = s;
    pos1 = res.find_first_of(""");
    while(pos1 != string::npos && pos2 != string::npos){
        pos2 = res.find_first_of(""",pos1+1);
        if(pos2 != string::npos ){
            len = pos2-pos1+1;
            res.replace(pos1,len,padString(len,'X'));
            pos1 = res.find_first_of(""");
        }
    }
    pos1=res.find_first_not_of(" trn",0);
    while(pos1 < s.length() && pos2 < s.length()){
        pos2 = res.find_first_of(" trn",pos1+1);
        if(pos2 == string::npos ){
            pos2 = res.length();
        }
        len = pos2-pos1;
        result.push_back(s.substr(pos1,len));
        pos1 = res.find_first_not_of(" trn",pos2+1);
    }
}
int main()
{
    string s = "234 "5678 91" 8989"; 
    vector<string> args;
    splitArgs(s,args);
    cout<<"original string:"<<s<<endl;
    for(size_t i=0;i<args.size();i++)
       cout<<"arg "<<i<<": "<<args[i]<<endl;
    return 0;
}

这是输出:

original string:234 "5678 91" 8989
arg 0: 234
arg 1: "5678 91"
arg 2: 8989