Для чтения и записи текстовых данных, Qt предоставляет класс QTextStream. Он может использоваться как для чтения/записи простого текста, так и для файлов с другими текстовыми форматами, такими как HTML, XML и файлов с исходными текстами программ. Он принимает на себя обязательства по преобразованию кодировки символов между Unicode и 8-ми битными кодировками, а так же по разному обрабатывает признак окончания строки, в соответствии с соглашениями, принятыми в различных операционных системах.
В качестве фундаментального типа данных, QTextStream использует QChar. В дополнение к символьным и строковым данным, QTextStream поддерживает базовые числовые типы языка C++, конвертируя их в/из строки.
С целью демонстрации возможностей QTextStream, продолжим рассмотрение реализации класса Gallery. Ниже приводится исходный текст функции saveText(), которая сохраняет список картин в простой текстовый файл:
bool Gallery::saveText(const QString &fileName)
{
QFile file(fileName);
if (!file.open(IO_WriteOnly | IO_Translate)) {
ioError(file, tr("Cannot open file %1 for writing"));
return false;
}
QTextStream out(&file);
out.setEncoding(QTextStream::UnicodeUTF8);
list<Drawing>::const_iterator it = drawings.begin();
while (it != drawings.end()) {
out << *it;
++it;
}
if (file.status() != IO_Ok) {
ioError(file, tr("Error writing to file %1"));
return false;
}
return true;
}
При открытии файла используется флаг
IO_Translate, чтобы корректным образом перевести символ
перевода строки в последовательность символов, которая
соответствует используемой операционной системе ("/r/n"
-- для Windows, "/r" -- для Mac OS X). Затем
устанавливается кодировка символов UTF-8, совместимая с ASCII. (За
дополнительной информацией об Unicode, см. Главу 15.) После этого, в цикле, в файл выводятся
описания картин, с помощью перегруженного оператора
"<<":
QTextStream &operator<<(QTextStream &out, const Drawing &drawing)
{
out << drawing.myTitle << ":" << drawing.myArtist << ":"
<< drawing.myYear << endl;
return out;
}
При записи сведений о картине, в качестве разделителя полей,
используется символ двоеточия. Каждая запись в файле завершается
символом перевода строки. При этом мы исходим из предположения, что ни
имя художника, ни название картины не содержат символов двоеточия или
перевода строки.Ниже показан пример содержимого файла, созданного функцией saveText():
The False Shepherds:Hans Bol:1576
Panoramic Landscape:Jan Brueghel the Younger:1619
Dune Landscape:Jan van Goyen:1630
River Delta:Jan van Goyen:1653
Теперь перейдем к функции чтения файла:
bool Gallery::loadText(const QString &fileName)
{
QFile file(fileName);
if (!file.open(IO_ReadOnly | IO_Translate)) {
ioError(file, tr("Cannot open file %1 for reading"));
return false;
}
drawings.clear();
QTextStream in(&file);
in.setEncoding(QTextStream::UnicodeUTF8);
while (!in.atEnd()) {
Drawing drawing;
in >> drawing;
drawings.push_back(drawing);
}
if (file.status() != IO_Ok) {
ioError(file, tr("Error reading from file %1"));
return false;
}
return true;
}
Все самое интересное в этой функции, заключено внутри цикла
while. Он выполняет чтение данных, с
помощью оператора ">>", до тех пор, пока не будет
достигнут конец файла.Реализация оператора ">>" не так тривиальна, поскольку представление текстовых данных не так однозначно. Рассмотрим следующий пример:
out << "alpha" << "bravo";
Если исходить из того, что out -- это
экземпляр класса QTextStream, то в файл
фактически будет записана одна строка "alphabravo". Мы не
сможем прочитать данные, просто написав:
in >> str1 >> str2;
Фактически, в переменную str1 будет
записана строка "alphabravo", а в переменную str2 -- ничего.Если записываемый текст состоит из отдельных слов, мы можем вставлять пробелы между ними и затем читать этот текст слово за словом. (Этот подход был реализован в функциях DiagramView::copy() и DiagramView::paste(), в Главе 8.) Но в данном случае этот вариант не подходит, поскольку имя художника и название картины могут состоять более чем из одного слова. Поэтому, за один раз читается целая строка и затем разбивается на элементы, с помощью функции QStringList::split() :
QTextStream &operator>>(QTextStream &in, Drawing &drawing)
{
QString str = in.readLine();
QStringList fields = QStringList::split(":", str);
if (fields.size() == 3) {
drawing.myTitle = fields[0];
drawing.myArtist = fields[1];
drawing.myYear = fields[2].toInt();
}
return in;
}
Текстовые файлы могут читаться за один прием, с помощью
QTextStream::read():
QString wholeFile = in.read();
В переменной, конец каждой строки будет отмечен символом
'\n', независимо от используемой операционной системы.Считывание файла за раз может оказаться удобным решением, если данные должны пройти предварительную обработку, например:
wholeFile.replace("&", "&");
wholeFile.replace("<", "<");
wholeFile.replace(">", ">");
Чтобы записать данные в файл за одно обращение, можно сначала
разместить их в переменной, а затем вывести на диск:
QString Gallery::saveToString()
{
QString result;
QTextOStream out(&result);
list<Drawing>::const_iterator it = drawings.begin();
while (it != drawings.end()) {
out << *it;
++it;
}
return result;
}
Связать поток со строковой переменной так же просто, как и
связать поток с файлом.
void Gallery::readFromString(const QString &data)
{
QString string = data;
drawings.clear();
QTextIStream in(&string);
while (!in.atEnd()) {
Drawing drawing;
in >> drawing;
drawings.push_back(drawing);
}
}
Запись текстовых данных -- довольно простая операция, а вот
чтение их может оказаться довольно сложной задачей. В случае
использования сложных форматов может потребоваться написать полноценный
синтаксический анализатор. Как правило, подобные анализаторы считывают
текст символ за символом, с помощью оператора ">>" в
переменную типа QChar или построчно, с
помощью readLine() и затем анализируют
полученную строку.
Пред. | В начало | След. |
Ввод/вывод. | На уровень выше | Работа с файлами и каталогами. |