1.与STL对应Qt库有自己的容器类
QVector<T>、QList<T>、QLinkedList<T>、QStack<T>、QQueue<T>、 QMap<K,T>、QHash<K,T>等,Qt库的容器的基本操作与STL库基本相同,但是具体实现不同。
对于容器元素的访问支持3种方式:
(1)通过下标访问
(2)通过迭代器访问
(3)通过成员函数访问(比如value(idx)带越界检测的访问,at(idx)不带越界检查的访问)
与STL库不同的是,Qt库的许多类都采用了隐含共享技术(也称写时复制),是C++中广泛使用的用来提高资源利用率和降低对象拷贝频率的优化技术,但对使用者来说是不可见的。写时复制具体过程为:当类之间发生深度拷贝时,不会立即拷贝,而会共享内存,2个类通过引用计数指向同一内存地址,当某个类要修改更新内存的值此时才发生深度复制产生2块内存(以前的内存引用计数减1)
2.迭代器(只读迭代器和读写迭代器)
Qt同时支持Java风格和STL风格的迭代器。
Java风格迭代器强调"间"的概念,迭代器自身并不指向容器中的元素,而是代表元素与元素之间的位置,使用if(iter.hasNext())来判断是否迭代到尾部
Java迭代器的实现独立于容器,而STL迭代器的实现保护在容器类的实现中。
QVector<int>qvec_int;
QVectorIterator<int> iter(qvec_int); //只读迭代器
while(itr.hasNext()) //itr.hasPrevious()
{print_IternValue(iter.next())}; //print_ItemValue(itr.peekPrevious());
QMutableVectorIterator<T> 读写迭代器
QVector<int>qvec_int;
QMutableVectorIterator<int>iter(qvec_int);
while(i.hasNext())
{int val = iter.next();
if(val< 0) {iter.setValue(qAbs(val));}
else if( val = 0) {iter.remove();}
}
STL库中的迭代器在各个容器类中实现,对容器操作灵活。
QVector<int> qvec_int;
QVector<int>::const_iterator itr = qvec_int.begin();//只读迭代器
QVectoκin t>: :const_iterator end = qvec_int.end();
for(;itr!= end;++itr)
{print_ IternValue(*itr);}
QVector<int> qvec_int;
QVector<int>::iterator itr = qvec_int.begin() ;//选择容器的读写迭代器;
QVectoκint>:: iterator end= qvec_int.end();
for(;itr != end;)
if((* itr) < O){(*itr) = qAbs((*itr));++itr;}
else if(O = (*itr)){itr = qvec int.erase(itr);} //删除元素
}
3.STL库中的针对迭代器的通用算法
#include <QtAlgorithms>
(1)void qFill( Forwardlterator begin, Fonvardlterator end, const T & value)
算法能够自动地剧一个特定值( value )来对[begin,end]范回内的元索进行赋值。
(2)Outputiterator qCopy( InputIteror beginl, Inputiterator end1, Outputlterator begin2)
算法将[beginl ,end1]内的元素按照顺序复制到[begin2,...]范围内。
(3)void qSort( RandomAccessiterator be自1, Randon1Accesslterator end)
void qSort( RandomAccesslterator begin, RandomAccessiterator end, LessThan lessThan)
算法将[beginl,end1]范围内的序列按升序排序。第一个声明函数使用operator<(),第二个声明函数使用提供的比较器对象来决定顺序。
(4)inputlterator qFind( lnputlterator begin, Inputlterator end, const T & value)
线性收索算法在[begin,end]范围内的元素序列中查找valt1e 。返问一个迭代器对象,指向在范围[begin,end]内value第l次出现的位置。如果value不在范围内,qFind()返回end。
(5)RandomAccesslterator qBinaryFind(RandomAccessiterator begin, RandomAccesslterator end,const T &value)
二分收索,同上
5.其他容器类
QLinkedList<T> 单链表实现
Map:enumMap.insert(pair<int, Cstring>(1, “One”)); enumMap.insert(map<int, Cstring>::value_type (1, “One”));
6.Qt提供了QIODevice类进行外设到内存的数据输入输出,以下类都继承于该类。
QFile提供对文件的打开等操作,对文件的读写不进行任何字符的转换
QBuffer从QByteArray中读取数据。
QTemporaryFile 在本地文件系统中创建并读写临时文件
QFile 在本地文件系统或者嵌入式资源中读写文件
QProcess 运行外部程序ft处理进程间的边信
QTcpSockei 利用TCP 协议在网络设备上传输数据
QUdpSocket 利用UDP 协议在网络设备上传输数据
QSslSocket 利用SSL/TSL 协议在网络设备上传输加密数掘
QDataStream和QTextStream类可以是以上所有类的上层,可以提供二进制流和文本格式的读写操作。
QDataStream提供按二进制形式操作文件,格式化读写或者串行操作(读写整型、浮点型数据等)
QTextStream提供按文本格式操作文件,格式化读写
7.QDir提供了对目录的遍历等方法
QFileInfo保存某个文件的具体属性
QFileSystemWatcher 类来监视指定目录或者文件的变化,addPath()、addPaths()函数用来监视指定的一个或者多个文件或者目录。
QXML提供了对XML文件的处理方法(有2中处理方式DOM文档对象模型和SAX简单应用程序接口处理方式,后者适合于处理大型文档)
常见操作XML类有QXmlStrea1nReader、QDornDocument、QxmlDefaultHandler、QXrnl Si1npleReader等
QXmlSimpleReader是SAX模型实现的解析器
8.多线程
同步方法有:互斥量、信号量、读写锁、条件变量
Qt 包含下面一些线程相关的类:
(1) QThread 提供了开始一个新线程的方法;
(2) QThreadStorage 提供逐线程数据存储:
(3) QMutex 提供相互排斥的锁,或互斥量:
(4) QMutexLocker 是一个互斥锁的,它可以自动对QMutex 加锁与解:
(5) QReadWriterLock 提供了一个可以同时读操作的锁;
(6) QReadLocker 与QWriteLocker 是便利类,它自动对QReadWriteLock加锁与解锁:
(7) QSemaphore 提供了一个整型信号量,是互斥量的泛化;
(8) QWaitCondition 条件变量提供了一种方法,使得线程可以在被其他线和唤醒它之前一直休眠。
线程的实现需要继承Qthread类并实现run()方法
#include <Qthread>
class MyThread : public Qthread{
Q_OBJECT
protected:
void run();
}
void MyThread::run(){
printf(”Thread Begin,sleep two seconds !\n");
sleep(2);
printf(”Thread End,exit!\n”);
}
int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);
MyThread newThread;
newThread.start();
return a.exec();
}
QWaitCondition 类允许线程在某些情况发生时唤醒其他的线程。当一个或多个线程阻塞等待时,可用wakeOne()随机唤醒一个线程, wakeAll()唤醒所有线程。其设计也需要一个mutex量来互斥的访问条件。与linux中使用条件变量的代码结构基本相同。
线程池:
Qt4 提供线程池QThreadPool管理和回收QThread 创建的线和对象, 从而减少由于线程创建和销毁所带来的资源开销。每一个Qt 的应用程序都有一个全局的QThreadPool对象,可以通过函数globalInstance()调用该线程池全局对象。事实上,这里是使用QThreadPool类来运行一个QRunnabl e 对象,它维护了一个线程
池。通过调用QThreadPool::start(runnable),把一个QRunnable 放入QThreadPool 的运行队列中;只要钱程是可见的,QRunable 将会被拾起并且在那个线程里运行。尽管所有的Qt 应用程序都有一个全局的线程池,且它是通过调用QThreadPool::globalInstance()可见的, 但通常是显式地创边井管理一个私有的QThreadPool 实例。
#include <QtCore/QCoreApplication>
#include <QthreadPool>
class HelloWorldTask: public QRunnable
{void run()
{printf(”This is thread HelloWroldTask.\n”);}
}
class CThread : public QRunnable
{ void run()
{printf(”This is thread CThread.\n”);}
}
int main(int ar民char *argv0)
{QCoreApplication a(argc, argv);
HelloWorldTask *hello = new HelloWorldTask();
CThread* thread = new CThread();
QthreadPool::globalInstance()->start(hello);
QthreadPool::globalInstance()->start(thread);
return a.exec();
}
9.数据库
QT库提供了以下数据库驱动
QDB2 LBM 082
QIBASE Borland I nterBase 驱动
QMYSQL MySQL 驱动
QOCI Orcale Call lnterface 驱动
QODBC OBDC 驱动(包括Micofosfot SQL Server)
QPSQL PostgreSQL 驱动
QSQLITE SQLite version 3 或更高版本
QSQLITE2 SQLite version 2
QtDS Sybase Adaptive Server
Qt 的数据库棋快主要通过QSqlDatabase 类对象实现对数据库的绑定和连接,然后通过QSqlQuery
类对象执行相关的SQL语句对数据库进行操作。
mysql数据库的连接:
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); //指定使用的驱动
db.setHostNan1e("qpk"); //数据库所在的远程主机名或域名
db.setDatabaseName("bookmis.db"); //查询的数据库名
db.setUserName("qpk"); //登录数据库的用户名
db.setPassword("cugbupt");//密码
bool ok = db.open();
QSqlQuery类对数据库的操作方式
QSqlQuery query;
bool value;
query.exec("SELECT * FROM book");//在QSqlQuery 类中当执行execO后会把指针放在记录集中第一个记录之上,所以需要调用QSqlQuery::next()来获取第一个数据.
while(query.next()){QString name = query.value(O).toSt1ing();}//QSq!Query: :value()函数得到当前记录区域中的数据,value()默认返回的是一个QVariant类型,可以使用QVariant::toString 或QVariant::toInt().
value=query.exec("delete from book where ID=’”+id+’””);
value=query .exec("update book name set name='+nan1e+’where ID='+ id+ ’");
10.QT网络编程
获取主机信息:包括主机名、IP地址等。在Qt中,QHostlnfo类用来获取主机网络配置的相关信息。该类提供了两个常用的静态函数来获取机信息, 一个是使用lookupHost( const QString & na1ne, QObject*receiver, const char *1ne1nber)函数异步查询主机信息,并且在主机查询完毕后发出一个信号。
另一个足使剧QHostlrlfo 企omName( const QString & name)的数阻塞查询主机信息, 并且返回一个QHostlnfo 对象。
QHostlnfo::lookuphost("wwvv.yyy.com",this,SLOT(handlerResults(QHostInfo)));
QHostlnfo::lookuphost("192.168.1.102",this,SLOT(handlerResults(QHostInfo)));
QHostlnfo host= QHostlnfo::FromName("www .yyy.com ");
QTcpSocket、QTcpServer 类用来实现TCP 客户端和服务器端的应用程序.
QUdpSocket 类可以用来发送束l接收UDP 数据报。
QHttp、QNetworkAccessManager 等类来构建HTTP客户端.
QFtp 类来实现异步工作的FTP 协议的客户端,同时也提供了多种操作来执行常见的FTP操作.
11.QT的信号与槽机制
信号与槽是Qt 的核心机制之一,应用于对象之间的通信,使用该机制可以在对象之间彼此并不了解的情况下将它们的行为联系起来。信号和槽能携带任意数量和任意类型的参数,它们是类型完全安全的,不会像回调函数那样产生core dumps。
所有从QObject 或其子类(例如QWidget)派生的类都能够包含信号和槽。
当对象改变其状态时, 信号就由该对象发射(emit)出去, 这就是对象所要做的全部事情,它不知道另一端是谁在接收这个信号。
糟用于接收信号, 但它们是普通的对象成员函数。一个柄并不知道是否有任何信号与自己相连接。而且,对象并不了解具体的通信机制。
通常Qt 的控件都提供了常用的信号与槽,在开发过程中无需定义可以直接使用这些内容的信号与槽。
包含信号或槽的类需要使用Q_OBJECT宏放在类的开始位置,当MOC元对象预编译器发现该宏时就会将该类文件生成一个XXX.MOC.CPP的文件,用于编译。
Q_OBJECT宏可以表明该类继承自QObject类,可以调用connect,disconnect等方法。
信号:
如果存在多个梢与某个信号相关联,那么,当这个信号被发射时争, 这些槽将会一个接一个地执行,但足它们执行的顺序将会是随机的、不确定的,不能人为地指定哪个先执行、哪个后执行。
信号的声明是在头文件中进行的, 使用了signals 关键:例如, 下面在类中定义了三个信号:
signals: //qt的关键字
void mySignal();
void mySignal(int x);
void mySignal(int x,int y);
信号类似于函数定义但是没有函数体,返回总是为void,并且可以重名定义。
槽:
槽是普通的C++成员函数,可以被正常调用,他们唯一的特殊性就是很多信号可以与其相关联。当与其关联的信号被发射时,这个槽就会被调用。槽可以有参数,但槽的参数不能有缺省值。
既然梢是普通的成员函数, 因此与其他的函数一样, 它们也有存取权限。槽的存取权限
决定了谁能够与其相关联。同普通的C++成员函数一样,槽函数也分为三种类型,即public slots、protected slots 和private slots。
public slots : 在这个区内声明的槽意味着任何对象都可将信号与之相连接。
protected slots : 在这个区内声明的槽意味着当前类及其子类可以将信号与之相连接。
private slots : 在这个区内声明的槽意味着只有类自己可以将信号与之相连接。
槽也可以声明为虚函数, 这也是非常有用的。
槽的声明也是在头文件中进行的。例如,下面声明了三个槽 :
public slots:
void myslot();
void mySlot(int x);
void mySignalParam(int x,int y);
信号不需要实现,槽是需要实现的。
信号与槽的关联:
使用connect()函数将某个对象的信号与另外一个对象的槽函数相关联,这样发送某信号时,接收对象的槽函数就会被执行。
使用disconnect()函数断开信号与槽之间的连接。(当对象销毁时,会自动断开连接)
bool QObject::connect( const QObject *sender,const char* signal,const QObject* receiver,const char* member) [static]
bool QObject::disconnect( const QObject *sender,const char* signal,const QObject* receiver,const char* member) [static]
如connect()函数声明所示,其将发射者sender对象中的信号signal与接收者receiver对象中的member 槽函数连接起来。
通常情况下无需显示调用断开信号与糟之间的关联,当对象销毁时,会自动断开连接。
然而有三种情况必须使用disconnect() 函数:
(1)断开与某个对象相关联的任何对象的任何信号和槽:当在某个对象中定义了多个信号,这些信号与另外若干个对象中的槽相关联,如果我们要切断这
些关联,可以使用disconnect(myObject,0,0,0)或者myObject->disconnect()
(2)断开与某个特定信号的任何关联:disconnect(myObject,STGNAL(mySignal()),0,0)或myObject->disconnect(SIGNAL(mySignal()))
(3)断开两个对象之间的关联:disconnect( myObject,0,myReceiver,0)或myObject->disconnect(myReceiver)
在disconnect函数中0可以用作一个通配符,分别表示任何信号、任何接收对象、接收对象中的任何槽的数。但是发射者sender不能为0,其他三个参数的值可以等于0。
函数指针和模板类不能作为信号和槽的参数
public slots:
void apply( void (*applyFunction)(QList*, void*), char*); //不合法
void MyWidget::setLocation (pair<int,int> location);//不合法
但可以使用typedef来规避这种限制,以下是合法的。
typedef pair<int,int> lntPair;
typedef void (*ApplyFunction)(QList*, void*);
public slots:
void apply( Apply Function Type, char*);
void setLocation (lntPair location);
12.Qt事件
几乎当今所有的应用程序开发框架都是以事件为驱动的,Qt 也不例外。
在Qt的事件机制中,Qt将环境变化的信息组织成一个继承自QEvent的对象并通过调用QObject::event()将
之发送到继承白QObject的目标对象事件处理队列中,交由目标对象去响应。
Qt 的事件处理的大致流程如下:
(1) Qt 的幸事件循环exec()感知某个事件的产生。
(2) Qt 创建此事件所对应的事件对象(派生自QEvent),通过QObj ect::event()进行派送。
(3)事件的目的对象根据事件类型,将其分配给对应的事件处理函数。
(4)在事件处理函数中根据事件的需要对事件进行处理(如发射特定信号emit)。
一般情况下, 事件来臼底层窗口系统, Qt 的主事件循环( QApplication: :exec() ) 从事件
队列取得本地窗口系统的事件,并将它们转变成QEvent , 然后将转换好的事件发送给目标
QObject , 但是也有可能通过QApplication 类的QApplication::sendEvent() 和
QApplication: :postEvent()来手动发送事件。
根据事件产生的方式, Qt 事件机制将事件分为以下三类:
( 1 )原生事件( Spontaneous ) , 由窗口系统产生, 它们被放到系统队列中, 通过事件循
环逐个处理。
(2 )转发事件( Posted ),由Qt 或是应用程序产生,它们被Qt 组成队列,再通过事件循
环处理。
(3 )发送事件( Sent ),由Qt 或是应用程序产生, 但它们被直接发送到目标对象。
根据事件的处理方式,上述三类事件又可以分为异步处理事件( Spontaneous 事件、
Posted 事件)和同步处理事件( Sent 事件)。
在Qt 的主事件循环I j 1 处理的是异步事件。首先, 事件循环处理所有的Posted 事件,
直到队列空。然所再处理所有的Spontaneous 事件, 最后它处理所有的因为处理
Spontaneous 事件而产生的Posted 事件。Send 事件并不在事件循环内处理, 它们都直接被发送到目标对象。
常见的一些事件:QKeyEvent键盘事件、QMouseEvent鼠标事件、QDragMoveEve拖放事件、socket事件等
Qt 为不同的事件提供了不同的处理函数,用户可以通过重新实现相应的事件处理函数来响应井处理对应的事件。
( 1 ) void mousePressEvent(QMouseEvent *) : 当鼠标在组件区域内被按下时, 会调用此
事件处理函数。
( 2) void mouseMoveEvent(QMouseEvent *): 当鼠标在组件区域内移动时, 会调用此事
件处理函数。
Qt 允许用户根据自身的需要自定义事件。Qt 中的自定义事件很简单, 同创
建自定义窗口组件很相似, 都是要继承一个类(QEvent)进行扩展。
对于自定义事件的响应, 可以通过重新实现目标对象的customEvent(QEvent * event)
函数来对自定义事件地行处理, customEvent 是QObject 提供的用以处理用户自定义事件的
方法,参数event 就是由Qt 事件系统所接收到的事件枚举值大于等于QEvent::User 的事件对
象指针.
void CustomEventReceiver::customEveot( QEvent *event )
{switch(event->type()) {
case CustomEventType l:...
break;
case CustomEventType2:...
}}
12.MVC模型/视图(Model-View-Controller设计模式)
MVC把交互系统的组成分解成模型(Model)、视图(View),控制(Controller)三种部件。
Model是应用程序对象,View是它的屏幕表示,Controller定义了用户界面如何对用户输入进行响应.
13.Qt常用控件
每个控件都有自己的信号和槽,通过信号与槽的连接可以实现用户交互。
QButton、QPushButton、QButtonGroup、QRadioButton、QLineEdit、QTextEdit、QTime、QTimeEdit、QDate、QDateEdit、QCalendarWidget、QCombox、QLCDNumber、QProgress Bar、QSpinBox、QDoubleSpinBox、QSlider、QTreeWidget、QTabWidget等
14.布局管理
Qt提供了3种布局管理方法
(1)绝对位置法:该布局方法通过调用基类QWidget提供的setGeometry()函数来设置子窗口部件在父窗口中显示的国定大小和位置。
(2)人工位置法:该布局方法通过调用基类QWidget提供的setGeometry()函数来设置子窗口部件在父窗口中显示的国定大小和位置。但与绝对位置法不同的是, 该方法通过重载voidQWidget::resizeEvent( QResizeEvent *)函数使得子窗口部件大小和父窗口部件大小成比例。
(3)布局管理器方式:该布局方法为不同类型的窗口部件提供一系列合适的默认值, 并且会根据窗口的大小策略和最大、最小尺寸,自动调整布局来响应样式、窗口人小的变化。
Qt 布局管理器负责在父窗口部件区域内存放子窗口部件。
QLayout 类是布局管理器的基类,它的派生类主要有QBoxLayout、QFormLayout、QGridLayout 和QStackedLayou t, 而QBoxLayout 又有两类派生类QHBoxLayout 和QVBoxLayout。
15.对话框QDialog
Qt实现了一些标准对话框:QAbstractPrintDialog、QColorDialog、QErrorMessage、QFileDialog、QFontDialog、QlnputDialog、QPageSetupDialog、QMessageBox、QProgressDialog、QWizard等
16.QMainWindow主窗口应用程序
主窗口和对话框的区别:主窗口通常包括菜单栏、工具栏、工作区和状态栏,主窗口中也可以有控件;而对话框可以理解为添加控件的一个容器。
一个应用程序可以有多个主窗口和多个对话框。一个应用程序的主窗口必须从QMainWindow继承,而对话框必须从QDialog继承。
17.XML文件解析方法
Qt提供了3种XML文件解析方法,其中DOM和SAX也被其他语言所常用。
QXmlStreamReader和SAX模型将处理XML中的10种记号:
startdocument xml处理开始时将遇到此记号或产生此事件
enddocument xml处理结束时将遇到此记号或产生此事件
startelement 比如<iter>
sendelement 比如</iter>
characters <iter>与</iter>之间的内容
comment 比如<!--fix--> 注释
DTD 比如<!DOCTYPE>
entityreference 比如™ 引用实体
processinginstruction 比如<?alert?> 处理指示标记,一般在第一行
invalid 比如>&<! 无效或错误标记
(1)QXmlStreamReader(顺序模型) 最快最简单,适于大型文档(不会将整个文档读入内存),适于查找性解析XML,只使用readNext()来查找下一个记号。
QXmlStreamReader提供了迭代的方法读写一个标记.readNext(),并提供了对.readNext()读回的标记的判断(是10种标记中的哪一种)。具体的解析过程需要用户根据XML文档自行实现。
(2)DOM(对象模型) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合(以树形结构解析)。便于对XML进行解析和修改。
对应 XML 语法中的每个概念定义相应的类:元素、属性、实体、文档。解析器读入 XML 文档的时候,建立 XML 语法和类之间的一对一映射。比如,每遇到一个标记,就实例化一个元素类。
每一个子树都是一个QDomElement元素
(3)SAX(事件模型) SAX是一种事件API,处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。
SAX解析器采用了基于事件的模型,它在解析XML文档的时候可以触发一系列的事件,当发现给定的tag的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。
一般实现方法为继承QXmlDefaultHandler类,并实现遇到标记时需要调用的虚函数,一般需要实现startelement(),endelement()
Qt也提供了3种写入XML的方法:
(1)使用QXmlStreamWriter。对于创建xml文档比较容易,对已有xml进行修改比较困难。
(2)使用DOM模型。对于已有的xml进行修改比较容易
(3)手动写入。有用户自行创建文件,按一般文件的方式写入字符串
18.XML语法简介
1).xml用途:xml仅仅是纯文本,用于应用程序之间传输和存储数据,不同于HTML用于显示数据
2).一个 XML 文档实例
<?xml version="1.0" encoding="ISO-8859-1"?>
<note>
<to>George</to>
<from date="08/08/2008">John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
第一行是 XML 声明,处理指示
根元素有4个子元素(to, from, heading 以及 body)
XML 文档必须包含根元素,该元素是所有其他元素的父元素,XML 文档中的元素形成了一棵文档树。
所有 XML 元素都须有关闭标签,HTML则可以没有。并且XML开始标签和关闭标签必须正确嵌套。
<from date="08/08/2008">John</from> 这个整体是一个以<to>标签引导的元素,元素包括元素名、属性(属性值必须加引号)和内容文本,还可以包含子元素。
3).实体引用
在内容文本等中需要使用XML的缺省字符时,可以使用实体引用。
<message>if salary < 1000 then</message> 代表的字符串是“if salary < 1000 then”
在 XML 中,有 5 个预定义的实体引用:
< < 小于
> > 大于
& & 和号
' ' 单引号
" " 引号
4).XML 中的注释
<!-- This is a comment -->
5).避免 XML 属性?
因使用属性而引起的一些问题:
?属性无法包含多重的值(元素可以)
?属性无法描述树结构(元素可以)
?属性不易扩展(为未来的变化)
?属性难以阅读和维护
请尽量使用元素来描述数据。而仅仅使用属性来提供与数据无关的信息。
6). XML 文档类型定义文档结构
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE note SYSTEM "Note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
在上例中,DOCTYPE 声明是对外部 DTD 文件的引用。下面的段落展示了这个文件的内容。
DTD 的作用是定义 XML 文档的结构。它使用一系列合法的元素来定义文档结构:
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
7).xml语法在线验证器可以提供对xml语法的验证
本文出自 “tech记录” 博客,谢绝转载!