#c #stl
#c #stl
Вопрос:
В приведенном ниже коде я ожидаю, что результат будет abc #def. Но я получаю вывод как abcdef. Кажется, strtok изменяет вектор, хотя я не передаю вектор напрямую функции strtok. Могу ли я узнать, как это происходит внутри
std::vector<std::pair<const std::string, int>> x;
std::vector<std::string> z;
int main()
{
char* pch;
x.push_back(std::make_pair("abc#def", 1));
std::string m = x[0].first;
pch = strtok ((char*)(m.c_str()),"#");
while (pch != NULL)
{
z.push_back(pch);
pch =strtok (NULL, "#");
}
cout<<x[0].first<<endl;
return 0;
}
Комментарии:
1. вы модифицируете std::string buffer, потому что strtok изменяет его параметр (так оно и работает).
2. что я должен сделать, чтобы получить abc #def в выводе с помощью strtok
3.
strtok
изменяет проанализированную строку. Именно такstrtok
и работает. Приведение к подобному((char*)(m.c_str())
— определенно плохая идея.4. Не используйте
strtok
. Особенно не в c .
Ответ №1:
Скопированные экземпляры std::string
могут использовать один и тот же резервный буфер. То есть x[0] и m могут фактически использовать один и тот же резервный буфер.
Вот почему c_str()
член возвращает const char *
— вам не разрешено его изменять.
Вы отбрасываете константу, используя приведение в стиле C (char *).
В общем, лучше использовать приведения C : static_cast<>/reinterpret_cast<>/ dynamic_cast<> и const_cast<>, если вам действительно нужно удалить const. Последний предназначен только для взаимодействия со старым C-кодом без квалификаторов const . Вам не нужно использовать его в обычном коде C .
Комментарии:
1. На самом деле, если быть педантичным, им больше не разрешено использовать один и тот же буфер резервного копирования, но некоторые библиотеки все равно это делают.
2. Кроме того, совет использовать
reinterpret_cast<>
здесь вместо этого глуп, потому что это ничего не исправит.3. @MooingDuck (1): Это единственное объяснение поведения, которое он видит здесь, хотя оно может быть больше не разрешено. (2) Вот почему я сказал «в общем».
Ответ №2:
Вместо использования методов strtok
use find_first_of
amp; find_first_not_of
string
вот так:
using namespace std;
int main () {
string s="abc#def";
string temp = s,res;
while(temp.size()){
size_t st,en;
st = temp.find_first_not_of("#");
en = temp.find_first_of("#",st);
res= temp.substr(st,en);
cout<<res.c_str()<<endl;
temp=(en == string::npos) ? "" : temp.substr(en);
}
return 0;
}
Ответ №3:
В вашей реализации c_str()
должна быть возвращена ссылка на внутренний char
буфер строки без создания какой-либо копии. Со страницы руководства glibc strtok
:
ОШИБКИ
Будьте осторожны при использовании этих функций. Если вы их используете, обратите внимание, что:
- Эти функции изменяют свой первый аргумент.
итак, да, strtok
примененный к указателю, возвращаемому из c_str()
, изменит строковый буфер.
Вы должны использовать std::getline
вместо strtok
того, чтобы разделять строку на #
:
std::vector<std::pair<const std::string, int>> x;
int main() {
x.push_back(std::make_pair("abc#def", 1));
std::string m = x[0].first;
std::string token;
while(std::getline(m, token, '#')) {
std::cout << token << std::endl;
}
cout<< x[0].first <<endl;
return 0;
}
или, если вам действительно нужно использовать strtok
, по крайней мере, дублируйте буфер, возвращаемый c_str()
with strdup
(и помните об free()
этом).
Комментарии:
1. На самом деле, нет никакой гарантии (или не было до C 11), которая
c_str()
вернет указатель на внутреннее представление (хотя я никогда не слышал о реализации, где этого не было). И изменение чего-либо с помощью указателя, возвращаемого,c_str()
является неопределенным поведением; в некоторых реализациях это может иметь очень странные результаты.2. И я бы не стал использовать
getline
для этого. Что не так сstd::find
orstd::string::find
?3. @JamesKanze
std::getline
удобен, если строка содержит более одного#
разделителя. Конечно, можно перебирать токены, используяstd::find
, но я нахожу это довольно громоздким. Оc_str
, я отредактировал ответ, объясняющий часть «ваша реализация имеет такое поведение».4. В or нет ничего громоздкого
std::find
std::string::find
, и они легко расширяются до использования одного из набора разделителей, и если вы заменитеstd::find
наstd::find_if
, вы можете определить практически любые критерии в качестве разделителя (например, любой пробел). И, конечно, название также не вводит в заблуждение; использованиеgetline
для чего угодно, кроме чтения строк, вводит читателя в заблуждение.