home | login | register | DMCA | contacts | help |      
mobile | donate | ВЕСЕЛКА

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Э Ю Я


my bookshelf | genres | recommend | rating of books | rating of authors | reviews | new | форум | collections | читалки | авторам | add
fantasy
space fantasy
fantasy is horrors
heroic
prose
  military
  child
  russian
detective
  action
  child
  ironical
  historical
  political
western
adventure
adventure (child)
child's stories
love
religion
antique
Scientific literature
biography
business
home pets
animals
art
history
computers
linguistics
mathematics
religion
home_garden
sport
technique
publicism
philosophy
chemistry
close

реклама - advertisement



Строки, массивы байтов и объекты произвольного типа

QString, QByteArray и QVariant — три класса, которые имеют много общего с контейнерами и могут использоваться в некоторых контекстах как альтернатива контейнерам. Кроме того, как и контейнеры, эти классы используют неявное совмещение данных для уменьшения расхода памяти и повышения быстродействия.

Мы начнем с рассмотрения типа QString. Строковые данные применяются в любой программе с графическим пользовательским интерфейсом и не только непосредственно для пользовательского интерфейса, но часто и в качестве структур данных. В стандартном составе С++ содержится два типа строк: традиционные символьные массивы языка С с завершающим символом «\0» и класс std::string. Класс QString содержит 16-битовые значения в коде Unicode. Unicode содержит в качестве подмножеств коды ASCII и Latin-1 с их обычным числовым представлением. Но поскольку QString имеет 16-битовые значения, он может представлять тысячи других символов, используемых для записи букв большинства мировых языков. Дополнительную информацию по кодировке Unicode вы найдете в главе 17.

При использовании QString не стоит беспокоиться о таких не очень понятных вещах, как выделение достаточного объема памяти или гарантирование завершения данных символом '\0'. Концептуально строки QString можно рассматривать как вектор символов QChar. Внутри QString могут быть символы '\0'. Функция length() возвращает размер строки, включая символы '\0'.

Класс QString содержит бинарный оператор +, обеспечивающий конкатенацию двух строк, и оператор += для добавления одной строки в конец другой. Поскольку QString заранее автоматически добавляет память в конец данных строки, построение строки путем повторения операций добавления символов в конец строки выполняется очень быстро. Ниже приводится пример обоих операторов:

QString str = "User: ";

str += userName + "\n";

Существует также функция QString::append(), которая делает то же самое, что и оператор +=:

str = "User: ";

str.append(userName);

str.append("\n");

Совершенно другой способ объединения строк заключается в использовании функции sprintf() класса QString:

str.sprintf("%s %.1f%%", "perfect competition", 100.0);

Данная функция поддерживает спецификаторы формата, используемые функцией библиотеки С++ sprintf(). В приведенном выше примере переменной str присваивается значение «perfect competition 100.0%» (абсолютно безупречное соревнование).

Имеется еще один способ составления строк из других строк или чисел, и он заключается в использовании функции arg():

str = QString("%1 %2 (%3s-%4s)")

.arg("реrmissive").arg("society").arg(1950).arg(1970);

В этом примере «%1» заменяется словом «permissive» (либеральное), «%2» заменяется словом «society» (общество), «%3» заменяется на «1950» и «%4» заменяется на «1970». В результате получаем «permissive society (1950s — 1970s)» (либеральное общество в 1950—70 годах). Функция arg() перегружается для обработки различных типов данных. В некоторых случаях используются дополнительные параметры для управления шириной поля, базой числа или точностью числа с плавающей точкой. В целом гораздо лучше использовать arg(), а не sprintf(), поскольку эта функция сохраняет тип, полностью поддерживает Unicode и позволяет трансляторам изменять порядок параметров «%1».

QString может преобразовывать числа в строки, используя статическую функцию QString::number():

str = QString::number(59.6);

Или это можно сделать при помощи функции setNum():

str.setNum(59.6);

Обратное преобразование строки в число осуществляется при помощи функций toInt(), toLongLong(), toDouble() и так далее. Например:

bool ok;

double d = str.toDouble(&ok);

Этим функциям передается необязательный параметр—ссылка на переменную типа bool, которая устанавливается на значение true или false в зависимости от успешности преобразования. Если преобразование завершается неудачей, эти функции возвращают 0.

Имея некоторую строку, нам часто приходится выделять какую-то ее часть. Функция mid() возвращает подстроку заданной длины (второй аргумент), начиная с указанной позиции (первый аргумент). Например, следующий программный код выводит на консоль слово «pays»[6]:

QString str = "polluter pays principle";

qDebug() << str.mid(9, 4);

