نظرات مطالب
MVC Scaffolding #2
- سوئیچ ModelType رو ذکر نکردید. مثالش هست در متن (... ModelType Task - ...) 
- خطاهایی رو هم که دریافت می‌کنید، اینجا به نویسنده اصلی گزارش بدید (به صورت کامل البته؛ نه اینکه صرفا عنوان کنید کار نمی‌کند).
مطالب
تعیین تعداد ردیف در صفحه جداول خودکار iTextSharp

پیشنیاز : «تکرار خودکار سرستون‌های یک جدول در صفحات مختلف، توسط iTextSharp»
همانطور که در مطلب پیشنیاز عنوان شده ذکر گردید، iTextSharp امکان درج خودکار header و footer به علاوه محاسبه خودکار تعداد ردیف‌های یک جدول در یک صفحه را بر اساس طول و اندازه محتوای هر ردیف، دارد. برای مثال یک صفحه ممکن است 2 ردیف شود و یک صفحه 20 ردیف. تمام این‌ها را به صورت خودکار محاسبه می‌کند و بسیار عالی است. (این امکان مهمی است که خیلی از ابزارهای گزارشگیری موجود هنوز با آن مشکل دارند)
اما اگر فرض را بر این بگذاریم که اندازه سلول‌ها و در نتیجه طول هر ردیف ثابت است و مثلا تمام صفحات نهایتا از یک تعداد ردیف مشخص تشکیل خواهند شد، خاصیتی را به نام number of rows یا rows count و امثال آن‌را ندارد که مثلا به آن گفت، من در هر صفحه فقط 5 ردیف را می‌خواهم نمایش دهم و نه 20 ردیف را.
روش حل این مساله را در ادامه ملاحظه خواهید کرد و یک نکته‌ی خیلی ساده و مستند نشده دارد!

using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace RowsCountSample
{
class Program
{
static void Main(string[] args)
{
using (var pdfDoc = new Document(PageSize.A4))
{
var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
pdfDoc.Open();

var table1 = new PdfPTable(3);
table1.HeaderRows = 2;
table1.FooterRows = 1;

//header row
var headerCell = new PdfPCell(new Phrase("header"));
headerCell.Colspan = 3;
headerCell.HorizontalAlignment = Element.ALIGN_CENTER;
table1.AddCell(headerCell);

//footer row
var footerCell = new PdfPCell(new Phrase("footer"));
footerCell.Colspan = 3;
footerCell.HorizontalAlignment = Element.ALIGN_CENTER;
table1.AddCell(footerCell);

//adding some rows
for (int i = 0; i < 70; i++)
{
//adds a new row
table1.AddCell(new Phrase("Cell[0], Row[" + i + "]"));
table1.AddCell(new Phrase("Cell[1], Row[" + i + "]"));
table1.AddCell(new Phrase("Cell[2], Row[" + i + "]"));

//sets the number of rows per page
if (i > 0 && table1.Rows.Count % 7 == 0)
{
pdfDoc.Add(table1);
table1.DeleteBodyRows();
pdfDoc.NewPage();
}
}

pdfDoc.Add(table1);
}

//open the final file with adobe reader for instance.
Process.Start("Test.pdf");
}
}
}

نکته جدید این مثال، قسمت زیر است:


if (i > 0 && table1.Rows.Count % 7 == 0)
{
pdfDoc.Add(table1);
table1.DeleteBodyRows();
pdfDoc.NewPage();
}

هر زمان که table1 به صفحه اضافه شود، header و footer هم اضافه خواهند شد، اما اگر BodyRows آن حذف نشود،‌ دفعه‌ی دومی که این table به صفحه اضافه می‌شود، شامل ردیف‌های مثلا یک تا 10 خواهد بود بجای 6 تا 10 .

مطالب
بومی سازی تاریخ و اعداد در جاوا اسکریپت در سال 2020
احتمالا تا به امروز در برنامه‌های جاوا اسکریپتی خود از کتابخانه‌های ثالث و یا توابع ویژه‌ای برای نمایش شمسی تاریخ، نمایش فارسی اعداد، افزودن جدا کننده‌ی سه رقمی اعداد (جداکننده‌ی هزارگان)، نمایش تاریخ نسبی مانند 1 روز قبل و ... استفاده کرده‌اید. خبر خوب اینکه موتور جاوا اسکریپتی تمام مرورگرهای جدید (از فایرفاکس 29 و کروم 24 به بعد) به صورت توکار یک چنین تبدیل‌هایی را پشتیبانی می‌کنند و برای مثال برای تبدیل تاریخ میلادی به شمسی و نمایش آن، در بسیاری از موارد نیازی به کتابخانه‌ی حجیم moment.js (و یا سایر روش‌های مرسوم خانگی) نیست.


معرفی API استاندارد بومی سازی JavaScript

Internationalization یا به صورت خلاصه i18n (یعنی یک i به همراه 18 حرف و یک n)، پروسه‌ای که در آن برنامه به نحوی طراحی می‌شود تا خروجی آن قابلیت استفاده‌ی برای انواع و اقسام فرهنگ‌ها را داشته باشد. برای مثال دو متد زیر را در نظر بگیرید:
function formatDate(d)
{
  var month = d.getMonth() + 1;
  var date = d.getDate();
  var year = d.getFullYear();
  return month + "/" + date + "/" + year;
}

function formatMoney(amount)
{
  return "$" + amount.toFixed(2);
}
آیا در همه جای دنیا، تاریخ به صورت ماه، روز و سال نمایش داده می‌شود؛ آن هم به صورت میلادی؟ و یا آیا خروجی فرمت شده‌ی یک مقدار پولی، همیشه با دلار شروع می‌شود و نمایش آن نیز با اعداد انگلیسی است؟
پیشتر جاوا اسکریپت برای مدیریت یک چنین مواردی (i18n-aware formatting) از متد toLocaleString استفاده می‌کرد (و هنوز هم برای پشتیبانی از برنامه‌های قدیمی، از API عمومی آن حذف نشده‌است) و خروجی آن از هر مرورگر و پیاده سازی خاصی، به مرورگر دیگری می‌تواند متفاوت باشد؛ حتی اگر جزئیات دقیقی هم درخواست شود. برای رفع این مشکل، استاندارد ECMAScript Internationalization API ارائه شد تا قابلیت‌های توکار i18n جاوا اسکریپت را بهبود بخشیده و همچنین یک‌دست کند. توسط آن امکان انتخاب یک یا چند منطقه‌ی خاص و سپس فرمت کردن تاریخ، اعداد و یا حتی مرتب سازی واژه‌ها و عبارات با معرفی collations، میسر می‌باشد. در اینجا حتی امکان سفارشی سازی این فرمت‌ها نیز پیش‌بینی شده‌است.


معرفی اینترفیس Intl

i18n API در یک شیء سراسری به نام Intl قابل دسترسی است و تعدادی از سازنده‌های آن Intl.Collator ،Intl.DateTimeFormat و Intl.NumberFormat نام دارند؛ مانند:
const result =  new Intl.NumberFormat("fa").format(123456)
برای کار با این شیء، نیازی به import هیچ ماژول و یا کتابخانه‌ای نیست و جزئی از جاوا اسکریپت استاندارد می‌باشد. به همین جهت کار با آن حجمی را به برنامه‌ی شما اضافه نخواهد کرد.

تمام این سازنده‌ها می‌توانند با یک فرهنگ و یا آرایه‌ا‌‌ی از فرهنگ‌های مدنظر شروع شوند:
const portugueseTime =  new Intl.DateTimeFormat(["pt-BR", "pt-PT"], options);
در مثال اول فرهنگ فارسی و در مثال دوم فرهنگ پرتغالی که در برزیل و پرتغال مورد استفاده‌است، ذکر شده‌اند.
پارامتر اختیاری دوم آن‌ها نیز تنظیماتی است که جهت سفارشی سازی این بومی سازی می‌توان تعریف کرد.


نمایش شمسی تاریخ میلادی توسط i18n API

پس از معرفی i18n API، اکنون می‌خواهیم در طی مثال‌هایی، تمام کتابخانه‌های ثالث تبدیل تاریخ میلادی به شمسی را کنار گذاشته و با استفاده از جاوا اسکریپت استاندارد، این تبدیل را انجام دهیم. پارامتر دوم سازنده‌ی new Intl.DateTimeFormat که تنظیمات آن‌را مشخص می‌کند، می‌تواند به همراه ترکیبی از موارد زیر باشد که مقادیر مجاز برای آن‌ها را نیز مشاهده می‌کنید:
{
  weekday: 'narrow' | 'short' | 'long',
  era: 'narrow' | 'short' | 'long',
  year: 'numeric' | '2-digit',
  month: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long',
  day: 'numeric' | '2-digit',
  hour: 'numeric' | '2-digit',
  minute: 'numeric' | '2-digit',
  second: 'numeric' | '2-digit',
  timeZoneName: 'short' | 'long',
// Time zone to express it in
  timeZone: 'Asia/Shanghai',
  // Force 12-hour or 24-hour
  hour12: true | false,
// Rarely-used options
  hourCycle: 'h11' | 'h12' | 'h23' | 'h24',
  formatMatcher: 'basic' | 'best fit'
}
برای نمونه، ذکر Intl.DateTimeFormat بدون هیچ تنظیمی و فقط با تعیین فرهنگ فارسی:
var dateFormat = new Intl.DateTimeFormat("fa");
console.log(dateFormat.format(Date.now()));
خروجی «۱۳۹۸/۱۲/۱» را نمایش می‌دهد.


