Разделение строки символом
Я знаю, что это довольно простая проблема, но я просто хочу решить ее для себя раз и навсегда
Я просто хотел бы разбить строку на массив с использованием символа в качестве разделителя. (Очень похоже на известную функцию С# <.cplit(). Я могу, конечно, применить подход грубой силы, но мне интересно, есть ли что-то лучше этого.
До сих пор я искал и, вероятно, подход ближайший - это использование strtok(), однако из-за неудобства (преобразование вашей строки в char array и т.д.) Мне не нравится использовать его. Есть ли более простой способ реализовать это?
Примечание: Я хотел бы подчеркнуть это, потому что люди могут спросить: "Как это работает, грубая сила не работает". Моим решением грубой силы было создать цикл и использовать функцию substr() внутри. Однако, поскольку для нее требуется начальная точка и длина, она терпит неудачу, когда я хочу разделить дату. Поскольку пользователь может ввести его как 7/12/2012 или 07/3/2011, где я действительно могу рассказать длину, прежде чем вычислять следующее местоположение разделителя '/'.
Ответы
Ответ 1
Использование векторов, строк и потока строк. Немного громоздко, но это делает свое дело.
std::stringstream test("this_is_a_test_string");
std::string segment;
std::vector<std::string> seglist;
while(std::getline(test, segment, '_'))
{
seglist.push_back(segment);
}
В результате получается вектор с тем же содержимым, что и
std::vector<std::string> seglist{ "this", "is", "a", "test", "string" };
Ответ 2
Другой способ (С++ 11/boost) для людей, которым нравится RegEx. Лично я большой поклонник RegEx для такого рода данных. IMO это намного мощнее, чем просто разделение строк с использованием разделителя, поскольку вы можете выбрать, чтобы быть намного умнее о том, что представляет собой "действительные" данные, если хотите.
#include <string>
#include <algorithm> // copy
#include <iterator> // back_inserter
#include <regex> // regex, sregex_token_iterator
#include <vector>
int main()
{
std::string str = "08/04/2012";
std::vector<std::string> tokens;
std::regex re("\\d+");
//start/end points of tokens in str
std::sregex_token_iterator
begin(str.begin(), str.end(), re),
end;
std::copy(begin, end, std::back_inserter(tokens));
}
Ответ 3
У Boost есть split(), который вы ищете в algorithm/string.hpp
:
std::string sample = "07/3/2011";
std::vector<string> strs;
boost::split(strs, sample, boost::is_any_of("/"));
Ответ 4
Другая возможность состоит в том, чтобы наполнить поток локалью, которая использует специальный грань ctype
. Поток использует фасет ctype для определения того, что "пробел", который он рассматривает как разделители. С фазой ctype, которая классифицирует ваш разделительный символ как пробел, чтение может быть довольно тривиальным. Здесь один из способов реализовать грань:
struct field_reader: std::ctype<char> {
field_reader(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table() {
static std::vector<std::ctype_base::mask>
rc(table_size, std::ctype_base::mask());
// we'll assume dates are either a/b/c or a-b-c:
rc['/'] = std::ctype_base::space;
rc['-'] = std::ctype_base::space;
return &rc[0];
}
};
Мы используем это, используя imbue
, чтобы указать потоку использовать локаль, которая его включает, затем прочитайте данные из этого потока:
std::istringstream in("07/3/2011");
in.imbue(std::locale(std::locale(), new field_reader);
При этом расщепление становится почти тривиальным - просто инициализируйте вектор, используя пару istream_iterator
, чтобы прочитать фрагменты из строки (встроенной в istringstream
):
std::vector<std::string>((std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>());
Очевидно, это имеет тенденцию к излишнему, если вы используете его только в одном месте. Тем не менее, если вы используете его много, он может пройти долгий путь, чтобы сохранить остальную часть кода совершенно чистой.
Ответ 5
Взгляните на boost:: tokenizer
Если вы хотите сворачивать свой собственный метод, вы можете использовать std::string::find()
, чтобы определить точки расщепления.
Ответ 6
Мне не нравится stringstream
, хотя я не уверен, почему. Сегодня я написал эту функцию, чтобы разрешить разбиение std::string
на любой произвольный символ или строку на вектор. Я знаю, что этот вопрос старый, но я хотел бы поделиться альтернативным способом расщепления std::string
.
Этот код пропускает часть строки, которую вы разделили, из результатов в целом, хотя ее можно легко изменить, чтобы включить их.
#include <string>
#include <vector>
void split(std::string str, std::string splitBy, std::vector<std::string>& tokens)
{
/* Store the original string in the array, so we can loop the rest
* of the algorithm. */
tokens.push_back(str);
// Store the split index in a 'size_t' (unsigned integer) type.
size_t splitAt;
// Store the size of what we're splicing out.
size_t splitLen = splitBy.size();
// Create a string for temporarily storing the fragment we're processing.
std::string frag;
// Loop infinitely - break is internal.
while(true)
{
/* Store the last string in the vector, which is the only logical
* candidate for processing. */
frag = tokens.back();
/* The index where the split is. */
splitAt = frag.find(splitBy);
// If we didn't find a new split point...
if(splitAt == string::npos)
{
// Break the loop and (implicitly) return.
break;
}
/* Put everything from the left side of the split where the string
* being processed used to be. */
tokens.back() = frag.substr(0, splitAt);
/* Push everything from the right side of the split to the next empty
* index in the vector. */
tokens.push_back(frag.substr(splitAt+splitLen, frag.size()-(splitAt+splitLen)));
}
}
Чтобы использовать, просто вызовите так...
std::string foo = "This is some string I want to split by spaces.";
std::vector<std::string> results;
split(foo, " ", results);
Теперь вы можете получить доступ ко всем результатам в векторе. Просто, как это - нет stringstream
, нет сторонних библиотек, не отбрасывается на C!
Ответ 7
Есть ли причина, по которой вы не хотите преобразовывать string
в массив символов (char*
)? Это довольно легко назвать .c_str()
. Вы также можете использовать цикл и функцию .find()
.
строковый класс
строка .find()
строка .c_str()
Ответ 8
Как насчет функции erase()
? Если вы знаете позицию exakt в строке, где нужно разделить, то вы можете "извлечь" поля в строке с помощью erase()
.
std::string date("01/02/2019");
std::string day(date);
std::string month(date);
std::string year(date);
day.erase(2, string::npos); // "01"
month.erase(0, 3).erase(2); // "02"
year.erase(0,6); // "2019"