Thứ Năm, 28 tháng 6, 2012

Loại bỏ HTML tags trong chuỗi bằng C#


Bài viết này nhằm giới thiệu với các bạn cách để loại bỏ HTML Tags trong chuỗi bằng C#. Đây là cách hay để hiển thị các chuỗi HTML dưới dạng chuỗi thuần và loại bỏ các định dạng như in đậm, in nghiêng…
Trong lúc hướng dẫn cho một lập trình viên về cách phân tích và trích lọc thông tin từ HTML DOM, tôi mới nghĩ đến việc tìm kiếm một phương thức có thể giúp loại bỏ các HTML tags trong chuỗi để có thể lưu vào CSDL một cách dễ dàng, ngoài ra, nhờ đó, chúng ta sẽ dễ dàng tìm kiếm ở CSDL hơn. Ví dụ tìm chuỗi “Ẩm thực ở Sài Gòn” sẽ dễ chịu hơn nếu không có các HTML Tags trong các chuỗi như “ Ẩm thực Sài Gòn ”. Thực may mắn cho tôi là tôi không cần phải tự mình làm quá nhiều, đây là một phương thức mà rất nhiều người quan tâm, và lẽ dĩ nhiên, rất nhiều người bạn đã chia sẻ cách làm đó. Việc của tôi chỉ là học tập lại một chút và chia sẻ cùng các bạn.
Chúng ta sẽ xây dựng một phương thức có thể loại bỏ các HTML Tags ra khỏi một chuỗi:
Nhập vào: “
Ẩm thực Sài Gòn

Kết quả: Ẩm thực ở Sài Gòn
Chúng ta có nhiều cách để thực hiện điều này, và tôi học được ba cách để chia sẻ cùng các bạn:

Cách 1 – Sử dụng Regex

Chúng ta cài đặt phương thức sử dụng Regex như sau:


public static string RemoveHtmlTagsUsingRegex(this string htmlString)
        {
            var result = Regex.Replace(htmlString, "<.*?>", string.Empty);
            return result;
        }
 
 Khi đó chúng ta sẽ có thể gọi một cách dễ dàng:


var result = testString.RemoveHtmlTagsUsingRegex(); 

Cách 2 – Sử dụng Compiled Regex ( câu lệnh Regex được biên dịch trước)

Khi các bạn đọc tới “biên dịch trước”, hẳn các bạn sẽ đoán ran ngay là phương thức mới này sẽ có tốc đột thực thi nhanh hơn so với phương thức trên. Tuy nhiên, để xác thực, chúng ta sẽ thử ở một unit test mà tôi sẽ xây dựng ở phía cuối bài viết.


static readonly Regex HtmlRegex = new Regex("<.*?>", RegexOptions.Compiled);

public static string RemoveHtmlTagsUsingCompiledRegex(this string htmlString)
        {
            var result = HtmlRegex.Replace(htmlString, string.Empty);
            return result;
        }
 

Cách 3 – Sử dụng mảng các ký tự (char array)

Cách này có vẻ thủ công hơn nhiều, và nó sẽ giúp gia tăng tốc độ thực thi, chúng ta cài đặt như sau:


public static string RemoveHtmlTagsUsingCharArray(this string htmlString)
        {
            var array = new char[htmlString.Length];
            var arrayIndex = 0;
            var inside = false;
 
            foreach (var @let in htmlString)
            {
                if (let == '<')
                {
                    inside = true;
                    continue;
                }
                if (let == '>')
                {
                    inside = false;
                    continue;
                }
                if (inside) continue;
                array[arrayIndex] = let;
                arrayIndex++;
            }
            return new string(array, 0, arrayIndex);
        }
 

Kiểm thử & so sánh tốc độ thực thi

 Tôi sẽ xây dựng một dự án MS Unit Tests có tên là StringUtitlies.Tests để kiểm thử cho các phương thức nói trên, lớp kiểm thử có tên là RemoveHtmlTagsTests.
Đầu tiên chúng ta sẽ xây dựng một phương thức để thực hiện việc loại bỏ HTML Tags và đo thời gian thực hiện theo các cách khác nhau. Có lẽ các bạn đã biết, tôi sẽ sử dụng đối tượng Stopwatch để đo lường thời gian. Và vì để bớt lặp lại mã lệnh, tôi định nghĩa một enum có tên là RemoveMethods và sau đó dùng nó để xây dựng phương thức RemoveHtmlTags như sau:


private TimeSpan RemoveHtmlTags(RemoveMethods method, out string result)
        {
            result = string.Empty;
            var stopWatch = new Stopwatch();
            stopWatch.Start();
            switch (method)
            {
                case RemoveMethods.Regex:
                    result = htmlString.RemoveHtmlTagsUsingRegex();
                    break;
                case RemoveMethods.CompiledRegex:
                    result = htmlString.RemoveHtmlTagsUsingCompiledRegex();
                    break;
                case RemoveMethods.CharArray:
                    result = htmlString.RemoveHtmlTagsUsingCharArray();
                    break;
            }
            stopWatch.Stop();
 
            return stopWatch.Elapsed;
        }
 
        private enum RemoveMethods
        {
            Regex,
            CompiledRegex,
            CharArray
        }
 
 Tiếp theo tôi sẽ xây dựng một phương thức kiểm thử để đảm bảo ba phương thức cho ra kết quả như tôi mong muốn (dĩ nhiên đây chỉ là một trường hợp đơn giản, nếu muốn kiểm thử kỹ càng hơn, bạn nên tự xây dựng thêm các test method của mình). 


private const string HtmlString = "  has a little lamb! 

";
        private const string ExpectResult = "Marry has a little lamb!";
 
        [TestMethod]
        public void Can_Remove_HtmlTags_By_Three_Methods()
        {
            string result;
 
            var timeSpan = RemoveHtmlTags(RemoveMethods.Regex, out result);
            Assert.AreEqual(result.Trim(), ExpectResult);
            Console.WriteLine("Remove HTML Tags using regex elasped time {0}", timeSpan);
 
            timeSpan = RemoveHtmlTags(RemoveMethods.CompiledRegex, out result);
            Assert.AreEqual(result.Trim(), ExpectResult);
            Console.WriteLine("Remove HTML Tags using compiled regex elasped time {0}", timeSpan);
 
            timeSpan = RemoveHtmlTags(RemoveMethods.CharArray, out result);
            Assert.AreEqual(result.Trim(), ExpectResult);
            Console.WriteLine("Remove HTML Tags using char array elasped time {0}", timeSpan);
        }
 
 Kết quả khi chạy kiểm thử:
Chúng ta có thể thấy rằng, thời gian để chạy  Compiled Regex sẽ nhanh hơn sử dụng Regex chưa biên dịch, và sử dụng char array cho tốc độ thực thi cao nhất, cao hơn nhiều so với hai cách còn lại.

Kết luận

Loại bỏ các HTML Tags là một nhu cầu khá lớn đối với tôi khi giúp đỡ các lập trình viên thực hiện một dự án về phân tích và khai thác dữ liệu web, và tôi đã học được vài cách hay để cùng chia sẽ với mọi người. Hy vọng các bạn sẽ thu lượm được thông tin bổ ích từ bài viết này!

Không có nhận xét nào: