2010-11-17 34 views
9

Tôi đang cố gắng tìm hiểu cách QXmlStreamReader hoạt động cho ứng dụng C++ mà tôi đang viết. Tệp XML mà tôi muốn phân tích là một từ điển lớn với cấu trúc phức tạp và nhiều ký tự Unicode nên tôi đã quyết định thử một trường hợp thử nghiệm nhỏ với một tài liệu đơn giản hơn. Thật không may, tôi đã đâm vào tường. Dưới đây là những ví dụ tập tin xml:Tại sao tôi không thể phân tích cú pháp tệp XML bằng QXmlStreamReader từ Qt?

<?xml version="1.0" encoding="UTF-8" ?> 
<persons> 
    <person> 
     <firstname>John</firstname> 
     <surname>Doe</surname> 
     <email>[email protected]</email> 
     <website>http://en.wikipedia.org/wiki/John_Doe</website> 
    </person> 
    <person> 
     <firstname>Jane</firstname> 
     <surname>Doe</surname> 
     <email>[email protected]</email> 
     <website>http://en.wikipedia.org/wiki/John_Doe</website> 
    </person> 
    <person> 
     <firstname>Matti</firstname> 
     <surname>Meikäläinen</surname> 
     <email>[email protected]</email> 
     <website>http://fi.wikipedia.org/wiki/Matti_Meikäläinen</website> 
    </person> 
</persons> 

... và tôi đang cố gắng để phân tích nó sử dụng mã này:

int main(int argc, char *argv[]) 
{ 
    if (argc != 2) return 1; 

    QString filename(argv[1]); 
    QTextStream cout(stdout); 
    cout << "Starting... filename: " << filename << endl; 

    QFile file(filename); 
    bool open = file.open(QIODevice::ReadOnly | QIODevice::Text); 
    if (!open) 
    { 
     cout << "Couldn't open file" << endl; 
     return 1; 
    } 
    else 
    { 
     cout << "File opened OK" << endl; 
    } 

    QXmlStreamReader xml(&file); 
    cout << "Encoding: " << xml.documentEncoding().toString() << endl; 

    while (!xml.atEnd() && !xml.hasError()) 
    { 
     xml.readNext(); 
     if (xml.isStartElement()) 
     { 
      cout << "element name: '" << xml.name().toString() << "'" 
       << ", text: '" << xml.text().toString() << "'" << endl; 
     } 
     else if (xml.hasError()) 
     { 
      cout << "XML error: " << xml.errorString() << endl; 
     } 
     else if (xml.atEnd()) 
     { 
      cout << "Reached end, done" << endl; 
     } 
    } 

    return 0; 
} 

... sau đó tôi nhận được kết quả này:

C:\xmltest\Debug>xmltest.exe example.xml
Starting... filename: example.xml
File opened OK
Encoding:
XML error: Encountered incorrectly encoded content.

Điều gì đã xảy ra? Tệp này không thể đơn giản hơn và có vẻ phù hợp với tôi. Với tệp gốc của tôi, tôi cũng nhận được một mục nhập trống cho mã hóa, tên của các mục() được hiển thị, nhưng than ôi, văn bản() cũng trống. Bất kỳ đề xuất đánh giá rất nhiều, cá nhân tôi là Thorougly mystified.

Trả lời

11

Tôi tự trả lời vấn đề này vì vấn đề này liên quan đến ba vấn đề, hai trong số đó được đưa ra bởi các câu trả lời.

  1. Tệp thực sự không được mã hóa UTF-8. Tôi đã thay đổi mã hóa thành iso-8859-1 và cảnh báo mã hóa biến mất.
  2. Hàm text() không hoạt động như tôi mong đợi. Tôi phải sử dụng readElementText() để đọc nội dung của các mục.
  3. Khi tôi cố gắng readElementText() trên một yếu tố không chứa văn bản, như cấp cao nhất < người > trong trường hợp của tôi, phân tích cú pháp trả về một "dữ liệu nhân vật mong đợi" lỗi và phân tích cú pháp là bị gián đoạn. Tôi thấy hành vi này lạ (theo ý kiến ​​của tôi trả về một chuỗi rỗng và tiếp tục sẽ tốt hơn) nhưng tôi đoán miễn là đặc tả được biết, tôi có thể làm việc xung quanh nó và tránh gọi hàm này trên mọi mục nhập.

Phần mã có liên quan mà làm việc như mong đợi bây giờ trông như thế này:

while (!xml.atEnd() && !xml.hasError()) 
{ 
    xml.readNext(); 
    if (xml.isStartElement()) 
    { 
     QString name = xml.name().toString(); 
     if (name == "firstname" || name == "surname" || 
      name == "email" || name == "website") 
     { 
      cout << "element name: '" << name << "'" 
         << ", text: '" << xml.readElementText() 
         << "'" << endl; 
     } 
    } 
} 
if (xml.hasError()) 
{ 
    cout << "XML error: " << xml.errorString() << endl; 
} 
else if (xml.atEnd()) 
{ 
    cout << "Reached end, done" << endl; 
} 
+0

