#c
#c
Вопрос:
Я видел это в «списке вопросов для интервью». Заставил меня задуматься.
Конечно, не обязательно ограничиваться пробелами, легко обобщается на «удаление некоторого определенного символа из строки на месте».
Мое решение:
void stripChar(char *str, char c = ' ') {
int x = 0;
for (int i=0;i<strlen(str);i ) {
str[i-x]=str[i];
if (str[i]==c) x ;
}
str[strlen(str)-x] = '';
}
Я сомневаюсь, что есть более эффективный, но есть ли более элегантное решение?
редактировать: совершенно забыл, что я оставил strlen
там, это определенно неэффективно
Ответ №1:
C не имеет аргументов по умолчанию, и если вы программируете на C , вам следует использовать std::string
и remove_if
from <algorithm>
.
Вы определенно можете сделать это более эффективным, исключив вызовы strlen
, которые превращают алгоритм O (N) в алгоритм O (N 2) и совершенно не нужны — вы все равно просматриваете строку, поэтому просто ищите NUL самостоятельно.
Вы также можете сделать это более C-идиоматичным, используя два указателя вместо индексации массива. Я бы сделал это так:
void strip_char(char *str, char strip)
{
char *p, *q;
for (q = p = str; *p; p )
if (*p != strip)
*q = *p;
*q = '';
}
Комментарии:
1. Вау, это так просто и элегантно.
Ответ №2:
Прежде всего, i<strlen(str)
это всегда неэффективная идиома для зацикливания на строке. Правильное условие цикла просто str[i]
, т.Е. Цикл до str[i]
тех пор, пока не будет нулевым терминатором.
С учетом сказанного, вот самый простой / самый краткий алгоритм, который я знаю:
for (size_t i=0, j=0; s[j]=s[i]; j =!isspace(s[i ]));
Примечание: Мое решение касается вопроса, написанного в теме (пробел), а не тела (конкретного символа). Вы можете легко адаптировать его, если это необходимо.
Комментарии:
1. Wow
j =!isspace(s[i ])
действительно умный.2. Еще более лаконично
while(s[!(s[i]==32)?j :j]=s[i ]);
, но вы должны настроить переменные вне его так. Тем не менее, я бы посоветовал OP никогда не использовать ни один из них в каком-либо реальном коде, конечно.3. @SethCarnegie: или даже
(s[i]-32)
4. Причина, по которой я написал это с помощью цикла for, заключается в том, чтобы сохранить его чистым и читаемым. Условие цикла и операция увеличения сохраняются на своих местах; единственное, что немного неканоническое, — это выполнение присваивания в условии цикла, и причина, по которой я помещаю его туда, а не в (пустое) тело цикла, заключается в том, что присвоение должно выполняться даже в последнем (завершающем)итерация.
Ответ №3:
void prepend(char* s,char ch){
int len = strlen(s);
memmove(s, s 1, len - 1);
s[len - 1] = 'x0';
}
void RemoveWhitespace(char* InStr, char ch){
int n(0);
if (InStr == NULL){
return;
}
else if ((*InStr) == 'x0'){
return;
}
else if ((*InStr) != ch){
RemoveWhitespace(InStr 1,ch);
}
else{
while ((*InStr) == ch){
prepend(InStr,InStr[0]);
n ;
}
RemoveWhitespace(InStr n,ch);
}
}
Комментарии:
1. @Steven Lu, рекурсия может привести к переполнению стека, но поскольку все остальные ответы были итеративными, я решил опубликовать эту рекурсивную реализацию. Кроме того, я знаю, что memmove и strlen — дорогостоящие операции.