C++ 带空格的拆分字符串路径

C++ 带空格的拆分字符串路径,c++,C++,我正在编写一个程序,该程序应该由用户接收3个参数:文件上传“本地路径”“远程路径” 代码示例: 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);

我正在编写一个程序,该程序应该由用户接收3个参数:文件上传“本地路径”“远程路径”

代码示例:

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:\dir\file
   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
   }
}
std::vector split(std::string str,字符分隔符){
std::向量v;
std::stringstream src(str);
std::字符串buf;
while(getline(src、buf、分隔符)){
v、 推回(buf);
}
返回v;
}
空函数(){
字符串输入
getline(标准::cin,输入);
//用户输入如下:file\u upload/home/Space Dir/file c:\Dir\file
标准::向量v_输入=分割(输入');
//代码将执行如下操作
如果(v_输入[0]。比较(“文件上传”)==0{
文件*文件;
file=fopen(v_输入[1].c_str(),“rb”);
发送上传目录(v_input[2].c_str());
//呜呜呜呜
}
}
我的问题是:第二个和第三个参数是目录,那么它们可以在名称中包含空格。如何使分割函数不改变第二个和第三个参数的空格

我想在目录中加上引号,并创建一个函数来识别,但不能100%工作,因为程序还有其他函数,它们只需要2个参数,而不是3个参数。有人能帮忙吗?


编辑:/home/user/Space Dir/file.out几天前,我遇到了类似的问题,解决方法如下:

首先我创建了一个副本,然后用一些填充来替换副本中带引号的字符串以避免空格,最后根据副本中的空格索引分割原始字符串

以下是我的完整解决方案:

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

#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(" \t\r\n",0);
    while(pos1 < s.length() && pos2 < s.length()){
        pos2 = res.find_first_of(" \t\r\n",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(" \t\r\n",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;
}

前几天我也遇到过类似的问题,解决方法如下:

首先我创建了一个副本,然后用一些填充来替换副本中带引号的字符串以避免空格,最后根据副本中的空格索引分割原始字符串

以下是我的完整解决方案:

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

#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(" \t\r\n",0);
    while(pos1 < s.length() && pos2 < s.length()){
        pos2 = res.find_first_of(" \t\r\n",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(" \t\r\n",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;
}

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

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

这就为可变宽度编码留下了4种可能的解决方案:


1:明确的分隔符

如果您可以选择一个保证永远不会出现在分隔值中的分隔符,则可以对其进行拆分。例如,如果保证NUL永远不会是模式值或路径值的一部分,那么我们可以这样做:

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

2:转义

您可以编写代码来迭代输入行,并在分隔符的未scaped实例上正确拆分它。转义实例将不被视为分隔符。可以参数化转义字符。例如:

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,' ','\\');
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,' ','"');

3:引用

您可以编写代码来迭代输入行,并在分隔符的非引号实例上正确拆分它。引用的实例将不被视为分隔符。可以参数化引号字符

这种方法的一个复杂之处是,除非引入转义机制(类似于解决方案#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,' ','\\');
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

4:长度值

最后,您可以编写代码,在每个值之前取一个长度,并且只获取那么多字符。我们可能需要一个固定宽度的长度说明符,或者跳过长度说明符后面的定界字符。例如(注意:错误检查灯亮):


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

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

这就为可变宽度编码留下了4种可能的解决方案:


1:明确的分隔符

如果您可以选择一个保证永远不会出现在分隔值中的分隔符,则可以对其进行拆分。例如,如果保证NUL永远不会是模式值或路径值的一部分,那么我们可以这样做:

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

2:转义

您可以编写代码来迭代输入行,并在分隔符的未scaped实例上正确拆分它。转义实例将不被视为分隔符。可以参数化转义字符。例如:

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,' ','\\');
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,' ','"');

3:引用

您可以编写代码来迭代输入行,并在分隔符的非引号实例上正确拆分它。引用的实例将不被视为分隔符。可以参数化引号字符

这种方法的一个复杂之处是,除非引入转义机制(类似于解决方案#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,' ','\\');
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

4:长度值

最后,您可以编写代码,在每个值之前取一个长度,并且只获取那么多字符。我们可能需要一个固定宽度的长度说明符,或者跳过长度说明符后面的定界字符。例如(注:灯亮错误ch