Существуют также функции left() и right(), которые выполняют аналогичную работу. Обеим функциям передается количество символов n, и они возвращают первые и последние n символов строки. Например, следующий программный код выдает на консоль слова «polluter principle»:

QString str = "polluter pays principle";

qDebug() << str.left(8) << " " << str.right(9);

Если требуется определить, содержится ли в строке конкретный символ, подстрока или соответствует ли строка регулярному выражению, мы можем использовать один из вариантов функции indexOf() класса QString:

QString str = "the middle bit";

int i = str.indexOf("middle");

В результате i становится равным 4. Функция indexOf() возвращает -1 при неудачном поиске и принимает в качестве необязательных аргументов начальную позицию и флажок учета регистра.

Если мы просто хотим проверить начальные или конечные символы строки, мы можем использовать функции startsWith () и endsWith():

if (url.startsWith("http:") && url.endsWith(".png"))

Это проще и быстрее, чем:

if (url.left(5) == "http:" && url.right(4) == ".png")

Оператор сравнения строк == зависит от регистра. Если сравниваются строки, которые пользователь видит на экране, обычно правильным решением будет использование функции localeAwareCompare(), а если необходимо сделать сравнение не зависимым от регистра, мы можем использовать функции toUpper() или toLower(). Например:

if (fileName.toLower() == "readme.txt")

Если мы хотим заменить определенную часть строки другой подстрокой, мы можем использовать функцию replace():

QString str= "a cloudy day";

str.replace(2, 6, "sunny");

Результатом является «sunny day» (солнечный день). Этот программный код может быть переписан с применением функций remove() и insert():

str.remove(2, 6);

str.insert(2, "sunny");

Во-первых, мы удаляем шесть символов, начиная с позиции 2, и в результате получаем строку «а_ _day» (с двумя пробелами), затем мы вставляем слово «sunny» в позицию 2.

Существуют перегруженные версии функции replace(), которые заменяют все подстроки, совпадающие со значением первого аргумента, вторым аргументом. Например, ниже показано, как можно заменить все символы «&» в строке на «&»:

str.replace("&", "&");

Часто требуется удалять из строки пробельные символы (пробелы, символы табуляции и перехода на новую строку). QString имеет функцию, которая удаляет эти символы с обоих концов строки:

QString str = " ВОВ \t THE \nDOG \n";

qDebug() << str.trimmed();

Строку str можно представить в виде

_ _ _ВОВ_\t_THE_ _\nDOG_\n

Строка, возвращаемая функцией trimmed(), имеет вид

ВОВ_\t_THE_ _\nDOG

При обработке введенных пользователем данных нам часто необходимо, кроме удаления пробельных символов с обоих концов строки, заменить каждую последовательность таких символов одним пробелом. Именно это выполняет функция simplified():

QString str = " ВОВ \t THE \nDOG \n";

qDebug() << str.simplified();

Строка, возвращаемая функцией simplified(), имеет вид

ВОВ_THE_DOG

Строку можно разбить на подстроки типа QStringList при помощи функции QList::split():

QString str = "polluter pays principle";

QStringList words = str.split(" ");

В приведенном выше примере мы разбиваем строку «polluter pays principle» на три подстроки: «polluter», «pays» и «principle». Функция split() имеет необязательный третий аргумент, показывающий, надо ли оставлять пустые подстроки (режим по умолчанию) или нет.

Элементы списка QStringList могут объединяться в одну строку при помощи функции join(). Передаваемый функции join() аргумент вставляется между каждой парой объединяемых строк. Например, ниже показано, как создавать одну строку из всех строк списка QStringList, расположенных в алфавитном порядке и разделенных символом перехода на новую строку:

words.sort();

str = words.join("\n");

При обработке строк нам часто приходится определять, пустая строка или нет. Это делается при помощи вызова функции isEmpty() или проверкой равенства нулю возвращаемого функцией length() значения.

Преобразование строк const char * в QString в большинстве случаев выполняется автоматически, например:

str += " (1870)";

Здесь мы добавляем строку const char * в конец строки QString без выполнения явного преобразования. Для явного преобразования const char * в QString выполните приведение типа в QString или вызовите функцию fromAscii() или fromLatin1(). (Работа с литеральными строками в других кодировках рассматривается в главе 17.)

Для преобразования QString в const char * используйте функцию toAscii() или toLatin1(). Эти функции возвращают QByteArray, который может быть преобразован в const char *, используя QByteArray::data() или QByteArray::constData(). Например:

printf("User: %s\n", str.toAscii().data());

Для удобства в Qt предусмотрен макрос qPrintable(), который эквивалентен последовательности функций toAscii().constData():

printf("User: %s\n", qPrintable(str));