Thú vị Có, readElementText() có lỗi một chút nói chung cũng không hoạt động đúng khi đọc dữ liệu từng bước từ luồng nơi dữ liệu có thể không đầy đủ (ví dụ: ổ cắm), hãy xem http://bugreports.qt.nokia.com/browse/QTBUG-14661 –

+0

Tôi có nên báo cáo lỗi này là lỗi không? đó là, hoặc nếu nó phải làm việc theo cách đó. – neuviemeporte

+0

@ FrankOsterfeld có gì mới trên 'readElementText()' không? Có chức năng nào để kiểm tra xem nó có hoạt động hay không? – Niklas

2

Bạn có chắc chắn tài liệu của mình được mã hóa UTF-8 không? Bạn đã sử dụng trình soạn thảo nào? Kiểm tra xem các ký tự ä trông như thế nào nếu bạn xem tệp mà không cần giải mã.

4

Tệp không được mã hóa UTF-8. Thay đổi mã hóa thành iso-8859-1 và nó sẽ phân tích cú pháp mà không có lỗi.

<?xml version="1.0" encoding="iso-8859-1" ?> 
+0

Bật. Tôi xấu hổ vì tôi không nghĩ về điều đó.:( – neuviemeporte

+0

Nhưng điều gì sẽ xảy ra nếu mã hóa XML không được chỉ định? QXmlStreamReader có giả định là UTF-8 không? Tức là có: '' ở đầu tệp XML cho ** "Đã gặp phải nội dung được mã hóa không đúng." ** – bitek

2

Giới thiệu về mã hóa: Như baysmith và và hmuelner đã nói, tệp của bạn có thể bị mã hóa sai (trừ khi mã hóa bị mất khi dán ở đây). Hãy thử khắc phục điều đó với một số trình soạn thảo văn bản nâng cao.

Vấn đề với việc bạn sử dụng văn bản() là nó không hoạt động như bạn mong đợi. text() trả về nội dung của mã thông báo hiện tại nếu nó thuộc loại ký tự, chú thích, DTD hoặc EntityReference. Mã thông báo hiện tại của bạn là StartElement, vì vậy nó trống. Nếu bạn muốn tiêu thụ/đọc văn bản của startElement hiện tại, sử dụng readElementText() để thay thế.

+0

Tôi đã nghiên cứu thêm và mã hóa không có sản phẩm nào, xuất hiện dưới dạng "UTF-8" nếu tôi đọc readNext() trước khi truy cập mã hóa() – neuviemeporte

1

Hãy thử ví dụ này tôi chỉ cần sao chép nó từ dự án của tôi nó làm việc cho tôi.

void MainWindow::readXML(const QString &fileName) 
{ 


fileName = "D:/read.xml"; 

QFile* file = new QFile(fileName); 
if (!file->open(QIODevice::ReadOnly | QIODevice::Text)) 
{ 
    QMessageBox::critical(this, "QXSRExample::ReadXMLFile", "Couldn't open xml file", QMessageBox::Ok); 
    return; 
} 

/* QXmlStreamReader takes any QIODevice. */ 
QXmlStreamReader xml(file); 
/* We'll parse the XML until we reach end of it.*/ 
while(!xml.atEnd() && !xml.hasError()) 
{ 
    /* Read next element.*/ 
    QXmlStreamReader::TokenType token = xml.readNext(); 
    /* If token is just StartDocument, we'll go to next.*/ 
    if(token == QXmlStreamReader::StartDocument) 
     continue; 

    /* If token is StartElement, we'll see if we can read it.*/ 
    if(token == QXmlStreamReader::StartElement) { 
     if(xml.name() == "email") { 
      ui->listWidget->addItem("Element: "+xml.name().toString()); 
      continue; 
     } 
    } 
} 
/* Error handling. */ 
if(xml.hasError()) 
    QMessageBox::critical(this, "QXSRExample::parseXML", xml.errorString(), QMessageBox::Ok); 

//resets its internal state to the initial state. 
xml.clear(); 
} 

void MainWindow::writeXML(const QString &fileName) 
{ 
fileName = "D:/write.xml"; 
QFile file(fileName); 
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) 
{ 
    QMessageBox::critical(this, "QXSRExample::WriteXMLFile", "Couldn't open anna.xml", QMessageBox::Ok); 
    return; 
} 
QXmlStreamWriter xmlWriter(&file); 
xmlWriter.setAutoFormatting(true); 
xmlWriter.writeStartDocument(); 
//add Elements 
xmlWriter.writeStartElement("bookindex"); 
ui->listWidget->addItem("bookindex"); 
xmlWriter.writeStartElement("Suleman"); 
ui->listWidget->addItem("Suleman"); 

//write all elements in xml filexl 
xmlWriter.writeEndDocument(); 
file.close(); 
if (file.error()) 
    QMessageBox::critical(this, "QXSRExample::parseXML", file.errorString(), QMessageBox::Ok); 


} 
Các vấn đề liên quan