2009-05-11 40 views
7

Tôi có một tập tin xml lớn (xấp xỉ 10 MB.) Trong cấu trúc đơn giản sau đây:Cách nhanh nhất để thêm nút mới vào cuối xml?

<Errors> 
    <Error>.......</Error> 
    <Error>.......</Error> 
    <Error>.......</Error> 
    <Error>.......</Error> 
    <Error>.......</Error> 
</Errors> 

nhu cầu của tôi là để viết thêm một mới nút < Lỗi > ở cuối trước khi </lỗi > thẻ. Whats là cách nhanh nhất để đạt được điều này trong .net?

+0

Bạn đang viết thế nào? Sử dụng DOM? SAX? Viết trực tiếp? :-P Cá nhân tôi thích cách tiếp cận DOM hơn; bạn chỉ cần truy cập nút Errors và gọi appendChild(). –

+2

Vui lòng xác định "nhanh nhất" cho tình huống này; bạn có nghĩa là "nhanh nhất để thực hiện" hoặc "nhanh nhất để phát triển"? –

+0

"nhanh nhất để thực hiện" –

Trả lời

10

Bạn cần sử dụng kỹ thuật bao gồm XML.

error.xml của bạn (không thay đổi, chỉ cần một sơ khai sử dụng bởi parsers XML để đọc.):

<?xml version="1.0"?> 
<!DOCTYPE logfile [ 
<!ENTITY logrows  
SYSTEM "errorrows.txt"> 
]> 
<Errors> 
&logrows; 
</Errors> 

tập tin errorrows.txt của bạn (thay đổi, phân tích cú pháp xml không hiểu nó) :

<Error>....</Error> 
<Error>....</Error> 
<Error>....</Error> 

Sau đó, để thêm một mục vào errorrows.txt:

using (StreamWriter sw = File.AppendText("logerrors.txt")) 
{ 
    XmlTextWriter xtw = new XmlTextWriter(sw); 

    xtw.WriteStartElement("Error"); 
    // ... write error messge here 
    xtw.Close(); 
} 

Hoặc thậm chí bạn có thể sử dụng .NET 3.5 XElement, và thêm văn bản vào StreamWriter:

using (StreamWriter sw = File.AppendText("logerrors.txt")) 
{ 
    XElement element = new XElement("Error"); 
    // ... write error messge here 
    sw.WriteLine(element.ToString()); 
} 

Xem thêm Microsoft's article Efficient Techniques for Modifying Large XML Files

0

Phương pháp nhanh nhất có thể đọc trong tệp bằng cách sử dụng XmlReader và chỉ sao chép từng nút đã đọc vào luồng mới bằng cách sử dụng XmlWriter Khi bạn đến điểm mà bạn gặp phải thẻ đóng </Errors> thì bạn chỉ cần để xuất phần tử <Error> bổ sung của bạn trước khi kết hợp chu trình 'đọc và sao chép'. Bằng cách này chắc chắn sẽ khó hơn là đọc toàn bộ tài liệu vào lớp DOM (XmlDocument), nhưng đối với các tệp XML lớn, nhiều hơn nhanh hơn. Phải thừa nhận rằng, sử dụng StreamReader/StreamWriter sẽ vẫn nhanh hơn một chút, nhưng khá khủng khiếp khi làm việc với mã.

0

XML-File của bạn được thể hiện bằng mã như thế nào? Bạn có sử dụng lớp System.XML không? Trong trường hợp này, bạn có thể sử dụng XMLDocument.AppendChild.

7

Đầu tiên, tôi sẽ loại bỏ System.Xml.XmlDocument vì it is a DOM yêu cầu phân tích cú pháp và xây dựng toàn bộ cây trong bộ nhớ trước khi có thể nối thêm. Điều này có nghĩa là 10 MB văn bản của bạn sẽ có dung lượng hơn 10 MB. Điều này có nghĩa nó là "bộ nhớ chuyên sâu" và "tốn thời gian".

