一、背景
不得不說Qt是一個很強大的類庫,不管是做項目還是做產品,Qt自身封裝的東西就已經非常全面了,我們今天的這篇文章就是模擬了Qt讀寫ini文件的一個操作,當然是由於一些外力原因,我們決定自己來完善下這個功能。好的,那么現在就讓我們隆重的請出今天的主角--QSettings。這個類能干嘛呢? 答案就是:讀寫注冊表或者讀寫ini文件,這對於我們做應用程序時記錄一些可持久化數據非常有用。
二、QSettings訪問ini文件
QSettings訪問ini文件相對來說比較簡單,我們只需要構造一個QSettings對象,傳入文件名稱和文件存儲格式即可,如圖1所示。但同時QSettings也有一些局限,如下:
1、QSettings的編碼問題(QTBUG15543、QTBUG19552)
2、QSettings的key不能為中文
3、當在一個嵌套作用域多次構造QSettings時並設置了編碼,此時訪問文件設置的編碼會失效
由於QSettings有一些限制,也就引出了我們這篇文章的內容,使用xml模擬ini文件,下面我們主要分析下怎么使用xml文件模擬ini文件,需要的接口並不多,讀、寫、新增和刪除。
圖1 QSettings讀寫ini
三、xml文件讀寫
讀寫xml文件的方式有很多,Qt提供了2種比較常用的方式:DOM和SAX,詳情可以參看:Qt學習之XML讀寫操作小結。初次之外C++還提供了幾種庫用於操作xml文件,比如:libxml2、tinyXml等。
此處我們模擬的是QSettings讀寫ini文件,因此使用Qt自帶的DOM方式讀寫xml,操作起來相對容易。
如圖2所示,是我們xml文件操作類的幾個重要接口和成員,前邊4個公有接口分別對應。讀、寫、新增和刪除;成員變量m_filePath表示加載的xml文件路徑,m_mItemMap表示2級的ini文件結構
圖2 xml文件操作頭文件
1、讀xml文件

1 void xmlOperate::readXml( const QString & filePath ) 2 { 3 if(filePath.isEmpty()) 4 return; 5 6 QFile file(filePath); 7 if(file.open(QFile::ReadOnly | QFile::Text) == false) 8 return; 9 10 QDomDocument domDocument; 11 QString error; 12 int row = 0, column = 0; 13 if(domDocument.setContent(&file, false, &error, &row, &column) == false) 14 return file.close(); 15 16 if(domDocument.isNull()) 17 return file.close(); 18 19 m_mItemMap.clear(); 20 21 QDomElement rootElement = domDocument.documentElement(); 22 QDomNodeList groupList = rootElement.childNodes(); 23 for(int i = 0; i < groupList.count(); ++i) 24 { 25 QDomNode groupNode = groupList.item(i); 26 QDomElement groupElement = groupNode.toElement(); 27 QString groupName = groupElement.attribute("name"); 28 29 QMap<QString, QString> items; 30 QDomNodeList itemList = groupElement.childNodes(); 31 for (int j = 0; j < itemList.count(); ++j) 32 { 33 QDomNode itemNode = itemList.item(j); 34 QDomElement itemElement = itemNode.toElement(); 35 QString itemName = itemElement.attribute("name"); 36 QString itemText = itemElement.text(); 37 38 if (itemName.isEmpty()) 39 { 40 items[QString::number(j)] = itemText; 41 } 42 else 43 { 44 items[itemName] = itemText; 45 } 46 } 47 m_mItemMap[groupName] = items; 48 } 49 50 file.close(); 51 52 qDebug() << m_mItemMap; 53 }
2、保存xml文件

1 void xmlOperate::writeXml(const QString & filePath) 2 { 3 QDomDocument domDocument; 4 QString strHeader( "version=\"1.0\" encoding=\"UTF-8\"" ); 5 domDocument.appendChild( domDocument.createProcessingInstruction("xml", strHeader) ); 6 7 QDomElement root = domDocument.createElement("groups"); 8 domDocument.appendChild(root); 9 10 for(auto iter = m_mItemMap.begin(); iter != m_mItemMap.end(); ++iter) 11 { 12 QDomElement groupNode = domDocument.createElement("group"); 13 groupNode.setAttribute("name", iter.key()); 14 15 QMap<QString, QString> items = iter.value(); 16 for (auto iter2 = items.begin(); iter2 != items.end(); ++iter2) 17 { 18 QString key = iter2.key(); 19 QString value = iter2.value(); 20 21 QDomElement itemNode = domDocument.createElement("item"); 22 itemNode.setAttribute("name", key); 23 24 QDomText textNode = domDocument.createTextNode(value); 25 itemNode.appendChild(textNode); 26 27 groupNode.appendChild(itemNode); 28 } 29 root.appendChild(groupNode); 30 } 31 32 QFile file(filePath); 33 if(file.open(QFile::WriteOnly | QFile::Text)) 34 { 35 QTextStream out(&file); 36 domDocument.save(out, 4); 37 file.close(); 38 } 39 qDebug() << m_mItemMap; 40 }
3、插入項
1 bool xmlOperate::addItem( const QString & value, const QString & group, const QString & key ) 2 { 3 if (value.isEmpty() || group.isEmpty()) 4 { 5 return false; 6 } 7 8 if (key.isEmpty()) 9 { 10 int count = m_mItemMap[group].size(); 11 m_mItemMap[group][QString::number(count)] = value; 12 } 13 else 14 { 15 m_mItemMap[group][key] = value; 16 } 17 18 writeXml(m_filePath); 19 20 return true; 21 }
4、刪除項
1 bool xmlOperate::removeItem( const QString & value, const QString & group, const QString & key /*= ""*/ ) 2 { 3 if (value.isEmpty() || group.isEmpty()) 4 { 5 return false; 6 } 7 8 if (key.isEmpty()) 9 { 10 int count = m_mItemMap[group].size(); 11 if (count == 0) 12 { 13 return false; 14 } 15 16 auto iter = m_mItemMap[group].begin(); 17 while (iter != m_mItemMap[group].end()) 18 { 19 if (iter.value() == value) 20 { 21 iter = m_mItemMap[group].erase(iter); 22 } 23 else 24 { 25 ++iter; 26 } 27 } 28 } 29 else 30 { 31 m_mItemMap[group].remove(key); 32 } 33 34 writeXml(m_filePath); 35 36 return true; 37 }
四、示例程序下載
如圖3是測試代碼生成的測試結果,group相當於ini文件中的一個分組,item表示分組中的一項
圖3 測試程序結果