نمایش تاریخ شمسی با فرمت «۱۳۹۸ اسفند ۱, پنجشنبه»

برای تبدیل تاریخ میلادی به شمسی می‌توان از سازنده‌ی new Intl.DateTimeFormat با فرهنگ fa استفاده کرد. در اینجا ذکر مقدار long برای نام روز هفته، سبب درج نام روز می‌شود. نمایش سال به صورت عددی تنظیم شده‌است، ماه را به صورت بلند و نام کامل نمایش می‌دهد و مقدار روز را به صورت عددی درج می‌کند. این اعداد نیز فارسی هستند:
const date = new Date(Date.UTC(2020, 1, 20, 3, 0, 0, 200));
const faDate = new Intl.DateTimeFormat("fa", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
}).format(date);
console.log(faDate);
که برای نمونه سبب درج خروجی «۱۳۹۸ اسفند ۱, پنجشنبه» در کنسول توسعه دهندگان مرورگر خواهد شد.

اگر فقط نیاز به نمایش «۱ اسفند ۱۳۹۸» بود، می‌توان از تنظیمات زیر که در آن ماه، روز و سال ذکر شده‌اند و در آن، ماه به صورت کامل و بلند نمایش داده می‌شود، استفاده کرد:
const isoString = new Date().toISOString();
const date = new Date(isoString);
console.log(
  new Intl.DateTimeFormat("fa", {
    month: "long",
    day: "numeric",
    year: "numeric"
  }).format(date)
);

یک نکته: همین خروجی را با متد قدیمی toLocaleDateString نیز می‌توان به دست آورد؛ اما روش توصیه شده برای برنامه‌های جدید، همان استفاده از new Intl است.
console.log(
  new Date().toLocaleDateString("fa", {
    month: "long",
    day: "numeric",
    year: "numeric"
  })
);

نمایش تاریخ شمسی با فرمت «۹۸/۱۲/۱،‏ ۶:۳۰»

برای اینکار پس از ذکر فرهنگ fa، تمام اجزای تاریخ را به صورت عددی مشخص می‌کنیم و سال را نیز دو رقمی نمایش خواهیم داد:
const date = new Date(Date.UTC(2020, 1, 20, 3, 0, 0, 200));
const fmt = new Intl.DateTimeFormat("fa", {
  year: "2-digit",
  month: "numeric",
  day: "numeric",
  hour: "numeric",
  minute: "numeric"
});
console.log(fmt.format(date));
در این حالت اگر نیاز بود حتما اعداد ماه و روز، دو رقمی باشند، می‌توان تنظیم 2-digit را صریحا ذکر کرد:
const faDateTime = new Intl.DateTimeFormat("fa", {
  year: "2-digit",
  month: "2-digit",
  day: "2-digit",
  hour: "2-digit",
  minute: "2-digit",
  timeZoneName: "short"
}).format;
const now = Date.now();
console.log(faDateTime(now));
با خروجی «۹۸/۱۲/۰۱،‏ ۱۲:۵۹ (‎+۳:۳۰ گرینویچ)»

و یا اگر «۱ اسفند ۱۳۹۸،‏ ۰۹:۲۹ (UTC)» مدنظر بود، می‌توان ماه را به long تنظیم کرد و مقدار timeZone را صریحا ذکر نمود (که البته ذکر تنظیمات timeZone اختیاری است):
const faTime = new Intl.DateTimeFormat("fa", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "2-digit",
  minute: "2-digit",
  timeZoneName: "short",
  timeZone: "UTC"
});
console.log(faTime.format(now));

نمایش تاریخ‌های نسبی مانند «1 روز بعد»
برای نمایش تاریخ‌های نسبی، می‌توان از شیء new Intl.RelativeTimeFormat استفاده کرد:
const rtf = new Intl.RelativeTimeFormat("en", {
  localeMatcher: "best fit", // other values: "lookup"
  numeric: "always", // other values: "auto"
  style: "long", // other values: "short" or "narrow"
});
console.log(rtf.format(-1, "day"));
console.log(rtf.format(1, "day"));
با خروجی‌های «۱ روز پیش» و «۱ روز بعد»


نمایش اعداد فارسی توسط i18n API

احتمالا برای تبدیل اعداد انگلیسی به فارسی و نمایش آن‌ها، متدهایی را برای replace حروف و اعداد طراحی کرده‌اید. به کمک شیء استاندارد Intl.NumberFormat دیگر نیازی به آن‌ها نخواهید داشت!
خروجی شیء Intl.NumberFormat به همراه ذکر فرهنگ فارسی و هیچ تنظیم اضافه‌تری
console.log(new Intl.NumberFormat("fa").format(123456));
به صورت «۱۲۳٬۴۵۶» است که هم اعداد آن فارسی شده‌اند و هم به همراه جداکننده‌ی هزارگان خودکار است.

اگر می‌خواهید این جداکننده‌ی هزارگان نمایش داده نشود، نیاز است تنظیمات آن‌را به همراه useGrouping: false، به صورت زیر ذکر کرد:
console.log(
   new Intl.NumberFormat("fa", { useGrouping: false }).format(123456)
);

این شیء یک مقدار غیرعددی را
console.log(new Intl.NumberFormat("fa").format("تست"));
به صورت «ناعدد» نمایش می‌دهد.

و یا برای نمایش واحد پولی، می‌توان حالت نمایش را به currency و نوع currency را به IRR که ریال است، تنظیم کرد:
const gasPrice = new Intl.NumberFormat("fa", {
  style: "currency",
  currency: "IRR",
  minimumFractionDigits: 3
});
console.log(gasPrice.format(5.2597));
با این خروجی: «‎ریال ۵٫۲۶۰» که در اینجا امکان تنظیم نمایش تعداد اعشار آن نیز میسر است.

برای نمایش درصد پس از اعداد می‌توان از تنظیم زیر استفاده کرد:
const faPercent = new Intl.NumberFormat("fa", {
  style: "percent",
  minimumFractionDigits: 2
}).format;
console.log(faPercent(0.438));
که خروجی «۴۳٫۸۰٪» را نمایش می‌دهد.

و یا برای نمایش ممیز به همراه تنظیم دقت آن داریم:
const persianDecimal = new Intl.NumberFormat("fa", {
  minimumIntegerDigits: 2,
  maximumFractionDigits: 2
});
console.log(persianDecimal.format(3.1416));
با این خروجی: «۰۳٫۱۴»
نظرات مطالب
بررسی علت CPU Usage بالای برنامه در حال اجرا
در یک چنین حالتی بله. می‌تونید سرویس ویندوز ان اتی بنویسید برای بررسی خودکار. مطلب فوق یک بررسی دستی است. یک تایمر داخل این سرویس تعریف کنید سپس به کمک WMI ((+)) هر از چند لحظه یکبار، وضعیت CPU usage کل سیستم را بررسی کنید. سپس اگر بالا بود می‌شود کل پروسه‌های ویندوز را لیست کرد و نکته فوق را (بر اساس مرتب سازی اطلاعات با توجه به KernelTime + UserTime) به آن‌ها اعمال کرد. نهایتا یک گزارش متنی یا یک ایمیل می‌شود از این برنامه گرفت.
مطالب
آشنایی با نسخه بندی و چرخه انتشار نرم افزارها
نسخه بندی و چرخه انتشار یک نرم افزار، اهمیت زیادی در ارائه یک نرم افزار خوب دارد. هر چه نرم افزار شما بزرگ‌تر و از کتابخانه‌های بیشتری در تولید آن استفاده شده باشد، در بروز رسانی و نسخه بندی آن دقت بیشتری باید داشت و کار دشوار‌تری است. اما چگونه به بهترین روش، نسخه بندی نرم افرار خود را مدیریت نمایید.

مقدمه:
حتما نسخه بندی و نگارش‌های مختلف نرم افزار‌هایی را که استفاده می‌کنید، مشاهده نموده‌اید. نسخه‌های آلفا یا بتا یا نسخه بندی سالیانه یا با حروف و اعداد خاص. با این حال همه نرم افزار‌ها علاوه بر عناوین متعارف، یک نسخه بندی داخلی عددی، شمار‌ه‌ای هم دارند. بسته به حجم و اندازه نرم افزار‌ها، ممکن چرخه انتشار نرم افزار‌ها متفاوت باشند. سیاست عرضه نرم افزار در هر شرکت هم متفاوت است. مثلا شرکت مایکروسافت برای عرضه ویندوز ابتدا نسخه بتا یا پیش نمایش آن را عرضه نموده تا با دریافت بازخورد‌هایی از استفاده کنندگان، نسخه نهایی نرم افزار خود را با حداقل ایراد و خطا عرضه نماید. البته این بخاطر بزرگی نرم افزار ویندوز نیز می‌باشد اما شرکت ادوبی اکثرا هر یکی دو سال بدون عرضه نسخه‌های قبل از نهایی یک دفعه نسخه جدیدی را رسما عرضه می‌نماید.

