У меня совершенно другой подход, чем у других решений, который предлагает большую ценность в том смысле, что другим решениям по-разному не хватает, но, конечно, у него есть и свои недостатки. Здесь - рабочая реализация с примером размещения <tag></tag>
вокруг слов.
Для начала, эта проблема может быть решена с помощью одного цикла, без дополнительной памяти, и путем рассмотрения всего лишь четырех логических случаев. Концептуально нас интересуют границы. Наш код должен отражать это: давайте переберем строку и посмотрим на два символа за раз, имея в виду, что у нас есть особые случаи в начале и конце строки.
Недостатком является то, что нам нужно написать реализацию, которая является несколько многословной, но в основном удобной для примера.
Положительным моментом является то, что мы написали реализацию, поэтому очень легко настроить ее для конкретных нужд, таких как разграничение левых и записывающих границ слов, использование любого набора разделителей или обработка других случаев, таких как неограниченный или ошибочный позиций.
using namespace std;
#include <iostream>
#include <string>
#include <cctype>
typedef enum boundary_type_e {
E_BOUNDARY_TYPE_ERROR = -1,
E_BOUNDARY_TYPE_NONE,
E_BOUNDARY_TYPE_LEFT,
E_BOUNDARY_TYPE_RIGHT,
} boundary_type_t;
typedef struct boundary_s {
boundary_type_t type;
int pos;
} boundary_t;
bool is_delim_char(int c) {
return isspace(c); // also compare against any other chars you want to use as delimiters
}
bool is_word_char(int c) {
return ' ' <= c && c <= '~' && !is_delim_char(c);
}
boundary_t maybe_word_boundary(string str, int pos) {
int len = str.length();
if (pos < 0 || pos >= len) {
return (boundary_t){.type = E_BOUNDARY_TYPE_ERROR};
} else {
if (pos == 0 && is_word_char(str[pos])) {
// if the first character is word-y, we have a left boundary at the beginning
return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos};
} else if (pos == len - 1 && is_word_char(str[pos])) {
// if the last character is word-y, we have a right boundary left of the null terminator
return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
} else if (!is_word_char(str[pos]) && is_word_char(str[pos + 1])) {
// if we have a delimiter followed by a word char, we have a left boundary left of the word char
return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos + 1};
} else if (is_word_char(str[pos]) && !is_word_char(str[pos + 1])) {
// if we have a word char followed by a delimiter, we have a right boundary right of the word char
return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
}
return (boundary_t){.type = E_BOUNDARY_TYPE_NONE};
}
}
int main() {
string str;
getline(cin, str);
int len = str.length();
for (int i = 0; i < len; i++) {
boundary_t boundary = maybe_word_boundary(str, i);
if (boundary.type == E_BOUNDARY_TYPE_LEFT) {
// whatever
} else if (boundary.type == E_BOUNDARY_TYPE_RIGHT) {
// whatever
}
}
}
Как видите, код очень прост для понимания и точной настройки, а фактическое использование кода очень короткое и простое. Использование C ++ не должно останавливать нас от написания самого простого и легко настраиваемого кода, даже если это означает, что мы не используем STL. Я думаю, это пример того, что Линус Торвальдс мог бы назвать «вкус» , поскольку мы устранили всю логику, которая нам не нужна, при написании в стиле, который, естественно, позволяет обрабатывать больше случаев, когда если возникает необходимость справиться с ними.
Что могло бы улучшить этот код, так это использование enum class
, принимающего указатель функции на is_word_char
в maybe_word_boundary
вместо прямого вызова is_word_char
и передавающего лямбду.