2016-06-22 2 views
0

Я пишу программу, которая должна принимать 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:\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 параметра, а не три. может ли кто-нибудь помочь?

EDIT:/Главная/пользователь/Space Dir/file.out < - путь с именем пространства.

В этом случае размер вектора больше, чем ожидалось, и путь к каталогу будет сломана .. это не может случиться ..

вектор будет содержать что-то вроде этого:

вектор [1] =/дом/пользователь/Space

вектор [2] = Dir/file.out

и что я хочу это:

вектор [1] =/дом/пользователь/Space Dir/file.out

+1

Как насчет требует, что создатель этих топонимов кавычки вокруг каталогов, которые имеют пробелы? Если они не соответствуют установленным вами правилам, это проблема пользователя, а не ваша. – PaulMcKenzie

+1

Есть ли причина, по которой вы принимаете все 3 параметра через одну строку, считанную с stdin? Вместо этого вы можете прочитать каждый параметр в своей строке stdin или взять каждый параметр из аргументов командной строки в 'argv'. Любой из этих двух подходов мог бы решить проблему пространства. – bgoldst

+0

Как бы вы указали разницу между пространством, которое разделяет параметры и пространство в имени каталога? – Galik

ответ

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 
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: Escaping.

Вы можете написать код для итерации по строке ввода и правильного разделения его на неэкранированные экземпляры символа разделителя. Исключенные экземпляры не будут рассматриваться как разделители. Вы можете параметризовать escape-символ. Например:

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 
Смежные вопросы