چرخه انتشار نرم افزار:
 چرخه انتشار نرم افزار از زمان شروع کد نویسی تا عرضه نسخه نهایی می‌باشد که شامل چندین مرحله و عرضه نرم افزار می‌باشد.
  1. Pre-alpha
    این مرحله شامل تمام فعالیت‌های انجام شده قبل از مرحله تست می‌باشد. در این دوره آنالیز نیازمندیها، طراحی نرم افزار، توسعه نرم افزار و حتی تست واحد باشد. در نرم افزار‌های سورس باز چندین نسخه قبل از آلفا ممکن است عرضه شوند.
  2. Alpha
    این مرحله شامل همه فعالیت‌ها از زمان شروع تست می‌باشد. البته منظور از تست، تست تیمی و تست خود نرم افزار می‌باشد. نرم افزار‌های آلفا هنوز ممکن است خطا و اشکالاتی داشته باشند و ممکن است اطلاعات شما از بین رود. در این مرحله امکانات جدیدی مرتبا به نرم افزار اضافه می‌گردد.
  3. Beta
    نرم افزار بتا، همه قابلیت‌های آن تکمیل شده و خطا‌های زیادی برای کامل شدن نرم افزار وجود دارد. در این مرحله بیشتر به تست کاهش تاثیرات به کاربران و تست کارایی دقت می‌شود. نسخه بتا، اولین نسخه‌ای خواهد بود که بیرون شرکت و یا سازمان در دسترس قرار می‌گیرد. برخی توسعه دهندگان به این مرحله preview، technical preview یا early access نیز می‌گویند.
  4. Release candidate
    در این مرحله نرم افزار، آماده عرضه به مصرف کنندگان است و نرم افزارهایی مثل سیستم عامل‌های ویندوز در دسترس تولید کنندگان قرار گرفته تا با جدید‌ترین سخت افزار خود یکپارچه شوند.
  5.  General availability (GA)
    در این مرحله، عرضه عمومی نرم افزار و بازاریابی و فروش نرم افزار مد نظر است و علاوه بر این تست امنیتی و در نرم افزار‌های خیلی بزرگ عرضه جهانی صورت می‌گیرد 
مراحلی همچون عرضه در وب و پشتیبانی نیز وجود دارند.

نسخه بندی نرم افزار:
برنامه‌های ویندوزی یا وب در ویژوال استادیو یک فایل AssemblyInfo دارند که در قسمت آخر آن، اطلاعات مربوط به نسخه نرم افزار ذخیره می‌شود. هر نسخه نرم افزار شامل چهار عدد می‌باشد که با نقطه از هم جدا شده است.
  • Major Version
    وقتی افزایش می‌یابد که تغییرات قابل توجهی در نرم افزار ایجاد شود
  • Minor Version
    وقتی افزایش یابد که ویژگی جزئی یا اصلاحات قابل توجهی به نرم افزار ایجاد شود.
  • Build Number
    به ازای هر بار ساخته شدن پروژه افزایش می‌یابد.
  • Revision
    وقتی افزایش می‌یابد که نواقص و باگ‌های کوچکی رفع شوند. 
وقتی که major یا minor افزایش یابد می‌تواند با کلماتی همچون alpha، beta یا release candidate همراه شود.در اکثر برنامه‌های تجاری اولین شمارهٔ انتشار یک محصول از نسحهٔ شمارهٔ یک شروع می‌شود. ترتیب نسخه بندی هم ممکن است تغییر یابد
major.minor[.build[.reversion]]
یا
major.minor[.maintenance[.build]]
نسخه بندی مایکروسافت:
اگر به نسخه برنامه Office توجه کرده باشید مثلا Office 2013 نسخه 15.0.4481.1508 می‌باشد که در این روش از تاریخ شروع پروژه و تعداد ماه‌ها یا روز‌ها و یا ثانیه‌ها با یک الگوریتم خاص برای تولید نسخه نرم افزار استفاده می‌شود.
نسخه بندی معنایی:

به عنوان یک راه حل، مجموعه‌ی ساده‌ای از قوانین و الزامات که چگونگی طراحی شماره‌های نسخه و افزایش آن را مشخص می‌کند، وجود دارد. برای کار کردن با این سیستم، شما ابتدا نیاز به اعلام API عمومی دارید. این خود ممکن است شامل مستندات و یا اجرای کد باشد.

علیرغم آن، مهم است که این API، روشن و دقیق باشد. هنگامیکه API عمومی خود را تعیین کردید، تغییرات برنامه شما بر روی نسخه API عمومی تاثیر خواهد داشت و آنرا افزایش خواهد داد. بر این اساس، این مدل نسخه‌بندی را در نظر بگیرید: X.Y.Z یعنی (Major.Minor.Patch).

رفع حفره‌هایی که بر روی API عمومی تاثیر نمی‌گذارند، مقدار Patch را افزایش می‌دهند، تغییرات جدیدی که سازگار با نسخه قبلی است، مقدار Minor را افزایش می‌دهند و تغییرات جدیدی که کاملا بدیع هستند و به نحوی با تغییرات قبلی سازگار نیستند مقدار Major را افزایش می‌دهند. 

  1. نرم‌افزارهایی که از نسخه بندی معنایی استفاده می‌کنند، باید یک API عمومی داشته باشند. این API می‌تواند در خود کد یا و یا به طور صریح در مستندات باشد که باید دقیق و جامع باشد.
  2. یک شماره نسخه صحیح باید به شکل X.Y.Z باشد که در آن X،Y و Z اعداد صحیح غیر منفی هستند. X نسخه‌ی Major می‌باشد، Y نسخه‌ی Minor و Z نسخه‌ی Patch می‌باشد. هر عنصر باید یک به یک و بصورت عددی افزایش پیدا کند. به عنوان مثال: 1.9.0 -> 1.10.0 -> 1.11.0
  3. هنگامی که به یک نسخه‌ی Major یک واحد اضافه می‌شود، نسخه‌ی Minor و Patch باید به حالت 0 (صفر) تنظیم مجدد گردد. هنگامی که به شماره نسخه‌ی Minor یک واحد اضافه می‌شود، نسخه‌ی Patch باید به حالت 0 (صفر) تنظیم مجدد شود. به عنوان مثال: 1.1.3 -> 2.0.0 و 2.1.7 -> 2.2.0
  4. هنگامیکه یک نسخه از یک کتابخانه منتشر می‌شود، محتوای کتابخانه مورد نظر نباید به هیچ وجه تغییری داشته باشد. هر گونه تغییر جدیدی باید در قالب یک نسخه جدید انتشار پیدا کند.
  5. نسخه‌ی Major صفر (0.Y.Z) برای توسعه‌ی اولیه است. هر چیزی ممکن است در هر زمان تغییر یابد. API عمومی را نباید پایدار در نظر گرفت.
  6. نسخه 1.0.0 در حقیقت API عمومی را تعریف می‌کند. چگونگی تغییر و افزایش هر یک از نسخه‌ها بعد از انتشار این نسخه، وابسته به API عمومی و تغییرات آن می‌باشد.
  7. نسخه Patch یا (x.y.Z | x > 0) فقط در صورتی باید افزایش پیدا کند که تغییرات ایجاد شده در حد برطرف کردن حفره‌های نرم‌افزار باشد. برطرف کردن حفره‌های نرم‌افزار شامل اصلاح رفتارهای اشتباه در نرم‌افزار می‌باشد.
  8. نسخه Minor یا (x.Y.z | x > 0) فقط در صورتی افزایش پیدا خواهد کرد که تغییرات جدید و سازگار با نسخه قبلی ایجاد شود. همچنین این نسخه باید افزایش پیدا کند اگر بخشی از فعالیت‌ها و یا رفتارهای قبلی نرم‌افزار به عنوان فعالیت منقرض شده اعلام شود. همچنین این نسخه می‌تواند افزایش پیدا کند اگر تغییرات مهم و حیاتی از طریق کد خصوصی ایجاد و اعمال گردد. تغییرات این نسخه می‌تواند شامل تغییرات نسخه Patch هم باشد. توجه به این نکته ضروری است که در صورت افزایش نسخه Minor، نسخه Patch باید به 0 (صفر) تغییر پیدا کند.
  9. نسخه Major یا (X.y.z | X > 0) در صورتی افزایش پیدا خواهد کرد که تغییرات جدید و ناهمخوان با نسخه فعلی در نرم‌افزار اعمال شود. تغییرات در این نسخه می‌تواند شامل تغییراتی در سطح نسخه Minor و Patch نیز باشد. باید به این نکته توجه شود که در صورت افزایش نسخه Major، نسخه‌های Minor و Patch باید به 0 (صفر) تغییر پیدا کنند.
  10. یک نسخه قبل از انتشار می‌تواند توسط یک خط تیره (dash)، بعد از نسخه Patch (یعنی در انتهای نسخه) که انواع با نقطه (dot) از هم جدا می‌شوند، نشان داده شود. نشان‌گر نسخه قبل از انتشار باید شامل حروف، اعداد و خط تیره باشد [0-9A-Za-z-]. باید به این نکته دفت داشت که نسخه‌های قبل از انتشار خود به تنهایی یک انتشار به حساب می‌آیند اما اولویت و اهمیت نسخه‌های عادی را ندارد. برای مثال: 1.0.0-alpha ، 1.0.0-alpha.1 ، 1.0.0-0.3.7 ، 1.0.0-x.7.z.92
  11. یک نسخه Build می‌تواند توسط یک علامت مثبت (+)، بعد از نسخه Patch یا نسخه قبل از انتشار (یعنی در انتهای نسخه) که انواع آن با نقطه (dot) از هم جدا می‌شوند، نشان داده شود. نشان‌گر نسخه Build باید شامل حروف، اعداد و خط تیره باشد [0-9A-Za-z-]. باید به این نکته دقت داشت که نسخه‌های Build خود به تنهایی یک انتشار به حساب می‌آیند و اولویت و اهمیت بیشتری نسبت به نسخه‌های عادی دارند. برای مثال: 1.0.0+build.1 ، 1.3.7+build.11.e0f985a
  12. اولویت‌بندی نسخه‌ها باید توسط جداسازی بخش‌های مختلف یک نسخه به اجزای تشکیل دهنده آن یعنی Minor، Major، Patch، نسخه قبل از انتشار و نسخه Build و ترتیب اولویت بندی آن‌ها صورت گیرد. نسخه‌های Minor، Major و Patch باید بصورت عددی مقایسه شوند. مقایسه نسخه‌های قبل از انتشار و نسخه Build باید توسط بخش‌های مختلف که توسط جداکننده‌ها (نقطه‌های جداکننده) تفکیک شده است، به این شکل سنجیده شود:

