Как использовать разделители без дефолта при чтении текстового файла с помощью std:: fstream?
В моем коде на С++ я хочу прочитать из текстового файла (*.txt) и tokenize каждую запись. В частности, я хочу иметь возможность читать отдельные слова из файла, такие как "формат", "стоп", "Джейсон", "Европа" и т.д.
Я решил использовать fstream
для выполнения этой задачи, и я не знаю, как установить его разделителем на те, которые я хочу использовать (пробел, \n
, а также дефисы и даже апострофы, как в "Mcdonal's" ). Я понял, что пробел и \n
являются разделителями по умолчанию, но дефисы не являются, но я хочу рассматривать их как разделители, чтобы при анализе файла я получил слова в "бла-бла-ххх-звере-кошке" как просто "бла", "бла", "ххх", "животное", "кошка".
То есть, я хочу получить две строки из "переполнения стека", "вы" и т.д. и по-прежнему иметь возможность поддерживать \n
и пространство как разделители одновременно.
Ответы
Ответ 1
Исследник обрабатывает "пустое пространство" как разделители. Он использует локаль, чтобы указать, какие символы являются пробелами. Локаль, в свою очередь, включает ctype facet
, который классифицирует типы символов. Такой фасет может выглядеть примерно так:
#include <locale>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <sstream>
class my_ctype : public
std::ctype<char>
{
mask my_table[table_size];
public:
my_ctype(size_t refs = 0)
: std::ctype<char>(&my_table[0], false, refs)
{
std::copy_n(classic_table(), table_size, my_table);
my_table['-'] = (mask)space;
my_table['\''] = (mask)space;
}
};
И небольшая тестовая программа для показа работы:
int main() {
std::istringstream input("This is some input from McDonald and Burger-King.");
std::locale x(std::locale::classic(), new my_ctype);
input.imbue(x);
std::copy(std::istream_iterator<std::string>(input),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
Результат:
This
is
some
input
from
McDonald
s
and
Burger
King.
istream_iterator<string>
использует >>
для чтения отдельных строк из потока, поэтому, если вы используете их напрямую, вы должны получить те же результаты. Части, которые вам нужно включить, создают локаль и используют imbue
, чтобы поток использовал эту локаль.
Ответ 2
Вы можете использовать
istream::getline(char* buffer, steamsize maxchars, char delim)
хотя это поддерживает только один разделитель. Чтобы разделить строки на разных разделителях, вы можете использовать
char* strtok(char* inString, const char* delims)
который принимает несколько разделителей. Когда вы используете strtok, вам нужно только передать его адрес вашего буфера в первый раз - после этого просто перейдите в нуль, и он даст вам следующий токен из последнего, который он дал вам, возвращая нулевой указатель, когда нет больше.
EDIT: конкретная реализация будет похожа на
char buffer[120]; //this size is dependent on what you expect the file to contain
while (!myIstream.eofbit) //I may have forgotten the exact syntax of the end bit
{
myIstream.getline(buffer, 120); //using default delimiter of \n
char* tokBuffer;
tokBuffer = strtok(buffer, "'- ");
while (tokBuffer != null) {
cout << "token is: " << tokBuffer << "\n";
tokBuffer = strtok(null, "'- "); //I don't need to pass in the buffer again because it remembers the first time I called it
}
}