拆分空间的字符串路径

时间:2016-06-22 21:54:28

标签: c++

我正在编写一个应该由User接收3个参数的程序:file_upload" local_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:\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
   }
}

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

我想在目录中放置引号并使函数识别,但不能100%工作,因为程序有其他函数只有2个参数而不是3个。任何人都可以帮忙吗?

编辑: / home / user / Space Dir / file.out&lt; - 带空格名称的路径。

如果发生这种情况,矢量大小大于预期,并且目录的路径将被破坏..这不可能发生..

向量将包含以下内容:

vector [1] = / home / user / Space

vector [2] = Dir / file.out

我想要的是这个:

vector [1] = / home / user / Space Dir / file.out

2 个答案:

答案 0 :(得分:2)

前几天我遇到过类似的问题,并解决了这个问题:

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

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

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

#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;
}

这是输出:

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

答案 1 :(得分:2)

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

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

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

1:明确的分隔符。

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

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

或者管道人物:

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