بخش‌هایی که فقط حاوی عدد هستند، بصورت عددی مقایسه می‌شوند و بخش‌هایی که حاری حروف و یا خط تیره هستند بصورت الفبایی مقایسه خواهند شد.

بخش‌های عددی همواره اولویت پایین‌تری نسبت به بخش‌های غیر عددی دارند. برای مثال:

1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0-rc.1+build.1 < 1.0.0 < 1.0.0+0.3.7 < 1.3.7+build < 1.3.7+build.2.b8f12d7 < 1.3.7+build.11.e0f985a

منبع نسخه بندی معنایی : semver.org

 
بازخوردهای پروژه‌ها
راهنمایی در مورد چیدن اجزای گزارش
با عرض سلام من یک گزارشی طراحی کردم می‌خواستم اگر براتون مقدور هست فقط چک بکنید که اجزای گزارش را درست در صفحه قرار دادم یا نه. و اینکه در همین حین برای قسمت Footer یک مشکل دارم.

من قسمت  بالای صفحه (تا جدول هزینه ها) درون Header قرار دادم و خود جدول رو با استفاده از MainTableColumns ایجاد کردم حال می‌خواهم قسمت پایین صفحه رو ایجاد بکنم. حال سوال من اینجاست که آیا قسمت پایین صفحه(آیتم‌های بعد از جدول هزینه ها) باید در قسمت Footer گزارش قرار بگیرند؟ اگر جواب مثبت هست چگونه می‌تونم در قسمت Footer همانند قسمت Header جدول ایجاد بکنم که بتوانم این ساختار را ایجاد کنم.

ممنونم
مطالب
نحوه ذخیره شدن متن در فایل‌های PDF
تبدیل بی عیب و نقص یک فایل PDF (انواع و اقسام آن‌ها) به متن قابل درک بسیار مشکل است. در ادامه بررسی خواهیم کرد که چرا.
برخلاف تصور عموم، ساختار یک صفحه PDF شبیه به یک صفحه فایل Word نیست. این صفحات درحقیقت نوعی Canvas برای نقاشی هستند. در این بوم نقاشی، شکل، تصویر، متن و غیره در مختصات خاصی قرار خواهند گرفت. حتی کلمه «متن» می‌تواند به صورت سه حرف در سه مختصات خاص یک صفحه PDF نقاشی شود. برای درک بهتر این مورد نیاز است سورس یک صفحه PDF را بررسی کرد.

نحوه استخراج سورس یک صفحه PDF

using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace TestReaders
{
    class Program
    {
        static void writePdf()
        {
            using (var document = new Document(PageSize.A4))
            {
                var writer = PdfWriter.GetInstance(document, new FileStream("test.pdf", FileMode.Create));
                document.Open();

                document.Add(new Paragraph("Test"));

                PdfContentByte cb = writer.DirectContent;
                BaseFont bf = BaseFont.CreateFont();
                cb.BeginText();
                cb.SetFontAndSize(bf, 12);
                cb.MoveText(88.66f, 367);
                cb.ShowText("ld");
                cb.MoveText(-22f, 0);
                cb.ShowText("Wor");
                cb.MoveText(-15.33f, 0);
                cb.ShowText("llo");
                cb.MoveText(-15.33f, 0);
                cb.ShowText("He"); 
                cb.EndText();

                PdfTemplate tmp = cb.CreateTemplate(250, 25);
                tmp.BeginText();
                tmp.SetFontAndSize(bf, 12);
                tmp.MoveText(0, 7);
                tmp.ShowText("Hello People");
                tmp.EndText();
                cb.AddTemplate(tmp, 36, 343);
            }

            Process.Start("test.pdf");
        }

        private static void readPdf()
        {
            var reader = new PdfReader("test.pdf");
            int intPageNum = reader.NumberOfPages;
            for (int i = 1; i <= intPageNum; i++)
            {
                byte[] contentBytes = reader.GetPageContent(i);
                File.WriteAllBytes("page-" + i + ".txt", contentBytes);
            }
            reader.Close();
        }

        static void Main(string[] args)
        {
            writePdf();
            readPdf();
        }
    }
}
فایل PDF تولیدی حاوی سه عبارت کامل و مفهوم می‌باشد:


اگر علاقمند باشید که سورس واقعی صفحات یک فایل PDF را مشاهده کنید، نحوه انجام آن توسط کتابخانه iTextSharp به صورت فوق است.
هرچند متد GetPageContent آرایه‌ای از بایت‌ها را بر می‌گرداند، اما اگر حاصل نهایی را در یک ادیتور متنی باز کنیم، قابل مطالعه و خواندن است. برای مثال، سورس مثال فوق (محتوای فایل page-1.txt تولید شده) به نحو زیر است:
q
BT
36 806 Td
0 -18 Td
/F1 12 Tf
(Test)Tj
0 0 Td
ET
Q
BT
/F1 12 Tf
88.66 367 Td
(ld)Tj
-22 0 Td
(Wor)Tj
-15.33 0 Td
(llo)Tj
-15.33 0 Td
(He)Tj
ET
q 1 0 0 1 36 343 cm /Xf1 Do Q
و تفسیر این عملگرها به این ترتیب است:
SaveGraphicsState(); // q
BeginText(); // BT
MoveTextPos(36, 806); // Td
MoveTextPos(0, -18); // Td
SelectFontAndSize("/F1", 12); // Tf
ShowText("(Test)"); // Tj
MoveTextPos(0, 0); // Td
EndTextObject(); // ET
RestoreGraphicsState(); // Q
BeginText(); // BT
SelectFontAndSize("/F1", 12); // Tf
MoveTextPos(88.66, 367); // Td
ShowText("(ld)"); // Tj
MoveTextPos(-22, 0); // Td
ShowText("(Wor)"); // Tj
MoveTextPos(-15.33, 0); // Td
ShowText("(llo)"); // Tj
MoveTextPos(-15.33, 0); // Td
ShowText("(He)"); // Tj
EndTextObject(); // ET
SaveGraphicsState(); // q
TransMatrix(1, 0, 0, 1, 36, 343); // cm
XObject("/Xf1"); // Do
RestoreGraphicsState(); // Q
همانطور که ملاحظه می‌کنید کلمه Test به مختصات خاصی انتقال داده شده و سپس به کمک اطلاعات فونت F1، ترسیم می‌شود.
تا اینجا استخراج متن از فایل‌های PDF ساده به نظر می‌رسد. باید به دنبال Tj گشت و حروف مرتبط با آن‌را ذخیره کرد. اما در مورد «ترسیم» عبارات hello world و hello people اینطور نیست. عبارت hello world به حروف متفاوتی تقسیم شده و سپس در مختصات مشخصی ترسیم می‌گردد. عبارت hello people به صورت یک شیء ذخیره شده در قسمت منابع فایل PDF، بازیابی و نمایش داده می‌شود و اصلا در سورس صفحه جاری وجود ندارد.
این تازه قسمتی از نحوه عملکرد فایل‌های PDF است. در فایل‌های PDF می‌توان قلم‌ها را مدفون ساخت. همچنین این قلم‌ها نیز تنها زیر مجموعه‌ای از قلم اصلی مورد استفاده هستند. برای مثال اگر عبارت Test قرار است نمایش داده شود، فقط اطلاعات T، e و s در فایل نهایی PDF قرار می‌گیرند. به علاوه امکان تغییر کلی شماره Glyph متناظر با هر حرف نیز توسط PDF writer وجود دارد. به عبارتی الزامی نیست که مشخصات اصلی فونت حتما حفظ شود.
شاید بعضی از PDFهای فارسی را دیده باشید که پس از کپی متن آن‌ها در برنامه Adobe reader و سپس paste آن در جایی دیگر، متن حاصل قابل خواندن نیست. علت این است که نحوه ذخیره سازی قلم مورد استفاده کاملا تغییر کرده است و برای بازیابی متن اینگونه فایل‌ها، استفاده از OCR ساده‌ترین روش است. برای نمونه در این قلم جدید مدفون شده، دیگر شماره کاراکتر 0x41 مساوی A نیست. بنابر سلیقه PDF writer این شماره به Glyph دیگری انتساب داده شده و چون قلم و مشخصات هندسی Glyph مورد استفاده در فایل PDF ذخیره می‌شود، برای نمایش این نوع فایل‌ها هیچگونه مشکلی وجود ندارد. اما متن آن‌ها به سادگی قابل بازیابی نیست.
مطالب
مقایسه کارآیی روش‌های مختلف جایگزین کردن حروف در یک رشته در برنامه‌های NET.
فرض کنید قصد دارید عملیات نرمال سازی اطلاعات را بر روی یک رشته انجام داده و برای مثال اعداد فارسی و انگلیسی موجود در یک رشته را یک‌دست کنید. اولین روشی که برای اینکار به ذهن می‌رسد، استفاده از متد Replace است:
private static string toPersianNumbersUsingReplace(string data)
{
    if (string.IsNullOrWhiteSpace(data)) return string.Empty;
    return
      data
        .Replace("0", "\u06F0")
        .Replace("1", "\u06F1")
        .Replace("2", "\u06F2")
        .Replace("3", "\u06F3")
        .Replace("4", "\u06F4")
        .Replace("5", "\u06F5")
        .Replace("6", "\u06F6")
        .Replace("7", "\u06F7")
        .Replace("8", "\u06F8")
        .Replace("9", "\u06F9");
}
اما آیا این روش، کارآیی مناسبی را به همراه دارد؟ در ادامه چند روش دیگر را نیز جهت جایگزین کردن حروف، معرفی کرده و کارآیی آن‌ها را با هم مقایسه می‌کنیم.