Когда мы вызываем функции data() или constData() для объектов типа QByteArray, владельцем возвращаемой строки будет этот объект. Это означает, что нам не надо беспокоиться о возможных утечках памяти — Qt вернет нам память. С другой стороны, мы должны проявлять осторожность и не использовать указатель слишком долго. Если объект QByteArray не хранится в переменной, он будет автоматически удален в конце выполнения оператора.

Программный интерфейс класса QByteArray очень похож на программный интерфейс класса QString. Такие функции, как left(), right(), mid(), toLower(), toUpper(), trimmed() и simplified(), существуют в QByteArray и имеют такую же семантику, как и соответствующие функции в QString. QByteArray полезно использовать для хранения неформатированных двоичных данных и строк с 8-битовой кодировкой текста. В целом мы рекомендуем использовать QString для хранения текста, а не QByteArray, потому что QString поддерживает кодировку Unicode.

Для удобства QByteArray всегда автоматически обеспечивает наличие символа '\0' после последнего байта, облегчая передачу объекта QByteArray функции, принимающей const char *. QByteArray также может содержать внутри себя символы '\0', что позволяет использовать этот тип для хранения произвольных двоичных данных.

В некоторых ситуациях требуется в одной переменной хранить данные различных типов. Один из таких методов заключается в представлении этих данных в виде QByteArray или QString. Например, в виде строки можно хранить как текстовое значение, так и числовое значение. Эти подходы обеспечивают максимальную гибкость, но лишают некоторых преимуществ С++, в частности связанных с безопасностью типов и высокой эффективностью. Qt обеспечивает значительно более удобный способ для хранения данных различного типа: QVariant.

Класс QVariant может содержать значения многих типов Qt, включая QBrush, QColor, QCursor, QDateTime, QFont, QKeySequence, QPalette, QPen, QPixmap, QPoint, QRect, QRegion, QSize и QString, а также такие основные числовые типы С++, как double и int. Класс QVariant может, кроме того, содержать контейнеры QMap, QStringList и QList.

Широкое распространение получило применение этого типа в классах отображения элементов, в модуле баз данных и в классе QSettings, позволяя считывать и записывать данные элементов, данные базы данных и пользовательские настройки в виде любого значения, допускаемого типом QVariant. Пример этого мы уже видели в главе 3, когда объекты QRect, QStringList и пара булевых значений передавались функции QSettings::setValue() и затем считывались как объекты QVariant.

Можно создавать произвольно сложные структуры данных, используя тип QVariant для обеспечения вложенных структур контейнеров:

QMap pearMap;

pearMap["Standard"] = 1.95;

pearMap["Organic"] = 2.25;

QMap fruitMap;

fruitMap["Orange"] = 2.10;

fruitMap["Pineapple"] = 3.85;

fruitMap["Pear"] = pearMap;

Здесь мы создали отображение со строковыми ключами (названия продукции) и значениями, которыми могут быть либо числа с плавающей точкой (цены), либо отображения. Отображение верхнего уровня содержит три ключа: «Orange», «Pear» и «Pineapple» (апельсин, груша и ананас). Значение, связанное с ключом «Реаг», является отображением, содержащим два ключа «Standard» и «Organic» (стандартный и экологически чистый). При проходе по ассоциативному массиву, содержащему объекты QVariant, нам необходимо использовать функцию type() для проверки находящегося в QVariant типа, чтобы можно было его правильно обработать.

Способ создания подобным образом структур данных может быть очень привлекательным, поскольку мы можем создавать любые структуры данных. Но удобство применения типа QVariant достигается за счет снижения эффективности и читаемости программы. Для хранения наших данных, как правило, предпочтительнее использовать соответствующий класс языка С++ там, где это возможно.

QVariant используется мета-объектной системой Qt и поэтому является частью модуля QtCore. Тем не менее, когда мы собираем приложение с модулем QtGui, QVariant может хранить такие типы, связанные с графическим пользовательским интерфейсом, как QColor, QFont, QIcon, QImage или QPixmap:

QIcon icon("open.png");

QVariant variant = icon;

Для извлечения значений этих типов из QVariant мы можем следующим образом использовать шаблонную функцию—член QVariant::value():

QIcon icon = variant.value();

Функция value() также может использоваться для преобразования между типами неграфического интерфейса и типом QVariant, однако на практике обычно используются функции преобразования вида to…() (например, toString()) для преобразования типов неграфического интерфейса.

QVariant может также использоваться для хранения пользовательских типов данных при условии обеспечения ими стандартного конструктора и конструктора копирования. Чтобы это заработало, прежде всего необходимо зарегистрировать тип, используя макрос Q_DECLARE_METATYPE() обычно в заголовочном файле после определения класса:

Q_DECLARE_METATYPE(BusinessCard)

