Qt4_字符串、字节数组和变量

字符串

我们将从 QString开始。每一个图形用户界面(GUI) 程序都会用到字符串,不仅仅是为用户界面,更多的是为数据结构所用。 C++ 本身提供两种宇符串:传统的 C语言型的,以" \0",结尾的字符数组和 std::string类。与这两种字符串不同, QString 支持 16 位 Unicode 值。Unicode码以 ASCII 码和 Latin-1码为子集,具有它们常用的数字值。但由于 QString是 16位的;它可以表示数千种其他字符以表达世界上绝大多数的语言。

当使用 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"); 

使用 QString的 sprintf()函数则是连接字符串的另一种完全不同的方法:

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

这个函数支持与 C++库sprintf()函数相同的格式说明符。在之前的例子中, str 被赋值为"perfect competition 100.0%"。

从其他字符串或者数组来建立一个字符串的另一种方法是使用arg():

st r '" QSt ring ("%1 %2 (%3s-%4s)") 
.arg( "permissive") .arg("society") .arg(1950) .arg(1970); 

在这个例子中,“permissive” 会取代" %1",“society” 会取代"%2",“1950” 会取代"%3",而 “1970"会取代”%4"。相应的结果为"permissive society (1950s-1970s) “。arg()的重载可以处理各种数据类型。对于控制宇段长度、数值基数或者浮点精度等,一些重载有额外的参数。相较于 sprintf()而言, arg()通常是一个更好的解决方案,因为是它类型安全的,完全支持 Unicode 编码,并且允许译码器对”%n"参数进行重新排序。

通过使用 QString::number()静态函数, QString 可以将数字转换为字符串:

str = QString::number(59.6); 

或者也可以使用 setNum() 函数:

str.setNum(59.6); 

还可以使用 tolnt()、toLongLong()、toDouble()等来完成从字符串到数字的逆转换。例如:

bool ok; 
double d = str.toDouble(&ok); 

这些函数接受一个任选的指向 bool 变量的指针,并且根据转换成功与否来将变量值设为 true或false 如果转换没有完成,这些函数将返回0。

一旦有了字符串,我们通常都希望从字符串中提取出所需的部分。 mid() 函数返回在给定位置(第一个参数)开始且达到给定长度(第二个参数)的子串。例如,下面的代码在控制台上打印出"pays":

QString str = "polluter pays principle"; 
qDebug() << str.mid(9, 4); 

如果省略第二个参数, mid() 函数返回在给定位置开始到字符串末端结束的子串。例如,下面的代码在控制台上打印出"pays principle" :

QString str = "polluter pays principle"; 
qDebug() << str.mid(9); 

也可以使用 left()和 right() 函数来完成相似的任务。它们俩都可以接收字符的数量 并且返回字符串的前 n个字符或最后 n个字符。例如,下面的代码在控制台上打印出 “polluter principle”:

QStrirg str = "polluter pays principle"; 
qDebug() << str.left(8) << " "<< str.right(9);

如果想查明一个字符串是否包含一个特定的字符、子串或者正则表达式,可以使用QString中indexOf()函数:

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");

字符串的结果是"a smy day" 。上面的代码可以用 remove()和 insert()重新写为:

str.remove(2, 6); 
str. insert(2, "sunny"); 

首先,我们去掉从第二个位值开始的 6个宇符,字符串因此变为"a day"气(含有两个空格 ),然后在第二个位置插人"sunny"。

replace()函数的重载版本让第二个参数代替所有第一个参数出现的地方。例如,下面是使用"∓"代替字符串中所有的"&":

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

我们经常需要删除一个字符串中空白处的空格(比如空格符、制表符、换行符等 )。 QString有一个可以从字符串的两端删除空白处的空格的函数:

QString str ="  BOB \t THE  \nDOG \n"; 
qDebug() << str.trimmed(); 

字符串 str 描述如下:
Qt4_字符串、字节数组和变量
由trimmed() 返回的字符串为:
Qt4_字符串、字节数组和变量
当处理用户输人数据时,除了从字符串的两端删除空白处的空格特外,通常还想用简单的空格符代替字符串内部每一连续空白处的空格:

QString str ="  BOB \t THE  \nDOG \n";
qDebug() <<str.simplified(); 

simplified()返回的字符串为:
Qt4_字符串、字节数组和变量
使用 QString::split(),可以把一个字符串分成一些QStringList 子串:

QString str = "polluter pays principle";
QStringList words = str.split(" "); 

在上面的例子中,我们把宇符串 "polluter pays principle"分成了三个子串, “polluter”、“pays"和"principle”。

使用 join()函数, QStringList 中的项可以连接起来形成一个单一的字符串。在每一对被连接的字符串之间都要插人 join()的参数。例如,下面是如何创建一个由 QStringList 包含的所有字符串组成的、按字母表顺序排列且由换行符分隔的简单字符串:

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

当对字符串进行操作时,通常需要判断字符串是否为空。这可以通过调用 isEmpty()或者检查length()是否为 0来完成。
大多数情况下,从 const char *字符串到 QString的转换是自动完成的,例如:

str +=" (1878)"; 

这里,我们采用无格式形式为 QString 加上一个 const char *。为了明确地将 QString转换为const char* ,可以只使用一个 QString强制转换,或者调用 fromAscii()或fromLatin1()。