جایگزین کردن حروف با استفاده از Replace معمولی توسط رشته‌ها

نگارش اصلی تبدیل تمام اعداد موجود در یک رشته به اعداد فارسی، به صورت زیر است که در آن یک دست سازی اعداد عربی هم درنظر گرفته شده‌اند (برای مثال طرز نگارش عدد 4 فارسی و عربی متفاوت است):
        private static string toPersianNumbersUsingReplace(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;
            return
                toEnglishNumbers(data)
                .Replace("0", "\u06F0")
                .Replace("1", "\u06F1")
                .Replace("2", "\u06F2")
                .Replace("3", "\u06F3")
                .Replace("4", "\u06F4")
                .Replace("5", "\u06F5")
                .Replace("6", "\u06F6")
                .Replace("7", "\u06F7")
                .Replace("8", "\u06F8")
                .Replace("9", "\u06F9");
        }

        private static string toEnglishNumbers(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;
            return
               data.Replace("\u0660", "0") //٠
                   .Replace("\u06F0", "0") //۰
                   .Replace("\u0661", "1") //١
                   .Replace("\u06F1", "1") //۱
                   .Replace("\u0662", "2") //٢
                   .Replace("\u06F2", "2") //۲
                   .Replace("\u0663", "3") //٣
                   .Replace("\u06F3", "3") //۳
                   .Replace("\u0664", "4") //٤
                   .Replace("\u06F4", "4") //۴
                   .Replace("\u0665", "5") //٥
                   .Replace("\u06F5", "5") //۵
                   .Replace("\u0666", "6") //٦
                   .Replace("\u06F6", "6") //۶
                   .Replace("\u0667", "7") //٧
                   .Replace("\u06F7", "7") //۷
                   .Replace("\u0668", "8") //٨
                   .Replace("\u06F8", "8") //۸
                   .Replace("\u0669", "9") //٩
                   .Replace("\u06F9", "9"); //۹
        }


جایگزین کردن حروف با استفاده از Replace معمولی توسط کاراکترها

اینبار همان حالت قبل را درنظر بگیرید؛ با این تفاوت که بجای رشته‌ها از کاراکترها استفاده شود. برای مثال بجای:
  .Replace("\u0669", "9") //٩
خواهیم داشت:
  .Replace('\u0669', '9') //٩


جایگزین کردن حروف با استفاده از String Builder

در ادامه بجای استفاده از متد Replace متداول، آرایه‌ای از حروف قابل جایگزینی را توسط یک StringBuilder ایجاد کرده و حروف را یکی یکی تبدیل می‌کنیم و به این ترتیب برخلاف متد Replace، هربار برای جایگزینی یک مورد خاص، مجددا از ابتدای رشته شروع به جستجو نمی‌شود:
        private static string toPersianNumbersUsingStringBuilder(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;

            var strBuilder = new StringBuilder(data);
            for (var i = 0; i < strBuilder.Length; i++)
            {
                switch (strBuilder[i])
                {
                    case '0':
                    case '\u0660':
                        strBuilder[i] = '\u06F0';
                        break;

                    case '1':
                    case '\u0661':
                        strBuilder[i] = '\u06F1';
                        break;

                    case '2':
                    case '\u0662':
                        strBuilder[i] = '\u06F2';
                        break;

                    case '3':
                    case '\u0663':
                        strBuilder[i] = '\u06F3';
                        break;

                    case '4':
                    case '\u0664':
                        strBuilder[i] = '\u06F4';
                        break;

                    case '5':
                    case '\u0665':
                        strBuilder[i] = '\u06F5';
                        break;

                    case '6':
                    case '\u0666':
                        strBuilder[i] = '\u06F6';
                        break;

                    case '7':
                    case '\u0667':
                        strBuilder[i] = '\u06F7';
                        break;

                    case '8':
                    case '\u0668':
                        strBuilder[i] = '\u06F8';
                        break;

                    case '9':
                    case '\u0669':
                        strBuilder[i] = '\u06F9';
                        break;

                    default:
                        strBuilder[i] = strBuilder[i];
                        break;
                }
            }

            return strBuilder.ToString();
        }


جایگزین کردن حروف با استفاده از ToCharArray

متد زیر دقیقا شبیه به حالت استفاده از String Builder است؛ با یک تفاوت مهم: بجای استفاده از String Builder برای تهیه‌ی آرایه‌ای از حروف قابل تغییر، از متد ToCharArray استفاده شده‌است:
        private static string toPersianNumbersUsingToCharArray(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;

            var letters = data.ToCharArray();
            for (var i = 0; i < letters.Length; i++)
            {
                switch (letters[i])
                {
                    case '0':
                    case '\u0660':
                        letters[i] = '\u06F0';
                        break;

                    // مانند قبل

                }
            }

            return new string(letters);
        }


جایگزین کردن حروف با استفاده از string.Create

string.Create یکی از تازه‌های NET Core. است که امکان تغییر مستقیم یک قطعه string را میسر می‌کند:
        private static string toPersianNumbersUsingStringCreate(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;

            return string.Create(data.Length, data, (chars, context) =>
            {
                for (var i = 0; i < data.Length; i++)
                {
                    switch (context[i])
                    {
                        case '0':
                        case '\u0660':
                            chars[i] = '\u06F0';
                            break;

                    // مانند قبل

                    }
                }
            });
        }
در کدهای فوق، ابتدا طول رشته‌ی نهایی بازگشتی از string.Create مشخص می‌شود. سپس توسط پارامتر دوم، داده‌هایی که قرار است بر روی آن‌ها کاری صورت گیرد به متد string.Create ارسال می‌شوند. در آخر عملیات نهایی در action delegate تعریف شده رخ می‌دهد. در اینجا chars، به بافر درونی رشته‌ای که بازگشت داده می‌شود، اشاره می‌کند و باید پر شود (این بافر مستقیما در دسترس است). context همان پارامتر دوم متد string.Create است.