Это позволяет нам написать следующий программный код:

BusinessCard businessCard;

QVariant variant = QVariant::fromValue(businessCard);

if (variant.canConvert()) {

BusinessCard card = variant.value();

}

Эти шаблонные функции—члены не будут работать с компилятором MSVC 6 из-за ограничений последнего. Если вы не можете отказаться от этого компилятора, вместо указанных функций используйте глобальные функции qVariantFromValue(), qVariantValue() и qVariantCanConvert().

Если в пользовательском типе данных предусмотрены операторы << и >> для записи и чтения из потока данных QDataStream, их можно зарегистрировать, используя функцию qRegisterMetaTypeStreamOperators(). Это позволяет, среди прочего, хранить параметры настройки пользовательских типов данных, используя QSettings. Например:

qRegisterMetaTypeStreamOperators("BusinessCard");

В данной главе основное внимание было уделено контейнерам Qt, а также классам QString, QByteArray и QVariant. Кроме этих классов Qt имеет несколько других контейнеров. Один из них — QPair, который просто хранит два значения и аналогичен классу std::pair. Еще одним контейнером является QBitArray, который мы будем использовать в первом разделе главы 19. Наконец, имеется контейнер QVarLengthArray — низкоуровневая альтернатива вектору QVector. Поскольку он заранее выделяет память в стеке и не допускает неявное совместное использование, накладные расходы у него меньше, чем у вектора QVector, что делает его более подходящим в напряженных циклах.

Алгоритмы Qt, включая несколько не рассмотренных здесь, например qCopyBackward() и qEqual(), описаны в документации Qt, которую можно найти по адресу http://doc.trolltech.com/4.1/algorithms.html. Более подробное описание контейнеров Qt, в том числе информацию об их временных и объемных характеристиках, можно найти на странице http://doc.trolltech.com/4.1/containers.html.

Глава 12. Ввод—вывод

QT 4: программирование GUI на С++

Почти в каждом приложении приходится читать или записывать файлы или выполнять другие операции ввода—вывода. Qt обеспечивает великолепную поддержку ввода—вывода при помощи QIODevice — мощной абстракции «устройств», способных читать и записывать блоки байтов. Qt содержит следующие подклассы QIODevice:

• QFile — получает доступ к файлам, находящимся в локальной файловой системе или внедренным в исполняемый модуль,

• QTemporaryFile — создает временные файлы в локальной файловой системе и получает доступ к ним,

• QBuffer — считывает или записывает данные в QByteArray,

• QProcess — запускает внешние программы и обеспечивает связь между процессами,

• QTcpSocket — передает поток данных по сети, используя протокол TCP,

• QUdpSocket — передает и принимает из сети дейтаграммы UDP.

QProcess, QTcpSocket и QUdpSocket являются последовательными устройствами, т.е. они позволяют получить доступ к данным только один раз, начиная с первого байта и последовательно продвигаясь к последнему байту. QFile, QTemporaryFile и QBuffer являются устройствами произвольного доступа и позволяют считывать байты многократно из любой позиции; они используют функцию QIODevice::seek() для изменения положения указателя файла.

Кроме этих устройств Qt предоставляет два класса высокоуровневых потоков данных, которые можно использовать для чтения и записи на любое устройство ввода—вывода: QDataStream для двоичных данных и QTextStream для текста. Эти классы учитывают такие аспекты, как порядок байтов и кодировка текста, позволяя работающим на разных платформах и в разных странах приложениям Qt считывать и записывать файлы друг друга. Это делает классы Qt по вводу—выводу более удобными, чем соответствующие классы стандартного С++, при использовании которых решать подобные проблемы приходится прикладному программисту.

QFile позволяет легко получать доступ к отдельным файлам, независимо от того, располагаются они в файловой системе или оказываются внедренными в исполняемый модуль приложения как ресурсы. Для приложений, которым приходится работать с целыми наборами файлов, в Qt предусмотрены классы QDir и QFileInfo, которые позволяют работать с каталогами и получать сведения о файлах, расположенных внутри каталогов.

Класс QProcess позволяет нам запускать внешние программы и устанавливать связь с ними через стандартные каналы ввода, вывода и ошибок (cin, cout и cerr). Мы можем устанавливать переменные среды и рабочий каталог, которые будут использоваться внешним приложением. По умолчанию связь с процессом осуществляется в асинхронном режиме (без блокировок), но все же остается возможной блокировка определенных операций.

Работа с сетью, а также чтение и запись документов XML настолько важные темы, что будут рассмотрены отдельно в главах 14 и 15, специально им посвященным.


Обобщенные алгоритмы | QT 4: программирование GUI на С++ | Чтение и запись двоичных данных