پس از انتشار مطلب «
Pro Agile .NET Development With Scrum - قسمت اول» شاید این سؤال در ابتدای کار برای خواننده پیش بیاید که ... چقدر باید برای خواندن آن وقت بگذارم؟ برای پاسخ به این سؤال باید درنظر داشت که یک انسان معمولی، میتواند بین 200 تا 250 کلمه را در دقیقه، مطالعه کند. بنابراین در ابتدا باید محاسبه کرد که یک متن، چه تعدادی کلمه دارد؟
شاید عنوان کنید که کافی است متن ورودی را بر اساس فاصلهی بین کلمات تقسیم بندی کرده و سپس تعداد کلمات بدست آمده را محاسبه کنیم:
var words = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
return words.Length;
این روش با آزمون زیر کار نکرده و با شکست مواجه میشود:
[TestMethod]
public void TestInvalidChars()
{
const string data = "To be . ! < > ( ) ! ! , ; : ' ? + -";
Assert.AreEqual(2, data.WordsCount());
}
در اینجا ! ، و امثال آن نیز یک کلمه درنظر گرفته میشوند. برای حل این مشکل کافی است آرایهی split را کمی تکمیلتر کنیم تا حروف غیرمجاز را درنظر نگیرد:
var words = text.Split(
new[] { ' ', ',', ';', '.', '!', '"', '(', ')', '?', ':', '\'', '«' , '»', '+', '-' },
StringSplitOptions.RemoveEmptyEntries);
return words.Length;
تا اینجا مشکل !، >< حل شد، اما در مورد متن ذیل چطور؟
[TestMethod]
public void TestSimpleHtmlSpacesWithNewLine()
{
const string data = "<b>this is a test.</b>\n\r<b>this is a test.</b>";
Assert.AreEqual(8, data.WordsCount());
}
مطالب ثبت شده، عموما توسط HTML Editorها ثبت میشوند. بنابراین دارای انواع و اقسام تگها بوده و همچنین ممکن است در این بین new line هم وجود داشته باشد که در این حالت، test\n\rtest باید دو کلمه محاسبه شود و نه یک کلمه.
اگر این موارد را در نظر بگیریم، به کلاس ذیل خواهیم رسید:
using System;
using System.Text.RegularExpressions;
namespace ReadingTime
{
public static class CalculateWordsCount
{
private static readonly Regex _matchAllTags =
new Regex(@"<(.|\n)*?>", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static int WordsCount(this string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return 0;
}
text = text.cleanTags().Trim();
text = text.Replace("\t", " ");
text = text.Replace("\n", " ");
text = text.Replace("\r", " ");
var words = text.Split(
new[] { ' ', ',', ';', '.', '!', '"', '(', ')', '?', ':', '\'', '«' , '»', '+', '-' },
StringSplitOptions.RemoveEmptyEntries);
return words.Length;
}
private static string cleanTags(this string data)
{
return data.Replace("\n", "\n ").removeHtmlTags();
}
private static string removeHtmlTags(this string text)
{
return string.IsNullOrEmpty(text) ?
string.Empty :
_matchAllTags.Replace(text, " ").Replace(" ", " ");
}
}
}
در اینجا حذف تگهای HTML و همچنین پردازش خطوط جدید و حروف غیرمجاز درنظر گرفته شدهاند.
پس از اینکه موفق به شمارش تعداد کلمات یک متن HTML ایی شدیم، اکنون میتوان این تعداد را تقسیم بر 180 (یک عدد معمول و متداول) کرد تا زمان خواندن کل متن بدست آید. سپس با استفاده از متد toReadableString میتوان آنرا به شکل قابل خواندنتری نمایش داد.
using System;
namespace ReadingTime
{
public static class CalculateReadingTime
{
public static string MinReadTime(this string text, int wordsPerMinute = 180)
{
var wordsCount = text.WordsCount();
var minutes = wordsCount / wordsPerMinute;
return minutes == 0 ? "کمتر از یک دقیقه" : TimeSpan.FromMinutes(minutes).toReadableString();
}
private static string toReadableString(this TimeSpan span)
{
var formatted = string.Format("{0}{1}{2}{3}",
span.Duration().Days > 0 ? string.Format("{0:0} روز و ", span.Days) : string.Empty,
span.Duration().Hours > 0 ? string.Format("{0:0} ساعت و ", span.Hours) : string.Empty,
span.Duration().Minutes > 0 ? string.Format("{0:0} دقیقه و ", span.Minutes) : string.Empty,
span.Duration().Seconds > 0 ? string.Format("{0:0} ثانیه", span.Seconds) : string.Empty);
if (formatted.EndsWith("و "))
{
formatted = formatted.Substring(0, formatted.Length - 2);
}
if (string.IsNullOrEmpty(formatted))
{
formatted = "0 ثانیه";
}
return formatted.Trim();
}
}
}
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید: ReadingTime.zip