توضیحات بیشتر:
در دات نت، رشته‌ها نوع‌های ارجاعی (reference type) غیرقابل تغییر (immutable) هستند. به این معنا که هر زمانیکه ایجاد شدند، دیگر نمی‌توان محتوای آن‌ها را تغییر داد. به همین جهت است که مجبور هستیم آن‌ها را برای مثال توسط ToCharArray به یک آرایه تبدیل کنیم و سپس این آرایه‌ی قابل تغییر را ویرایش کنیم. در حین کار با رشته‌ها، این غیرقابل تغییر بودن، سبب تخصیص حافظه‌های بیش از حدی می‌شوند. اگر بخواهیم قسمتی از یک رشته را جدا و یا جایگزین کنیم و یا تعدادی رشته را با هم جمع بزنیم، نتیجه‌ی آن نیاز به یک تخصیص حافظه‌ی جدید را دارد. راه حل استاندارد مواجه شدن با این مشکل، استفاده از StringBuilder است که از یک بافر داخلی برای انجام کارهای خودش استفاده می‌کند و زمانیکه نتیجه‌ی نهایی را از آن درخواست می‌کنیم، تخصیص حافظه‌ای را برای تولید رشته‌ی حاصل انجام می‌دهد. البته این مورد نیاز به اندازه گیری دارد و ارزش StringBuilder با حجم بالایی از اطلاعات متنی مشخص می‌شود؛ وگرنه همانطور که مشاهده می‌کنید (در نتیجه‌ی نهایی بحث در ادامه)، الزاما کدهای سریعتری را به همراه نخواهد داشت.
هدف از string.Create، ایجاد رشته‌ها از داده‌های موجود است. هدف اصلی آن کاهش تخصیص‌های حافظه و کپی کردن اطلاعات است و امضای آن به صورت زیر می‌باشد:
public static string Create<TState> (int length, TState state, System.Buffers.SpanAction<char,TState> action);
مزیت این متد، عدم نیاز به یک پیش‌بافر است؛ به این معنا که مستقیما بر روی قسمتی از حافظه کار می‌کند که ارجاعی را به رشته‌ی «بازگشتی» دارد. یعنی در حالت کار با string.Create، غیرقابل تغییر بودن رشته‌ها در دات نت دیگر صادق نخواهد بود و برای تغییر آن نیازی به تخصیص بافر، کپی کردن و تخصیص حافظه‌ی نهایی برای بازگشت نتیجه نیست. پارامتر SpanAction آن، امکان دسترسی مستقیم به این ناحیه‌ی از حافظه را میسر می‌کند.
هنگام کار با این متد، chars ای که در اختیار ما قرار می‌گیرد، یک <Span<char اشاره کننده به رشته‌ی نهایی است که قرار است بازگشت داده شود (در ابتدای کار بر اساس اندازه‌ای که مشخص می‌شود، یک رشته‌ی خالی تخصیص داده می‌شود، اما بافر پر کردن آن اینبار در دسترس است و نیازی به تخصیص و کپی جداگانه‌ای را ندارد). بنابراین روش کار با این متد، پر کردن بافر درونی رشته‌ی بازگشتی (همان chars در اینجا) به صورت مستقیم است؛ کاری که با یک رشته‌ی معمولی نمی‌توان انجام داد.
State یا همان پارامتر دوم این متد، هر چیزی می‌تواند باشد. اگر نیاز است چندین رشته را در اینجا دریافت کنید تا بتوان بر اساس آن رشته‌ی نهایی را تشکیل داد، یک struct را تعریف کرده و بجای state به آن ارسال کنید. سپس این state توسط پارامتر context مربوط به SpanAction<char, string> action قابل دریافت و استفاده‌است که در این مثال، context همان data ارسالی به این متد است.

سؤال: در حین کار با string.Create، باید از پارامتر data استفاده کنیم و یا از context دریافتی؟ به نظر در مثال فوق، data و context یکی هستند. اکنون داخل action delegate مهیا که جهت ساخت رشته‌ی نهایی بکار می‌رود، باید از data استفاده کرد و یا از context؟
 return string.Create(data.Length, data, (chars, context) => {});
در اینجا اگر در داخل action delegate، ارجاعی را به data داشته باشیم، یک closure تشکیل می‌شود و در این حالت کامپایلر برای مدیریت آن، نیاز به تولید یک کلاس را در پشت صحنه خواهد داشت که خودش سبب کاهش کارآیی می‌گردد. به همین جهت متد Create، پارامتر state را به صورت معمولی دریافت می‌کند و آن‌را توسط context در اختیار delegate قرار می‌دهد تا نیازی نباشد delegate تعریف شده، یک closure را تشکیل دهد.


نتیجه‌ی نهایی بررسی کارآیی روش‌های مختلف جایگزین کردن حروف در یک رشته

کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: ReplacePerformanceTests.zip


ستون op/s در اینجا، مهم‌ترین ستون گزارش است و به معنای تعداد عملیات قابل انجام در یک ثانیه است. از 670 هزار عملیات در ثانیه با Replace معمولی، به 5 میلیون عملیات در ثانیه رسیده‌ایم که بسیار قابل توجه‌است.
همانطور که مشاهده می‌کنید، string.Create، سریعترین نگارش موجود است. در این بین نگارشی که از ToCharArray استفاده می‌کند، قابلیت انتقال بیشتری را دارد؛ از این جهت که نگارش‌های دیگر NET. هنوز دسترسی به string.Create را ندارند. همچنین نگارش کاراکتری متد Replace، از متد رشته‌ای آن سریعتر عمل کرده‌است.
مطالب
Angular Material 6x - قسمت دوم - معرفی Angular Flex layout
در این سری قصد داریم یک برنامه‌ی ساده‌ی دفترچه تلفن را توسط Angular 6x و کامپوننت‌های متریال آن ایجاد کنیم؛ اما Grid جزئی از بسته‌ی Angular Material نیست. بنابراین برای طرحبندی برنامه و قرار دادن المان‌های مختلف در مکان‌های تعیین شده‌ی صفحه، از Angular FlexBox Module استفاده خواهیم کرد که محصور کننده‌ی CSS 3 FlexBox است.


آشنایی با Flex Layout Box Model

برای طراحی ظاهر یک برنامه‌ی وب نیاز است عناصر آن‌را در مکان‌های مختلفی از صفحه قرار داد که به آن Layout گفته می‌شود. برای این منظور عموما 4 روش ذیل مرسوم هستند:
1. Table
2. Float, position, clear
3. CSS Grids
4. FlexBox CSS

امروزه دیگر آنچنان روش‌های 1 و 2 به صورت مستقیم مورد استفاده قرار نمی‌گیرند. CSS Grid روش نهایی طراحی Layout در آینده خواهد بود و در حال حاضر تعداد مرورگرهایی که از آن پشتیبانی می‌کنند، قابل توجه نیست؛ اما از FlexBox در IE 11، کروم 21 و فایرفاکس 22 به بعد پشتیبانی می‌شود.


FlexBox CSS، سیلان المان‌های قرار گرفته‌ی در داخل آن‌را سبب می‌شود. در اینجا یک container اصلی وجود دارد که در برگیرنده‌ی المان‌ها است. در تصویر فوق دو محور را مشاهده می‌کنید. محور افقی از چپ به راست ادامه پیدا می‌کند. محور عمودی نحوه‌ی ارتباط عناصر را مشخص می‌کند.
اکنون این سؤال مطرح می‌شود که چه تفاوتی بین یک Grid و FlexBox CSS وجود دارد؟ در یک Grid طراحی دو بعدی سطرها و ستون وجود دارد. اما به FlexBox باید به صورت سیلان یک بعدی سلول‌ها نگاه کرد. برای مثال عناصر قرار گرفته‌ی درون Container یا به صورت افقی درون آن گسترده شده و قرار می‌گیرند و یا به صورت عمودی.


نحوه‌ی تفکر و کارکرد با FlexBox چگونه است؟

در اینجا باید به دو مفهوم دقت داشت:
الف) سیلان عناصر درون Container که می‌تواند افقی و یا عمودی باشد.
ب) اندازه‌ی المان‌ها که می‌تواند ثابت و یا نسبی باشد.

یک Container، جهت سیلان عناصر درون آن‌را مشخص می‌کند. المان‌های آن، اندازه، فاصله‌ی از یکدیگر و ترتیب قرارگیری را ارائه می‌دهند. یک flex container می‌تواند شامل چندین flex container تو در تو نیز باشد.


نحوه‌ی سیلان عناصر در FlexBox چگونه است؟

برای نمونه طرحبندی متداول ذیل را درنظر بگیرید:


نحوه تفکر در مورد طراحی این طرحبندی، باید از بیرون به درون و از بالا به پایین (سیلان عمودی) باشد:


سپس نحوه‌ی سیلان عناصر درون Containerهای تعریف شده را مشخص می‌کنیم. برای مثال اولین Container دارای سیلان افقی از چپ به راست خواهد بود که عنصر سوم آن به دلیل اندازه‌های مشخص دو عنصر قبلی، به سطر دوم منتقل شده‌است.


در ادامه به قسمت میانی می‌رسیم که آن نیز دارای سیلان افقی از چپ به راست است:


در اینجا نیز می‌توان سه Container را متصور شد که وسطی دارای سیلان افقی از راست به چپ است و مواردی که بر اساس اندازه‌ی آن‌ها در یک سطر جا نشده‌اند، به سطر بعدی منتقل خواهند شد:


و تمام این سیلان‌ها و انتقال به سطرهای بعدی بر اساس اندازه‌ی المان‌ها صورت می‌گیرد:


البته در این تصویر یک ایراد هم وجود دارد. با توجه به اینکه در ناحیه‌ی میانی سه Container تعریف خواهند شد. Container ایی که در میان آن قرار می‌گیرد، دارای سیلان خاص خودش است و اندازه‌های آن باید نسبت به این Container تعریف شوند و نه نسبت به کل ناحیه‌ی میانی. یعنی بجای اینکه 50 درصد، 25، 25 و 50 درصد را داشته باشیم، این‌ها در اصل 100 درصد، 50 و 50 درصد و سپس 100 درصد هستند.


معرفی کتابخانه‌ی Angular Flex Layout

برای کار با Flex CSS نیاز است:
- مقدار زیادی کد CSS نوشت.
- نیاز به درک عمیقی از Flex Box دارد.
- نیاز است با باگ‌های مرورگرها و تفاوت‌های پیاده سازی‌های آن‌ها در مورد FlexBox آشنا بود.
- نیاز به Prefixing دارد.
- برای Angular طراحی نشده‌است.

جهت رفع این مشکلات و محدودیت‌ها، تیم Angular کتابخانه‌ای را به نام Angular Flex Layout مخصوص نگارش‌های جدید Angular طراحی کرده‌است. این کتابخانه مستقل از Angular Material است اما عموما به همراه آن استفاده می‌شود.

مزایای کار با کتابخانه‌ی Angular flex layout
- یک کتابخانه‌ی متکی به خود و مستقل است و برای کار با آن الزامی به استفاده‌ی از Angular Material نیست.
- به همراه هیچ فایل CSS جانبی ارائه نمی‌شود.
- پیاده سازی TypeScript ایی دارد. در اصل یک سری directives مخصوص Angular است که با TypeScript نوشته شده‌است.
- به صورت پویا و inline تمام CSSهای مورد نیاز را تولید و تزریق می‌کند.
- به همراه یک API استاتیک است و همچنین یک API واکنشگرا
- با Angular CLI نیز یکپارچه شده‌است.


نصب و تنظیم کتابخانه‌ی Angular Flex layout

برای نصب این کتابخانه، در ریشه‌ی پروژه دستور زیر را صادر کنید:
 npm install @angular/flex-layout --save
سپس ماژول آن‌را باید به shared.module.ts اضافه کرد:
import { FlexLayoutModule } from "@angular/flex-layout";

@NgModule({
  imports: [
    FlexLayoutModule
  ],
  exports: [
    FlexLayoutModule
  ]
})
export class SharedModule {
}


کار با API استاتیک Angular Flex layout

API استاتیک Angular Flex layout شامل این مزایا و مشخصات است:
- به صورت یکسری دایرکتیو Angular طراحی شده‌است که به HTML قالب کامپوننت‌ها اضافه می‌شود.
- از data binding پشتیبانی می‌کند.
- CSS نهایی را به صورت پویا و inline تولید و به صفحه تزریق می‌کند. Inline CSS تزریق شده به ویژگی‌های styles هر المان تزریق می‌شوند و موارد مشابه را در صورت وجود بازنویسی می‌کنند.
- از تشخیص تغییرات پشتیبانی می‌کند.
- به همراه ویژگی‌های fxHide و fxShow است.
- کارآیی مطلوبی دارد.

در اینجا برای تعریف container اصلی از دایرکتیوهای زیر استفاده می‌شود:
- fxLayout جهت‌های flex را مشخص می‌کند.
<div fxLayout="row" 
     fxLayout.xs="column"></div>
- fxLayout می‌تواند دارای مقداری مانند row، column و row-reverse و column-reverse باشد. برای مثال مقدار row-reverse‌، نمایش از راست به چپ را سبب می‌شود.
- fxLayoutWrap مشخص می‌کند که آیا المان‌ها باید به سطر و یا ستون بعدی منتقل شوند یا خیر؟
<div fxLayoutWrap></div>
- fxLayoutGap فاصله‌ی بین المان‌ها را مشخص می‌کند.
<div fxLayoutGap="10px"></div>
- fxLayoutAlign نحوه‌ی چیدمان المان را تعیین می‌کند.
<div fxLayoutAlign="start stretch"></div>

چند مثال:


و یا حالت راست به چپ آن به صورت زیر است:


و برای تعریف آیتم‌های قرار گرفته‌ی درون containers می‌توان از دایرکتیوهای زیر استفاده کرد:
- fxflex برای تعیین اندازه و flex المان‌ها
<div fxFlex="1 2 calc(15em + 20px)"></div>
در اینجا سه مقداری که ذکر می‌شوند (و یا تنها یک مقدار) چنین معنایی را به همراه دارند:
 fxFlex="grow shrink basis"
و یا
 fxFlex="basis"
- grow به این معنا است که آیتم جاری در صورت وجود فضا (طراحی واکنشگرا و واکنش نشان دادن به اندازه‌ی صفحه)، نسبت به سایر المان‌ها تا چه اندازه‌ای می‌تواند بزرگ شود.
- shrink به این معنا است که اگر به اندازه‌ی کافی فضا وجود نداشت، این المان نسبت به سایر المان‌های دیگر تا چه اندازه‌ای می‌تواند کوچک شود.
- basis به معنای اندازه‌ی پیش‌فرض المان است.

در اینجا اندازه‌ها برحسب پیکسل، درصد و یا calcs, em, cw, vh می‌توانند تعیین شوند. همچنین یک سری نام مستعار مانند grow, initial, auto, none, nogrow, noshrink هم قابل استفاده هستند.

- fxflexorder برای تعیین ترتیب قرارگیری یک المان
<div fxFlexOrder="2"></div>
-  fxflexoffset برای تعیین فاصله یک المان از container آن
 <div fxFlexOffset="20px"></div>
-  fxflexAlign برای تعیین محل قرارگیری المان
 <div fxFlexAlign="center"></div>
- fxflexfill برای تعیین اینکه این المان کل ردیف یا ستون را پر خواهد کرد
 <div fxFlexFill></div>

چند مثال:


در اینجا سه نمایشی را که در ذیل تعریف div‌ها مشاهده می‌کنید بر اساس تغییر اندازه‌ی صفحه حاصل شده‌اند. چون آیتم دوم دارای مقدار grow مساوی 5 است، به همین جهت با تغییر اندازه‌ی صفحه و دسترسی به مقدار فضای بیشتر، بزرگ‌تر شده‌است.

یک مثال کامل
اگر علاقمند باشید تا توانمندی‌های angular flex layout را در قالب یک مثال کامل مشاهده کنید، به آدرس زیر مراجعه نمائید:
https://tburleson-layouts-demos.firebaseapp.com/#/docs
در این مثال با تغییر گزینه‌‌های مختلف، کد معادل angular flex layout آن نیز تولید می‌شود.
همچنین wiki خود پروژه نیز به همراه مثال‌های بیشتری است:
https://github.com/angular/flex-layout/wiki



کار با API واکنشگرای Angular Flex layout


در طراحی واکنشگرا، container و عناصر داخل آن بر اساس تغییرات اندازه‌ی صفحه و یا اندازه‌ی وسیله‌ی نمایشی، تغییر اندازه و همچنین موقعیت می‌دهند و این تغییرات بر اساس انطباق با viewport وسیله‌ی نمایشی صورت می‌گیرند. به همین جهت برای طراحی واکنشگرا نیاز به Flex CSS و همچنین Media Query است. نوشتن Medial Query و ترکیب آن با Flex CSS کار مشکلی است. به همین جهت Angular Flex layout به همراه یک API واکنشگرا نیز هست که در پشت صحنه Flex CSS را بر اساس طراحی متریال و Medial Queries مورد استفاده قرار می‌دهد.
اگر علاقمند هستید تا اندازه‌های واکنشگرای استاندارد متریال را ملاحظه کنید، می‌توانید به آدرس زیر مراجعه نمائید (قسمت Breakpoint system آن):
https://material.io/design/layout/responsive-layout-grid.html#breakpoints
برای مثال هر اندازه‌ای کمتر از 600px در گروه extra small قرار می‌گیرد (با مخفف xs). از 600px تا 1024px در بازه‌ی small (با مخفف sm)، از 1024px تا 1440px در بازه‌ی medium (با مخفف md) و از 1440px تا 1920px در بازه‌ی large (با مخفف lg) و بیشتر از آن در بازه‌ی xlrage قرار می‌گیرند (با مخفف xl). این اعداد و بازه‌ها، پایه‌ی طراحی API واکنشگرای Angular Flex layout هستند. به همین جهت نام این بازه‌ها در این API به صورت مخفف xs, sm, md, lg, xl درنظر گرفته شده‌اند و مورد استفاده قرار می‌گیرند. همچنین اگر اندازه‌های مدنظر از این بازه‌ها کمتر باشند، می‌توان از lt-sm, lt-md, lt-lg, lt-xl نیز استفاده کرد. در اینجا lt به معنای less than است و یا اگر بازه‌های مورد نیاز بیش از این اندازه‌ها باشند می‌توان با gt-xs, gt-sm, gt-md, gt-lg کار کرد. در اینجا gt به معنای greater than است.
به این مخفف‌ها «media query alias» هم گفته می‌شود و اکنون که لیست آن‌ها مشخص است، تنها کافی است آن‌ها را به API استاتیکی که پیشتر بررسی کردیم، اضافه کنیم. برای مثال:
fxLayout.sm = "..."
fxLayoutAlign.md = "..."
fxHide.gt-sm = "..."
برای نمونه فرض کنید یک چنین طرحبندی دسکتاپی را داریم:


معادل طراحی آن با API استاتیک Angular Flex Layout به صورت زیر است:

که در اینجا دو container را ملاحظه می‌کنید. ابتدا Container بیرونی جهت ارائه‌ی ستونی از سه المان اضافه شده‌است. سپس یک Container میانی برای  تعریف ردیفی از سه المان تعریف شده‌است. توسط روش "fxFlex="grow shrink basis نیز اندازه‌های آن‌ها مشخص شده‌اند.

اکنون که این طرحبندی دسکتاپ را داریم، چگونه باید آن‌را تبدیل به طرحبندی موبایل، مانند شکل زیر کنیم؟


برای اینکار ابتدا fxLayout.xs را به سطر میانی اضافه می‌کنیم تا هرگاه به این اندازه رسیدیم، بجای ردیف، تبدیل به ستون شود. سپس توسط fxFlexOrder.xs، در اندازه‌ی xs، محل قرارگیری المان‌های این ستون را هم مشخص می‌کنیم:


همانطور که ملاحظه می‌کنید کار کردن با این API بسیار ساده‌است و نیازی به کارکردن مستقیم با Media Queries و یا برنامه نویسی مستقیم ندارد و تمام آن در قالب HTML یک کامپوننت قابل پیاده سازی است.
یک نکته: مثال کاملی که پیشتر در این بحث مطرح شد، به همراه مثال واکنشگرا نیز هست که برای مشاهده‌ی اثر آن‌ها بهتر است اندازه‌ی مرورگر را کوچک و بزرگ کنید.


مخفی کردن و یا نمایش قسمتی از صفحه بر اساس اندازه‌ی آن

علاوه بر media query alias هایی که عنوان شد، امکان نمایش و یا مخفی سازی قسمت‌های مختلف صفحه بر اساس اندازه‌ی صفحه‌ی نمایشی نیز هست:
 <div fxShow fxHide.xs="false" fxHide.lg="true"></div>
در اینجا fxShow سبب نمایش این div در حالت عادی می‌شود (پیش‌فرض آن xl، md و sm است). اما اگر اندازه‌ی صفحه lg باشد، fxHide.lg تنظیم شده‌ی به true سبب مخفی سازی آن خواهد شد و در اندازه‌ی xs مجددا نمایش داده می‌شود.


تغییر اندازه‌ی قسمتی از صفحه بر اساس اندازه‌ی آن

در مثال زیر اگر اندازه‌ی صفحه gt-sm باشد (بیشتر از small)، اندازه‌ی این div به 100 درصد بجای 50 درصد حالت‌های دیگر، تنظیم می‌شود:
 <div fxFlex="50%" fxFlex.gt-sm="100%"></div>

حالت‌های ویژه‌ی طراحی واکنشگرا در Angular Flex Layout

در API واکنشگرای آن حالت‌های ویژه‌ی fxshow, fxhide, ngclass  و  ngstyle نیز درنظر گرفته شده‌اند که امکان فعالسازی آن‌ها در اندازه‌های مختلف صفحه مسیر است:
<div fxShow [fxShow.xs]="isVisibleOnMobile()"></div>
<div fxHide [fxHide.gt-sm]="isVisibleOnDesktop()"></div>
<div [ngClass.sm]="{'fxClass-sm': hasStyle}" ></div>
<div [ngStyle.xs]="{color: 'blue'}"></div>


امکان کار با API واکنشگرا از طریق برنامه نویسی

برای این منظور می‌توان از سرویس ObservableMedia مانند مثال زیر استفاده کرد:


در اینجا به فعالسازی یک بازه‌ی خاص گوش فرا خواهیم داد. برای مثال اگر اندازه‌ی صفحه xs بود، سبب بارگذاری محتوای خاص مرتبط با موبایل خواهیم شد.



برای مطالعه‌ی بیشتر
قسمت‌های عمده‌ای از مطلب جاری، از ویدیوی زیر که توسط نویسنده‌ی اصلی angular flex layout تهیه شده‌است، گردآوری شدند.
 
مطالب
نرمال سازی (قسمت اول: First Normal Form)
مقدمه
نرمالسازی یا normalization باعث جلوگیری از تکرار و افزونگی اطلاعات می‌شود. و همچنین مانع از یکسری ناهنجاری‌ها در عملیات درج، بروز رسانی، حذف و انتخاب خواهد شد.
شکل‌های نرمال متعددی تعریف شده اند که به شرح زیر است:
  • شکل نرمال اول (1NF)
  • شکل نرمال دوم (2ND)
  • شکل نرمال سوم (3NF)
  • شکل نرمال بویس کاد (BCNF)
  • شکل نرمال چهارم (4NF)
  • شکل نرمال پنجم (5NF)
سه شکل اول نرمال یعنی 1NF، 2NF و 3NF توسط دکتر Codd تعریف شده اند. شکل نرمال بویس کاد نیز که یک تعریف اصلاح شده و قوی‌تر از 3NF به Boyce و Codd منسوب است. بعد از آن Fagin شکل چهارم نرمال(4NF) را تعریف کرد (چرا که در آن زمان BCNF شکل سوم نرمال خوانده می‌شد). 

تصویر فوق می‌گوید اگر جدولی در شکل سوم نرمال باشد حتما دارای شکل دوم نرمال و شکل اول نرمال هم خواهد بود.

شکل اول نرمال (First Normal Form)
تعریف رسمی:
یک متغیر رابطه ای به شکل اول نرمال است اگر و فقط اگر در هر مقدار مجاز آن متغیر رابطه ای، هر چندتایی فقط یک مقدار برای هر خصیصه داشته باشد.
 
منظور از اصطلاحات متغیر رابطه ای، چندتایی و خصیصه به طور غیر رسمی به ترتیب برابر است با جدول، سطر و ستون.
قسمت کلیدی تعریف، این جمله است:  "فقط یک مقدار برای هر خصیصه داشته باشد"
به دو جدول زیر توجه کنید، این جداول به شکل اول نرمال نمی‌باشد چرا که به ازای هر مشتری برای خصیصه شماره تلفن چند مقدار خواهیم داشت:


در جدول اول ستون شماره تلفن چند بار تکرار شده است. یعنی برای یک مشتری چند مقدار برای خصیصه شماره تلفن خواهیم داشت که این مغایر با تعریف شکل اول نرمال است. همین اتفاق نیز در جدول دوم افتاده است با این فرق که مقادیر خصیصه شماره تلفن در یک ستون درج شده اند.

برای تبدیل جدول غیر نرمال فوق به یک جدول نرمال اول، بایستی کاری کنیم که خصیصه شماره تلفن فقط یک مقدار را بگیرد. یعنی:در جدول فوق می‌بینید که برای خصیصه شماره تلفن به ازای هر سطر فقط یک مقدار داریم.


در جدول غیر نرمال مثال پیشین چند مقدار برای یک خصیصه داشتیم. حال به مثالی می‌پردازیم که یک مجموعه از خصیصه‌ها چند بار تکرار می‌شوند.
به جدول غیر نرمال زیر توجه کنید. دو خصیصه ترم و معدل چند بار در جدول تکرار می‌شوند. اصللاحا به این‌ها گروه‌های تکرار شونده می‌گویند.

گروه‌های تکرار شونده را با آکولاد ({}) مشکل کرده ام. این گونه جداول (که حتی در شکل نرمال اول هم قرار ندارند) مشکلات فراوانی دارند که در زیر به مواردی اشاره خواهیم داشت:
  • چگونه معدل ترم 5 را در جدول درج کنیم؟ پس برای اینکه بتوانیم تمام معدل‌ها را در جدول داشته باشیم باید به تعداد حداکثر ترم تحصیلی گروه‌های تکرار شونده در جدول داشته باشیم.
  • برای دانشجویی که فقط یک ترم تحصیل کرده است تمام گروه‌های تکرار شونده به غیر از یکی خالی خواهد ماند. فضای بسیاری به هدر خواهد رفت.
  • گزارش گیری بسیار سخت خواهد شد. بطور نمونه، چطور می‌خواهید بالاتری معدل دانشجویان را بدست بیاورین؟

پس با تبدیل جدول غیر نرمال به شکل نرمال اول، به مشکلات فوق غلبه خواهیم کرد:

اما یک متغیر رابطه ای که فقط به صورت شکل اول نرمال است ساختاری دارد که به دلایل متعدد، نامطلوب است.

در جدول فوق اطلاعاتی وجود دارد که به دفعات تکرار شده است. مثلا نام دانشجو به تعداد ترم‌ها تکرار شده است. در صورتی که باید نام دانشجو یکبار ذخیره شده باشد. پس یک جدولی که به فرم نرمال اول هست می‌تواند افزونگی اطلاعات داشته باشد.

در بخش بعدی ابتدا وابستگی تابعی مورد بررسی قرار خواهد گرفت سپس به فرم دوم نرمال پرداخته خواهد شد.