Thứ hai, tôi sẽ loại System.Xml.XmlReader vì nó requires parsing the entire file trước khi bạn có thể đến điểm khi bạn có thể nối thêm vào. Bạn sẽ phải sao chép XmlReader vào XmlWriter vì bạn không thể sửa đổi nó. Điều này đòi hỏi phải sao chép XML của bạn trong bộ nhớ đầu tiên trước khi bạn có thể thêm vào nó.

Giải pháp nhanh hơn để XmlDocument và XmlReader sẽ là chuỗi thao tác (trong đó có các vấn đề bộ nhớ riêng của mình):

string xml = @"<Errors><error />...<error /></Errors>"; 
int idx = xml.LastIndexOf("</Errors>"); 

xml = xml.Substring(0, idx) + "<error>new error</error></Errors>"; 

Chop tắt thẻ kết thúc, thêm vào các lỗi mới, và thêm thẻ back-end.

Tôi cho rằng bạn có thể phát điên với điều này và cắt bớt tệp của bạn theo 9 ký tự và thêm vào đó. Sẽ không phải đọc trong các tập tin và sẽ cho phép hệ điều hành tối ưu hóa tải trang (chỉ sẽ phải tải trong khối cuối cùng hoặc một cái gì đó).

System.IO.FileStream fs = System.IO.File.Open("log.xml", System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite); 
fs.Seek(-("</Errors>".Length), System.IO.SeekOrigin.End); 
fs.Write("<error>new error</error></Errors>"); 
fs.Close(); 

Điều đó sẽ trúng một vấn đề nếu tập tin của bạn là trống hoặc chỉ chứa "< lỗi > </lỗi >", cả hai đều có thể dễ dàng được xử lý bằng cách kiểm tra độ dài.

+0

OpenText() sẽ mở một tệp để đọc và trả về Trình tạo luồng. –

+0

Thật vậy, cảm ơn. Đã sửa? –

+0

greate! u giải quyết một vấn đề rất lớn, tôi không có lý do tại sao câu trả lời này không được bình chọn trên 1k –

3

Cách nhanh nhất có thể là truy cập tệp trực tiếp.

using (StreamWriter file = File.AppendText("my.log")) 
{ 
    file.BaseStream.Seek(-"</Errors>".Length, SeekOrigin.End); 
    file.Write(" <Error>New error message.</Error></Errors>"); 
} 

Nhưng bạn mất tất cả tính năng XML đẹp và có thể dễ dàng làm hỏng tệp.

+1

Đó cũng là những gì tôi đã đề xuất. –

+0

Tôi đang cố gắng này nhưng nhận được một 'Không thể tìm kiếm lạc hậu để ghi đè lên dữ liệu mà trước đây đã tồn tại trong một tệp được mở trong chế độ chắp thêm.' lỗi trên dòng .Seek. Ví dụ có đúng không? – Simon

+0

Không, bài kiểm tra không chính xác, nhưng tất cả những gì bạn cần làm để làm cho nó hoạt động được thay thế 'File.AppendText (...)' bằng 'StreamWriter mới (Tệp.Mở (filePath, FileMode.Open, FileAccess.Write) ' –

1

Tôi sẽ sử dụng XmlDocument hoặc XDocument để tải tệp của bạn và sau đó thao tác theo đó.

Sau đó, tôi sẽ xem xét khả năng lưu bộ nhớ cache XmlDocument này trong bộ nhớ để bạn có thể truy cập tệp nhanh chóng.

Bạn cần tốc độ cho điều gì? Bạn có một nút cổ chai hiệu suất đã hoặc bạn đang mong đợi một?

+0

XmlDocument là một mô hình DOM chậm hơn SAX như trong XmlReader. XmlDocument sẽ yêu cầu đại diện cho toàn bộ 10 MB trong bộ nhớ dưới dạng đối tượng (tổng cộng hơn 10 MB). XmlReader sẽ nhanh hơn (Tôi khá chắc chắn XmlDocument được xây dựng trên XmlReader) nhưng bạn vẫn phải phân tích cú pháp toàn bộ tài liệu. Không, với tôi, đủ điều kiện là "nhanh" nếu tất cả Ramesh đang làm là phụ thêm vào một tệp nhật ký (có vẻ là trường hợp). –