字节数组

要将 QString 转换为 const char*,可以使用 toAscii()或 toLatin1()函数,它们返回 QByteArray,而利用 QByteArray::data()或 QByteArray::constData(),可以将 QByteArray 转换为constData()。例如:

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

为了方便起见, Qt 提供了 qPrintable()宏用来执行与序列 toAscii().constData():相同的功能:

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

当在 QByteArray 上调用 data()或 constData()时,返回的字符串属于 QByteArray 对象。这就意味着不必为内存泄漏而担心了, Qt 将为我们重新收回内存。另一方面,必须注意不要太长时间地使用指针。如果 QByteArray 没有存储在一个变量中,那么它将在声明的末端自动删除。

QByteArray 类有一个与 QString很相似的应用编程接口。诸如 left()、right()、mid()、toLower()、toUpper()、trimmed()和 simplified()等函数,在 QByteArray中的语义形式与在 QString中的相同。

QByteArray对于存储原始的二进制数据以及 8 位编码的文本字符串非常有用。一般说来,我们推荐使用 QString而不是 QByteArray来存储文本,因为 QString支持 Unicode 编码。

为方便起见, QByteArray 自动保证"最后一个项之后的项"总为"\0",这使得利用 const char*可以很容易地将 QByteArray 传递给一个函数。 QByteArray 还支持嵌入的"\0"宇符,以允许我们存储任意的二进制数据。

在某些情况下,我们需要在同一个变量中存储不同类型的数据。一种方法是像 QByteArray或QString一样,对数据进行编码。例如,字符串可以支持文本值或者以字符串形式支持数字值。这些方法很灵活,但是它抛弃了 C++的一些优点,尤其是类型的安全性和效率。Qt提供了一个更加灵巧的方法,也就是 QVariant,来处理那些能够支持不同数据类型的变量。

变量

QVariant 类可以支持许多种 Qt 类型的值,除了基本的 C++ 数字类型(如 double int) ,还包括QBrush、QColor、QCursor、 QDateTime、QFont、QKeySequence、QPalette、QPen、Pixrnap、QPoint、QRect、QRegion、QSize和 QString。 QVariant类也支持容器类,如 QMap <QString,QVariant> QStringList和QList<QVariant>

在项视图类、数据库模块和 Settings 中广泛使用了变量,变量允许我们读写项数据、数据库数据以及任意与 QVariant 兼容的用户首选参数。

通过容器类的嵌套值,可以利用 QVariant创建任意复杂的数据结构:

QMap<QString,QVariant> pearMap; 
pearMap["Standard"] = 1.95; 
pearMap["Organic"] = 2.25; 
QMap<QString, QVariant> fruitMap;
fruitMap["Orange"] = 2.10; 
fruitMap["Pineapple"] = 3.85; 
frtlitMap["Pear"] = pearMap;

这里,我们利用字符串键(产品名)和浮点数(价格)或者映射的值创建一个映射。*映射包括三个键:“Orange”、“Pear”、“Pineapple”。与"Pear"键相关联的值是一个包含两个键(“Standard"和"Organic”)的映射。当遍历一个支持变量值的映射时,需要使用type()来检查变量保持所支持的类型,以便做出适当的反应。

创建这样的数据结构是非常吸引人的,因为能够以任意方式组织数据。但是QVariant的便利性是以降低效率及可读性为代价的。通常,定义一个适当的C++类来存储随时可能的数据,是值得的。

Qt的元对象系统使用QVariant,因此它也是QtCore模块的一部分。但是,当与QtGui模块相连时,QVariant可以存储与图形用户界面相关的类型,例如QColor、QFont、QIcon、QImage和QPixmap:

QIcon icon("open.png");
QVariant variant = icon;

为了从QVariant中获得与图形用户界面相关的值,可以使用如下的QVariant::value<T>()模板成员函数:

QIcon icon = variant.value<QIcon>();

value<T>()函数也可以用非图形用户界面数据类型和QVariant之间进行转换,但实际上对于非图形界面类型,通常使用to…()作为非图形用户界面数据类型的转换函数[例如toString()]。

如果自定义数据类型提供了默认的构造函数和副本构造函数的话,QVariant也可以用来存储它们。为此,必须首先使用Q_DECLARE_METATYPE()宏注册数据类型,尤其是在类定义下的头文件中:

Q_DECLARE_METATYPE(BusinessCard);

这使我们可以使用如下的方式来编写代码:

BusinessCard businessCard;
QVariant variant = QVariant::fromValue(businessCard);
...
if(variant.canConvert<BusinessCard>()){
   BusinessCard card = variant.value<BusinessCard>();
   ...
}

由于编译器的局限性,在MSVC 6中,这些模板成员函数并不可用。如果需要用到这个编译器,可以用qVariantFromValue()、qVariantValue<T>()和qVariantCanConvert<T>()全局函数来代替。

如果自定义数据类型采用<<和>>操作符来完成从QDataStream的读写,就可以使用qRegisterMetaTypeStreamOperators<T>()来注册这些自定义数据类型。这就可以利用QSettings在其他类型之中存储自定义数据类型的首选参数。例如:

qRegisterMetaTypeStreamOperator<BusinessCard>("BusinessCard");
上一篇:Qt4中学习使用QtCharts绘图一:


下一篇:python-matplotlib的Qt4 mplot3d