+0

Tôi hoàn toàn đồng ý, nhưng tôi sẽ luôn tránh viết XML với phần nối văn bản. Câu trả lời của tôi là tìm hiểu xem anh ta có thể nạp tài liệu vào bộ nhớ và sau đó viết vào đó không. Điều đó sẽ rất nhanh. Sau đó, một quá trình khác thường ghi XmlDocument ra tệp. Tất cả phụ thuộc vào kịch bản. –

1

Hãy thử điều này:

 var doc = new XmlDocument(); 
     doc.LoadXml("<Errors><error>This is my first error</error></Errors>"); 

     XmlNode root = doc.DocumentElement; 

     //Create a new node. 
     XmlElement elem = doc.CreateElement("error"); 
     elem.InnerText = "This is my error"; 

     //Add the node to the document. 
     if (root != null) root.AppendChild(elem); 

     doc.Save(Console.Out); 
     Console.ReadLine(); 
+1

Đây chắc chắn không phải là cách nhanh nhất. –

0

Dưới đây là làm thế nào để làm điều đó trong C, .NET nên tương tự.

Trò chơi là để nhảy đơn giản đến cuối tệp, bỏ qua lại thẻ, nối thêm dòng lỗi mới và viết một thẻ mới.

#include <stdio.h> 
#include <string.h> 
#include <errno.h> 

int main(int argc, char** argv) { 
     FILE *f; 

     // Open the file 
     f = fopen("log.xml", "r+"); 

     // Small buffer to determine length of \n (1 on Unix, 2 on PC) 
     // You could always simply hard code this if you don't plan on 
     // porting to Unix. 
     char nlbuf[10]; 
     sprintf(nlbuf, "\n"); 

     // How long is our end tag? 
     long offset = strlen("</Errors>"); 

     // Add in an \n char. 
     offset += strlen(nlbuf); 

     // Seek to the END OF FILE, and then GO BACK the end tag and newline 
     // so we use a NEGATIVE offset. 
     fseek(f, offset * -1, SEEK_END); 

     // Print out your new error line 
     fprintf(f, "<Error>New error line</Error>\n"); 

     // Print out new ending tag. 
     fprintf(f, "</Errors>\n"); 

     // Close and you're done 
     fclose(f); 
} 
0

Sử dụng kỹ thuật chuỗi dựa trên (như tìm cách kết thúc của tập tin và sau đó di chuyển ngược chiều dài của thẻ đóng) là dễ bị tổn thương đến bất ngờ nhưng các biến thể hoàn toàn hợp pháp trong cấu trúc tài liệu.

Tài liệu có thể kết thúc với bất kỳ khoảng trắng nào, để chọn vấn đề phù hợp nhất mà bạn gặp phải. Nó cũng có thể kết thúc với bất kỳ số lượng ý kiến ​​hoặc hướng dẫn xử lý. Và điều gì sẽ xảy ra nếu phần tử cấp cao nhất không có tên là Error?

Và đây là một tình huống mà sử dụng chuỗi thao tác thất bại hoàn toàn để phát hiện:

<Error xmlns="not_your_namespace"> 
    ... 
</Error> 

Nếu bạn sử dụng một XmlReader để xử lý XML, trong khi nó có thể không nhanh như tìm cách EOF, nó cũng sẽ cho phép bạn xử lý tất cả các điều kiện ngoại lệ có thể có này.

+0

Tệp mà anh ấy trình bày trông giống như tệp nhật ký và tôi cho rằng anh ấy đã đạt đến điểm mà nó ngày càng chậm hơn để nối thêm vào nó, do đó câu hỏi của anh ấy. Đủ để nói rằng tôi nghĩ rằng các định dạng đăng nhập là hoàn toàn dưới sự kiểm soát của mình. –

+0

Nó thường có thể được hoàn toàn tốt đẹp để làm cho những giả định. Tôi đã phải sửa chữa rất nhiều mã, nơi các nhà phát triển đoán sai, mặc dù. Trong hầu hết các trường hợp đó, nhà phát triển thậm chí không biết anh ta đang đoán. –

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