تکنولوژی معرفی سایت

همه چیز در مورد Unicode – UTF8 و مجموعه‌های کاراکتری

همه چیز در مورد Unicode – UTF8 و مجموعه‌های کاراکتری

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

این داستان تعدادی از آن کاراکترها را از نزدیک دنبال خواهد کرد هنگامی که آن‌ها از وب سرور به مرورگر و در جهت مخالف در حرکت هستند. در طول مسیر شما بیشتر درباره تاریخچه کاراکتر، مجموعه‌های کاراکتری، Unicode و UTF8 اطلاعات پیدا خواهید کرد، و اینکه چرا علامت‌های سوال و کاراکترهای استرس‌دار (accented: علامت‌ تکیه‌‌ی‌ صدا [بدین‌ شکل‌']) عجیب و غریب گاهی در پایگاه داده‌ها و فایل‌های متنی ظاهر می‌شوند.

 

اَسکی (ASCII)

کامپیوترها تنها با اعداد سروکار دارند (۰ و ۱) نه با حروف، بنابراین مهم است که همه کامپیوترها بر سر آنکه چه اعدادی نمایش دهنده چه حروفی باشند به توافق برسند.

فرض کنیم کامپیوتر من عدد ۱ را برای A، عدد ۲ را برای B، عدد ۳ را برای C و … و کامپیوتر شما عدد ۰ را برای A، عدد ۱ را برای B و … استفاده کنند. اگر من پیغام HELLO را برای شما ارسال کنم اعداد ۸، ۵، ۱۲، ۱۲ و ۱۵ برای شما ارسال می‌شوند اما در کامپیوتر شما ۸ به معنای I است پس وقتی کامپیوتر شما پیغام را دریافت می‌کند آن را به صورت IFMMP نشان می‌دهد. برای برقراری ارتباط موثر، ما نیاز داریم تا بر سر یک روش استاندارد برای رمزگذاری (encoding) کاراکترها به توافق برسیم.

برای این منظور، در سال ۱۹۶۰ انجمن استانداردهای آمریکا یک رمزگذاری ۷ بیتی که American Standard Code for Information Interchange (ASCII) نامیده می‌شود، ایجاد کرد. در این روش رمزگذاری HELLO اعداد ۷۲, ۶۹, ۷۶, ۷۶, ۷۹ است و به صورت دیجیتالی بدین گونه ۱۰۰۱۰۰۰ ۱۰۰۰۱۰۱ ۱۰۰۱۱۰۰ ۱۰۰۱۱۰۰ ۱۰۰۱۱۱۱ منتقل می‌شوند. (یک روش خیلی ساده برای تبدیل اعداد از مبنای ۱۰ به مبنای ۲ استفاده از ماشین حساب ویندوز است. ماشین حساب ویندوز را اجرا کرده و منوی View گزینه Scientific را علامت بزنید تا گزینه‌هایی به ماشین حساب اضافه شود. حال عدد مورد نظر خود در مبنای ۱۰ را وارد کنید و گزینه Bin‌ را که به معنای باینری است انتخاب کنید، عدد به صورت باینری به نمایش درمی‌آید.)

با استفاده از ۷ بیت، ۱۲۸ مقدار ممکن از ۰۰۰۰۰۰۰ تا ۱۱۱۱۱۱۱ خواهد بود بنابراین ASCII دارای فضای کافی برای همه حروف کوچک و بزرگ لاتین همراه با همه رقم‌ها، علائم نقطه گذاری مشترک، فاصله‌ها (spaces)، تب‌ها (tabs) و کاراکترهای کنترلی دیگر است. در سال ۱۹۶۸ Lyndon B. Johnson رییس جمهور ایلات متحده آن را رسمی کرد – تمام کامپیوترها باید ASCII را استفاده و درک کنند.

خودتان آن را امتحان کنید


بسیاری از جدول‌های اَسکی موجود ۱۲۸ کاراکتر را نمایش می‌دهند یا توصیف می‌کنند. شما هم می‌توانید یک جدول اَسکی را آنگونه که مایلید با کمی CSS، HTML و جاوا اسکریپت بسازید. کد زیر این کار را انجام می‌دهد:

1
2
3
4
5
6
7
8
9
10
11
<html>
<body>
<style type="text/css">
p {float: left; padding: 0 15px; margin: 0; font-size: 80%;}
</style>
<script type="text/javascript">
for (var i=0; i<128; i++)
document.writeln ((i%32?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>');
</script>
</body>
</html>

 

 

 

 

 

این کد جدولی شبیه این را نشان می‌دهد:

جدول اَسکی تولید شده توسط جاوا اسکریپت در فایرفاکس

قسمت مهم این کد تابع String.fromCharCode است. این تابع یک عدد می‌گیرید و آن را به یک کاراکتر تبدیل می‌کند. در واقع چهار خط HTML و JavaScript که در ادامه می‌آیند همگی نتیجه یکسانی را تولید می‌کنند:

1
2
3
4
HELLO
& #72;& #69;& #76;& #76;& #79;
<script>document.write ("HELLO");</script>
<script>document.write (String.fromCharCode (72,69,76,76,79));</script>

 

 

 

 

 

در خط دوم باید کاراکتر & چسبیده به # باشد اما چون اگر چسبیده می نوشتم وردپرس متن را تبدیل به HELLO می کرد. آن را جدا نوشتم که کد تبدیل نشود و شما متوجه منظور خط دوم بشوید.

بیت هشتم

چاپگرهای راه دور (teleprinter) و بورس از فرستادن ۷ بیت از اطلاعات به یکدیگر کاملاً راضی بودند اما ریز پردازنده‌های نوظهور در ۱۹۷۰ ترجیح می‌دادند تا با توان‌های ۲ کار کنند. آنها می‌توانستند در یک زمان ۸ بیت را پردازش کنند و ۸ بیت (یک بایت byte یا octed [گروه هشت تایی]) را برای ذخیره هر کاراکتر مورد استفاده قرار دهند که ۲۵۶ مقدار ممکن در دسترس خواهد بود.

یک کاراکتر ۸ بیتی می‌تواند یک عدد را تا ۲۵۵ ذخیره کند اما ASCII تنها تا ۱۲۷ را اختصاص می‌دهد. مقادیر دیگر از ۱۲۸ تا ۲۵۵ یدکی هستند. در ابتدا PCهای IBM از مقادیر یدکی برای نمایش حروف ویژه استرس‌دار (accented letters)، نمادها و اشکال مختلف و تعدادی از حروف یونانی استفاده می‌کرد. به عنوان مثال عدد ۲۰۰ گوشه پایین سمت چپ یک جعبه بود: ╚، و ۲۴۴ حرف آلفای یونانی به صورت کوچک بود: α. به این روش رمزگذاری حروف نام code page 437 داده شد.

با این حال بر خلاف ASCII کاراکترهای ۱۲۸ تا ۲۵۵ هرگز به صورت استاندارد درنیامدند و کشورهای مختلف مقادیر یدکی را با الفبای خودشان شروع می‌کردند. همه بر سر اینکه ۲۲۴ باید α را نشان دهد توافق نداشتند حتی یونانیان. این منجر به ایجاد تعدادی صفحات کد (code page) جدید شده است. به عنوان مثال در کامپیوترهای IBM روسیه با استفاده از code page 885، عدد ۲۲۴ حرف Я را نشان می‌دهد. در code page 737 یونانی نشان دهنده امگای کوچک: ω است.

حتی پس از آن نیز اختلاف نظر وجود داشت. از ۱۹۸۰ ویندوز مایکروسافت کد پیج‌های خاص خود را معرفی کرد. در کد پیج سیریلیک (Cyrillic) Windows-1251، عدد ۲۲۴ حرف a سیریلیک را نشان می‌دهد و Я، ۲۲۳ است.

در اواخر ۱۹۹۰ تلاشی در جهت استانداردسازی شکل گرفت. پانزده مجموعه کاراکتری ۸ بیتی مختلف برای پوشش دادن الفباهای گوناگون مانند سیریلیک، عربی، عبری، ترکی و تایلندی شکل گرفت. آنها ISO-8859-1 تا ISO-8859-16 نامیده شدند (برای شماره ۱۲ هیچ مجموعه کاراکتری قرار داده نشده). در ISO-8859-5 سیریلیک، ۲۲۴ حرف p و ۲۰۷، Я را نشان می‌دهد.

بنابراین اگر یک دوست روسی سندی را برای شما بفرستد، شما در واقع نیاز دارید که بدانید از چه کد پیجی استفاده شده است. سند به خودی خود یک توالی از اعداد است. کاراکتر ۲۲۴ می‌تواند Я، a یا p باشد. سند با کد پیج اشتباه، شبیه یک دسته از حروف و علائم بهم ریخته دیده می‌شود.

خودتان آن را امتحان کنید


کد پیج‌ها به عنوان مجموعه‌های کاراکتری (character sets) شناخته می‌شوند. می‌توانید خودتان این مجموعه‌های کاراکتری را امتحان کنید اما باید از PHP یا یک زبان سمت سرور شبیه آن استفاده کنید (بطور کلی به این دلیل که کاراکتر نیاز دارد قبل از اینکه به مرورگر برسد در صفحه باشد). این خطوط را در یک فایل PHP ذخیره کرده و آن را به سرورتان آپلود کنید:

1
2
3
4
5
6
7
8
9
<html>
<head>
<meta charset="ISO-8859-5">
</head>
<body>
<style type="text/css">p {float: left; padding: 0 15px; margin: 0; font-size: 80%;}</style>
<?php  for ($i=0; $i<256; $i++) echo ($i%32?'':'<p>') . $i . ': ' . chr ($i) . '<br>'; ?>
</body>
</html>

 

 

 

 

 

کد بالا جدولی شبیه این را نمایش می‌دهد:

مجموعه کاراکتری سیریلیک ISO-8858-5 که در فایرفاکس به نمایش در آمده

تابع chr در PHP کاری شبیه به همان تابع String.fromCharCode را در جاوا اسکریپت انجام می‌دهد. برای مثال chr(224) عدد ۲۲۴ را قبل از ارسال آن به مرورگر در صفحه وب، قرار می‌دهد. چنانکه قبلاً دیدیم ۲۲۴ می‌تواند به معنای چیزهای مختلفی باشد. بنابراین مرورگر نیاز دارد که بداند از کدام مجموعه کاراکتری برای نمایش ۲۲۴ استفاده کند. این چیزی است که در خط سوم کد بالا مشخص شده است. این خط به مرورگر می‌گوید که از مجموعه کاراکتری سیریلیک ISO-8858-5 استفاده کند:

1
<meta charset="ISO-8859-5">

 

 

 

 

 

اگر خط مربوط به مشخص کردن charset را قرار ندهید، صفحه با استفاده از پیش فرض مرورگر به نمایش درمی‌آید. در کشورهای با الفبای مبتنی بر لاتین (مانند انگلستان و ایالات متحده) احتمالاً پیش فرض ISO-8859-1 است که در این صورت ۲۲۴: à است.

این وضعیت در حدود ۱۹۹۰ است. اسناد در بسیاری از زبان‌ها می‌توانند نوشته شده، ذخیره یا رد و بدل شوند اما باید بدانید که از کدام مجموعه کاراکتری در آنها استفاده شده است. همچنین هیچ راه ساده‌ای برای استفاده دو یا بیشتر از دو الفبای غیر انگلیسی در همان سند وجود ندارد، و الفباهایی با بیشتر از ۲۵۶ کاراکتر مثل چینی و ژاپنی مجبور به استفاده از سیستم‌های کاملاً متفاوتی هستند.

در نهایت اینترنت در حال ظهور است. بین المللی و جهانی شدن، این مسأله را بسیار بزرگتر می‌نماید. یک استاندارد جدید لازم است.

یونیکد برای نجات

در اواخر ۱۹۸۰ یک استاندارد جدید پیشنهاد شد که یک شماره منحصر به فرد (که به طور رسمی به عنوان code point شناخته می‌شد) را به هر حرف در هر زبان اختصاص می‌داد. در این روش بیشتر از ۲۵۶ مقدار مورد نیاز بود. این روش Unicode (یونیکد) نامیده شد. ورژن یونیکد هم اکنون ۶٫۱ و شامل بیش ۱۱۰۰۰۰ کد پوینت است. در صورت تمایل می‌توانید همه آنها را مشاهده کنید.

۱۲۸ کد پوینت ابتدایی یونیکد همانند اَسکی هستند. محدوده ۱۲۸ تا ۲۵۵ شامل نمادهای ارز و سایر نمادهای معمول و کاراکترهای استرس‌دار (به عنوان کاراکترهای با علائم تشخیصی برای تلفظ [diacritical marks] شناخته می‌شوند) هستند، و بیشتر آن از ISO-8859-1 گرفته شده‌اند. بعد از ۲۵۶، کاراکترهای استرس‌دار بسیار بیشتری قرار دارند. پس از ۸۸۰ به حروف یونانی می‌رسد، سپس سیریلیک، عبری، عربی، هندی و تایلندی. چینی، ژاپنی و کره‌ای از ۱۱۹۰۴ آغاز می‌شوند و بسیاری دیگر در این بین قرار دارند.

این مهم است که هر حرف به وسیله شماره منحصر به فرد خود نمایش داده می‌شود. حرف Я سیریلیکی همیشه ۱۰۷۱ و حرف α یونانی همیشه ۹۴۵ است. à همیشه ۲۲۴ و H 72 است. توجه داشته باشید که این کد پوینت‌های یونیکد به طور رسمی به صورت هگزادسیمال با U+ در ابتدای آنها نوشته می‌شوند. بنابراین کد پوینت یونیکد H به جای ۷۲ معمولاً به صورت U+0048 نوشته می‌شود ( برای تبدیل هگزادسیمال [مبنای شانزده] به دسیمال [مبنای ده] : ۷۲ = ۴ * ۱۶ + ۸).

مشکل اصلی این است که بیشتر از ۲۵۶ تا از آنها وجود دارد. کاراکترهای بعد از ۲۵۶ در ۸ بیت جا نخواهند شد. هر چند یونیکد، مجموعه کاراکتری یا کد پیج نیست. بنابراین این مشکل کنسرسیوم یونیکد نیست. آنها فقط ایده را مطرح کردند و اجازه دادند تا هر کسی پیاده سازی خود را انجام دهد.

یونیکد در داخل مرورگر

یونیکد در داخل ۸ بیت یا حتی ۱۶ بیت جا نمی‌شود. اگرچه تنها ۱۱۰۱۱۶ کد پوینت استفاده شده، قابلیت آن را دارد تا ۱۱۱۴۱۱۲ از آنها تعریف شود که ۲۱ بیت نیاز دارد.

بهرحال کامپیوترها از سال ۱۹۷۰ پیشرفت کرده بودند و یک ریزپردازنده ۸ بیتی قدیمی شده بود. کامپیوترهای جدید اکنون پردازنده‌های ۶۴ بیتی دارند بنابراین چرا ما نتوانیم از یک کاراکتر ۸ بیتی به سمت یک کاراکتر ۳۲ یا ۶۴ بیت برویم؟

نخستین جواب این است : ما می‌توانیم!

نرم‌افزارهای زیادی که با C یا ++C نوشته شده‌اند از کاراکتر عریض (wide character) پشتیبانی می‌کنند. این یک کاراکتر ۳۲ بیتی است که wchar_t نامیده شده است. این یک بسطی از گونه char، ۸ بیتی C است. مرورگرهای وب مدرن از کاراکترهای عریض (یا چیزی شبیه به آن) به صورت داخلی استفاده می‌کنند و از لحاظ تئوری می‌توانند با ۴ بیلیون کاراکتر متمایز سروکار داشته باشند. این مقدار برای یونیکد کفایت می‌کند بنابراین مرورگرهای وب مدرن به صورت داخلی از یونیکد استفاده می‌کنند.

خودتان آن را امتحان کنید


کد جاوا اسکریپت زیر شبیه کد اَسکی بالا است بجز اینکه کد زیر تا مقادیر بالاتری ادامه پیدا می‌کند. برای هر عدد، کد زیر به مرورگر می‌گوید تا کد پوینت یونیکد متناظر با آن را نمایش دهد:

1
2
3
4
5
6
7
8
9
<html>
<body>
<style type="text/css">p {float: left; padding: 0 15px; margin: 0; font-size: 80%;}</style>
<script type="text/javascript">
for (var i=0; I < 2096; i++)
  document.writeln ((i%256?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>');
</script>
</body>
</html>

 

 

 

 

 

نتیجه آن جدولی شبیه زیر است:

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

تصویر بالا تنها زیر مجموعه‌ای از چند هزار کد پوینت خروجی بوسیله جاوا اسکریپت می‌باشد. نکته مهم این است که جاوا اسکریپت کاملاً در مرورگر وب اجرا می‌شود جایی که کاراکترهای ۳۲ بیتی بطور کامل قابل پذیرش است. تابع جاوا اسکریپت String.fromCharCode(1071) کد پوینت یونیکد ۱۰۷۱ را که Я است در خروجی نشان می‌دهد.

به همین ترتیب اگر شما Я را در یک صفحه HTML قرار دهید، مرورگر وب مدرن Я را نشان می‌دهد.

از طرف دیگر، تابع پی اچ پی chr(1071) یک اسلش “/” را نشان می‌دهد به خاطر اینکه تابع chr تنها با مقادیر ۸ بیتی (تا مقدار ۲۵۶) سروکار دارد و پس از آن خود را تکرار می‌کند.۴۷ = ۱۰۷۱ % ۲۵۶ که از سال ۱۹۶۰ یک “/” بوده است.

UTF-8 برای نجات

اگر مرورگرها می‌توانند با کاراکترهای ۳۲ بیتی یونیکد کار کنند پس مشکل کجاست؟ مشکل در ارسال و دریافت و خواندن و نوشتن کاراکترهاست.

مشکل باقیست چون:

  • بسیاری از نرم‌افزارها و پروتکل‌های ارسال/دریافت و خواندن/نوشتن با کاراکترهای ۸ بیتی کار می‌کنند
  • با استفاده از ۳۲ بیت برای ارسال/ذخیره متن انگلیسی مقدار پهنای باند/فضای موردنیاز چهار برابر خواهد شد

هر چند مرورگرها می‌توانند بصورت داخلی با یونیکد کار کنند، هنوز باید برای مرورگر وب داده را از وب سرور بگیرید و دوباره برگردانید، و نیاز دارید تا آن را در یک فایل یا جایی در یک پایگاه داده ذخیره کنید. بنابراین هنوز به راهی نیاز دارید که ۱۱۰۰۰۰ کد پوینت یونیکد را تنها در ۸ بیت جا دهید.

تلاش‌های مختلفی برای حل این مسأله انجام گرفته مثل UCS2 و UTF-16. اما در سال‌های اخیر UTF-8 برنده بوده است که مخفف فرمت ۸ بیتی انتقال مجموعه کاراکتری جهانی (Universal Character Set Transformation Format 8 bit) است.

UTF-8 باهوش است و نسبتاً شبیه کلید Shift روی کیبورد کار می‌کند. به طور معمول هنگامی که شما H را از کیبورد می‌فشارید حرف کوچک h روی صفحه ظاهر می‌شود. اما اگر شما ابتدا Shift را فشار داده باشید، حرف بزرگ H ظاهر می‌شود.

UTF-8 با اعداد ۰ تا ۱۲۷ همانند اَسکی، ۱۹۲ تا ۲۴۷ به عنوان کلیدهای Shift و ۱۲۸ تا ۱۹۲ به عنوان کلیدهایی که با شیفت استفاده می‌شوند، رفتار می‌کند. برای مثال کاراکترهای ۲۰۸ و ۲۰۹ شما را به محدوده سیریلیک منتقل می‌کنند (شیفت می‌دهند). ۲۰۸ همراه با ۱۷۵ کاراکتر ۱۰۷۱ است که Я سیریلیک می‌شود. محاسبه دقیق آن به این صورت است: ۱۰۷۱ = (۶۴ % ۱۷۵) + ۶۴ * (۳۲ % ۲۰۸). کاراکترهای ۲۲۴ تا ۲۳۹ شبیه شیفت مضاعف (double shift) هستند. ۲۲۶ به دنبال آن ۱۹۰ و سپس ۱۲۸ کاراکتر ۱۲۱۶۰: ⾀ است. ۲۴۰ به بالا شیفت سه گانه (triple shift) است.

بنابراین UTF-8 یک رمزگذاری با عرض متغیر (variable-width) چند بایتی (multi-byte) است. چند بایتی به خاطر اینکه یک کاراکتر واحد شبیه Я بیشتر از یک بایت را برای مشخص شدن می‌گیرد. عرض متغیر به این دلیل که برخی از کاراکترها مثل H تنها یک بایت را می‌گیرند و برخی تا چهار بایت را اشغال می‌کنند.

بهتر از همه این است که با ASCII نیز سازگار است. بر خلاف برخی دیگر از راه حل‌های پیشنهادی، هر سندی که تنها در ASCII با کاراکترهای ۰ تا ۱۲۷ نوشته شده کاملاً در UTF-8 معتبر است و همچنین موجب صرفه جویی در پهنای باند می‌شود.

خودتان آن را امتحان کنید


این یک تجربه متفاوت است. PHP شش عدد ۷۲، ۲۰۸، ۱۷۵، ۲۲۶، ۱۹۰ و ۱۲۸ را درون یک صفحه HTML قرار می‌دهد. مرورگر آن اعداد را به عنوان UTF-8 تفسیر می‌کند و به صورت داخلی آنها را به کد پوینت‌های یونیکد تبدیل می‌کند. سپس جاوا اسکریپت مقادیر یونیکد را به خروجی می‌فرستد. سعی کنید charset را از UTF-8 به ISO-8859-1 تغییر دهید و ببینید چه اتفاقی می‌افتد:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Characters embedded in the page:<br>
<span id="chars"><?php echo chr(72).chr(208).chr(175).chr(226).chr(190).chr(128); ?></span>
<p>Character values according to Javascript:<br>
<script type="text/javascript">
function ShowCharacters (s) {
  var r='';
  for (var i=0; i<s.length; i++)
    r += s.charCodeAt (i) + ': ' + s.substr (i, 1) + '<br>';
  return r;
}
document.writeln (ShowCharacters (document.getElementById('chars').innerHTML));
</script>
</body>
</html>

 

 

 

 

 

چیزی که مشاهده می‌کنید به این شکل است:

مجموعه کاراکتری UTF-8

مجموعه کاراکتری ISO-8859-1

اگر صفحه را با استفاده از مجموعه کاراکتری UTF-8 مشاهده کنید، تنها سه کاراکتر: HЯ⾀ را خواهید دید. اگر آن را با مجموعه کاراکتری ISO-8859-1 مشاهده کنید شش کاراکتر جداگانه: €¾HЯâ را خواهید دید. این چیزی است که اتفاق می‌افتد:

  • در وب سرور، PHP اعداد ۷۲، ۲۰۸، ۱۷۵، ۲۲۶، ۱۹۰ و ۱۲۸ را درون صفحه وب قرار می‌دهد.
  • صفحه وب از طریق اینترنت از وب سرور به مرورگر شما می‌رسد.
  • مرورگر آن اعداد را دریافت و آن را بر اساس مجموعه کاراکتری تفسیر می‌کند.
  • مرورگر به صورت داخلی کاراکترها را با استفاده از مقادیر یونیکد نمایش می‌دهد.
  • جاوا اسکریپت مقادیر یونیکد را به مقادیر مجموعه کاراکتری یاد شده متناظر می‌کند و آنها را به خروجی می‌فرستد.

توجه داشته باشید زمانی که پنج عدد ۷۲، ۲۰۸، ۱۷۵، ۲۲۶، ۱۹۰ به عنوان ISO-8859-1 مشاهده شوند با کد پوینت‌های یونیکدشان یکسان هستند. این بدین خاطر است که در این محدوده یونیکد به شدت از ISO-8859-1 اقتباس کرده است. بهر حال آخرین عدد که سمبل € است متفاوت است. این سمبل در ISO-8859-1 در مکان ۱۲۸ است و در یونیکد مقدار ۸۳۶۴ را دارد.

خلاصه‌ای از حدود سال‌های ۲۰۰۳

UTF-8 در حال تبدیل شدن به محبوب‌ترین مجموعه کاراکتری بین المللی بر روی اینترنت و جانشینی مجموعه‌های تک بایتی (single-byte) مانند ISO-8859-5 است. هنگامی که شما یک سند غیر انگلیسی زبان را مشاهده یا ارسال می‌کنید، هنوز هم نیاز به دانستن این داریدکه آن سند از چه مجموعه کاراکتری استفاده می‌کند. برای بیشترین قابلیت همکاری، مدیران وب سایت‌ها نیاز دارند تا مطمئن شوند که همه صفحات وبشان از مجموعه‌های کاراکتری UTF-8 استفاده می‌کنند.

شاید Ð آشنا به نظر برسد. این کاراکتر گاهی نمایش داده می‌شود اگر شما سعی کنید تا سندهای UTF-8 زبان روسی را مشاهده کنید. بخش بعدی شرح می‌دهد که چگونه مجموعه‌های کاراکتری با هم اشتباه گرفته می‌شوند و در نهایت همه چیز را به اشتباه در یک پایگاه داده ذخیره می‌کنند.

بسیاری از مشکلات

تا زمانی که همه در حال صحبت کردن درباره UTF-8 هستند، همه باید بدون مشکل کار کنند. اگر اینگونه نباشد کاراکترها می‌توانند خرد شوند. برای توضیح این کار، یک نمونه‌ی تعاملیِ یک وب سایت را تصور کنید که همچون یک کاربر، نظری (comment) را در یک پست وبلاگ می‌گذارید.

  • صفحه وب یک فرم نظر خواهی را نمایش می‌دهد
  • کاربر یک نظر را تایپ می‌کند و دکمه submit را فشار می‌دهد
  • آن نظر به سرور فرستاده شده و در پایگاه داده ذخیره شده است
  • نظر بعداً از پایگاه داده بازیابی شده و در یک صفحه وب به نمایش در آمده است

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

HTML ENTITIES

برای لحظه‌ای وانمود کنید که چیزی در مورد مجموعه‌های کاراکتری نمی‌دانید. فرم در وبلاگتان احتمالاً خود را با استفاده از مجموعه کاراکتری ISO-8859-1 نشان می‌دهد. این مجموعه کاراکتری، هیچگونه روسی یا تایلندی یا چینی نمی‌شناسد و تنها مقدار کمی یونانی را می‌شناسد. اگر شما تلاش کنید تا چیزی را از فرم copy و paste کنید و دکمه submit را فشار دهید، یک مرورگر مدرن سعی خواهد کرد آن را به entityهای عددی HTML مثل Я برای کاراکتر Я تبدیل کند.

این چیزی است که در پایگاه داده شما ذخیره می‌شود و این چیزی است که هنگام نمایش یک نظر (comment) در خروجی ظاهر می‌شود. بدین معنی که آن نظر، در صفحه وب به درستی نمایش داده می‌شود اما زمانی که شما سعی کنید آن را به یک فایل PDF یا ایمیل بفرستید یا جستجوهای متنی را در پایگاه داده برای یافتن آن اجرا کنید، مشکلاتی رخ می‌دهد.

کاراکترهای درهم (confused characters)

چه خواهد شد اگر شما با یک وب سایت روسی کار کنید و یک مجموعه کاراکتری معین در صفحه وبتان نداشته باشید؟ یک کاربر روسی را که مجموعه کاراکتری پیش فرضش ISO-8859-5 است در نظر بگیرید. برای گفتن “hi” او ممکن است Привет را تایپ کند. وقتی کاربر دکمه Submit را فشار می‌دهد، کاراکترها بر اساس مجموعه کاراکتری صفحه فرستاده شده، کدگذاری (encode) می‌شوند. در این مورد، Привет به صورت اعداد ۱۹۱، ۲۲۴، ۲۱۶، ۲۱۰، ۲۱۳ و ۲۲۶ کدگذاری می‌شود. این اعداد از طریق اینترنت به سرور فرستاده شده و به همین صورت در پایگاه داده ذخیره می‌شوند.

اگر بعداً کسی آن نظر (comment) را با استفاده از ISO-8859-5 مشاهده کند، متن را به درستی خواهد دید. اما اگر آن را با مجموعه کاراکتری دیگری مثل Windows-1251 ببیند، їаШТХв را مشاهده خواهد کرد. این متن هنوز روسی است اما معنی درست را نمی‌رساند و هیچ حسی ایجاد نمی‌کند.

کاراکترهای استرس‌دار (accented: علامت‌ تکیه‌‌ی‌ صدا [بدین‌ شکل‌']) با تعداد زیادی از حروف صدادار (:vowel حروف a، e، i، o، u و گاهی y)

اگر شخصی همان کامنت را با استفاده از ISO-8859-1 مشاهده کند، ¿àØÒÕâ را به جای Привет خواهد دید. یک عبارت طولانی‌تر شبیه Я тоже рада Вас видеть (“nice to see you” به شکل رسمی خطاب به یک خانم) وقتی با ISO-8859-5 کدگذاری شود در مجموعه کاراکتری ISO-8859-1 اینگونه نشان داده می‌شود: Ï âÞÖÕ àÐÔÐ. این بدین علت است که محدوده ۱۲۸ تا ۲۵۵ در ISO-8859-1 شامل تعداد زیادی از حروف صدا دار با استرس‌ها (accent) هستند.

بنابراین اگر شما این نوع الگو را مشاهده می‌کنید، احتمالاً بدین خاطر است که متن با یکی از مجموعه‌های کاراکتری تک بایتی (single byte) (یکی از ISO-8859ها مثلاً ISO-8859-5 یا یکی از Windowsها مثلاً Windows-1251) وارد شده است و در حال حاضر با ISO-8859-1 به نمایش در آمده است. برای درست کردن متن احتیاج دارید که تعیین کنید که متن با کدام مجموعه کاراکتری وارد شده و به جای آن، متن را با UTF-8 وارد کنید.

کاراکترهای استرس‌دارِ تبدیل (alternating)

اگر کاربر کامنت را در UTF-8 ارائه کند چه اتفاقی می‌افتد؟ در آن صورت کاراکترهای سیریلیک که کلمه Привет را تشکیل می‌دهند هر کدام به صورت ۲ عدد فرستاده می‌شوند: ۲۰۸/۱۵۹، ۲۰۹/۱۲۸، ۲۰۸/۱۸۴، ۲۰۸/۱۷۸، ۲۰۸/۱۸۱ و ۲۰۹/۱۳۰٫ اگر آن را تحت ISO-8859-1 مشاهده کنید شبیه این دیده می‌شود: Привет.

به کاراکترهای Ð و Ñ توجه کنید. این کاراکترها شماره‌های ۲۰۸ و ۲۰۹ هستند و به UTF-8 می‌گویند تا به محدوده سیریلیک سوییچ کند. بنابراین اگر شما تعداد زیادی Ð و Ñ می‌بینید، می‌توانید فرض کنید که در حال نگاه کردن به یک متن روسی هستید که در UTF-8 وارد شده و با ISO-8859-1 دیده می‌شود. به طور مشابه، یونانی تعداد زیادی Î و Ï، ۲۰۶ و ۲۰۷ را خواهد داشت. و عبری، کاراکتر تبدیل × به شماره ۲۱۵ را دارد.

حروف صدا دار قبل از علامت پوند و کپی رایت

یک موضوع رایج در بریتانیا علامت پول پوند (£) است که به £ تبدیل می‌شود. این دقیقاً همان مساله‌ای است که در بالا پیش آمد. مقدار سمبل £ در یونیکد و ISO-8859-1، ۱۶۳ است. به یاد بیاورید که در UTF-8 هر کاراکتر بیشتر از ۱۲۷ به وسیله توالی دو یا بیشتر از اعداد نشان داده می‌شود. در این مورد، توالی UTF-8، ۱۹۴/۱۶۳ است. دلیل ریاضی آن این است که : ۱۶۳ = (۶۴ %۱۶۳) + ۶۴ * (۳۲ % ۱۹۴).

از لحاظ دیداری این بدین معنی است که اگر شما توالی UTF-8 را با استفاده از ISO-8859-1 مشاهده کنید، یک Â نمایان می‌شود که کاراکتر ۱۹۴ در ISO-8859-1 است. این همان چیزی است که برای همه کد پوینت‌های ۱۶۱ تا ۱۹۱ یونیکد اتفاق می‌افتد که شامل © و ® و ¥ می‌شود.

پس اگر در کنار کاراکترهای £ یا © ناگهان یک Â را ظاهر شد به خاطر این است که آن‌ها به عنوان UTF-8 وارد شده‌اند.

علامت‌های سوال داخل‌ لوزی‌های سیاه

اگر Привет را به عنوان ISO-8859-5 وارد کنید به عنوان اعدادی که در بالا ذکر شد ذخیره خواهد شد: ۱۹۱، ۲۲۴ و غیره. اگر بعداً سعی کنید این را تحت UTF-8 ببینید ممکن است تعدادی علامت سوال داخل لوزی‌های سیاه ببینید: �. مرورگر زمانی اینها را نشان می‌دهد که نمی‌تواند مفهوم اعدادی را که در حال خواندنشان است، دریابد.

UTF-8 خود-همگام‌ساز (self-synchronizing) است. بر خلاف دیگر رمزگذاری‌های کاراکتری چند بایتی، با UTF-8 همیشه می‌دانید که در چه موقعیتی قرار دارید. اگر یک عدد ۱۹۲-۲۴۷ را ببینید شما می‌فهمید در ابتدای یک توالی چند بایتی قرار دارید. اگر ۱۲۸-۱۹۱ را ببینید، می‌فهمید که در میانه یکی از آن‌ها قرار دارید.

هیچ خطری در از دست دادن عدد اول و تحریف (garbling) بقیه متن وجود ندارد. به این معنی که در UTF-8، توالی ۱۹۱ و پس از آن ۲۲۴ هرگز به طور طبیعی رخ نخواهد داد، بنابراین مرورگر نمی‌داند با این چه کند و در عوض �� را نشان می‌دهد.

این همچنین می‌تواند باعث مشکلات مربوط به £ و © شود. £۵۰در ISO-8859-1 اعداد ۱۶۳، ۵۳ و ۴۸ است. ۵۳ و ۴۸ هیچ مساله‌ای ایجاد نمی‌کنند اما در UTF-8، ۱۶۳ هرگز به خودی خود اتفاق نمی‌افتد بنابراین به شکل �۵۰ نشان داده می‌شود. بطور مشابه اگر �۲۰۱۲ را ببینید احتمالاً به خاطر این است که ©۲۰۱۲ به عنوان ISO-8859-1 وارد شده اما به عنوان UTF-8 نشان داده شده است.

جاهای خالی (blank)، علامت‌های سوال و جعبه‌ها

با وجود اینکه UTF-8 و یونیکد کاملاً سودمند و موثر هستند، یک مرورگر هنوز ممکن است نداند که چگونه یک کاراکتر را نمایش دهد. چند کاراکتر ابتدایی ASCII 1 تا ۳۱ بیشتر توالی‌هایی برای کنترل teleprinterها (چیزهایی مثل تایید و توقف) هستند. اگر سعی در نشان دادن آن‌ها کنید، یک مرورگر ممکن است یک ? یا یک جای خالی یا یک جعبه با اعداد ریز داخل آن را نشان دهد.

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

مرورگرهای قدیمی همچنین ممکن است بیشتر اوقات برای برخی موارد فوق نظیر نمایش ?، جاهای خالی و جعبه‌ها متفاوت رفتار کنند.

پایگاه داده‌ها

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

هنگام ذخیره و بازیابی داده، MySQL تنها با اعداد سروکار دارد. اگر به آن بگویید عدد ۱۶۳ را ذخیره کند، ذخیره خواهد شد. اگر ۲۰۸/۱۵۹ را به آن بدهید MySQL هر دوی آن اعداد را ذخیره خواهد کرد. و هنگام بازیابی داده، شما همان دو عدد را دریافت می‌کنید.

هنگامی که شما از توابع پایگاه داده برای مقایسه، تبدیل و اندازه‌گیری داده‌ استفاده می‌کنید، مجموعه کاراکتری مهم‌تر می‌شود. برای مثال LENGTH طول یک فیلد به مجموعه کاراکتری‌اش وابسته است، و همچنین مقایسه‌های رشته‌ای با استفاده از LIKE و = . متدی که برای مقایسه رشته‌ها استفاده می‌شود collation نام دارد.

مجموعه‌های کاراکتری و مقایسه (collation)ها در MySQL یک موضوع جامع است. این به سادگیِ یک مورد از تغییر مجموعه کاراکتری یک جدول به UTF-8 نیست. بیشتر دستورات SQL اطمینان از اینکه داده در فرمت مناسب نیز وارد و خارج می‌شود را در نظر می‌گیرند. این وبلاگ نقطه شروع خوبی است.

خودتان آن را امتحان کنید.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
$charset = $_POST['charset']; if (!$charset) $charset = 'ISO-8859-1';
$string = $_POST['string'];
if ($string) {
        echo '<p>This is what PHP thinks you entered:<br>';
        for ($i=0; $i<strlen($string); $i++) {$c=substr ($string,$i,1); echo ord ($c).': '.$c.' <br/>';}
}      
?>     
<html>
<head>
<meta charset="<?=$charset?>">
</head>
<body>
<form method="post">
<input name="lastcharset" type="hidden" value="<?php echo $charset?>"/>
Form was submitted as: <?php echo $_POST['lastcharset']?><br/>
Text is displayed as: <?php echo $charset?><br/>
Text will be submitted as: <?php echo $charset?><br/>
Copy and paste or type here:
<input name="string" type="text" size="20" value="<?php echo $string?>"/><br/>
Next page will display as:
<select name="charset"><option>ISO-8859-1<option>ISO-8859-5
<option>Windows-1251<option>ISO-8859-7<option>UTF-8</select><br/>
<input type="submit" value="Submit" onclick="ShowCharacters (this.form.string.value); return 1;"/>
</form>
<script type="text/javascript">
function ShowCharacters (s) {
  var r='You entered:';
  for (var i=0; i<s.length; i++) r += '\n' + s.charCodeAt (i) + ': ' + s.substr (i, 1);
  alert (r);
}
</script>
</body>
</html>

 

 

 

 

 

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


نمونه‌ای از ورودی و خروجی در مجموعه‌های کاراکتری متفاوت. این شکل یک علامت £ را نشان می‌دهد که در گوگل کروم به یک � تغییر می‌کند.

صفحه بالا گذشته، حال و آینده مجموعه‌های کاراکتری را نشان می‌دهد. می‌توانید از این کد استفاده کنید تا سریعاً ببینید چگونه متن از هم تفکیک می‌شود. برای مثال اگر در شکل بالا دوباره Submit را فشار دهید، � دارای کد پوینتِ یونیکد ۶۵۵۳۳ است که معادل ۲۳۹/۱۹۱/۱۸۹ در UTF-8 است و به صورت �۵۰ در ISO-8859-1 نشان داده می‌شود. بنابراین اگر همیشه نمادهای £ شما تبدیل به � می‌شود، به خاطر مسیر و روشی است که از آن طریق دریافت شده‌اند.

توجه داشته باشید که در کد بالا select box که در پایین صفحه قرار گرفته در هر بار تغییر دوباره مقدارش به ISO-8859-1 باز می‌گردد.

یک راه حل


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

1
2
<meta charset="UTF-8">
<meta http-equiv="Content-type" content="text/html; charset=UTF-8">

 

 

 

 

 

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

همچنین می‌توانید UTF-8 را در جدول‌های MySQL مشخص کنید، هر چند برای استفاده کامل از این ویژگی، نیاز به جستجوی بیشتر در این باره دارید.

توجه داشته باشید که کاربران هنوز می‌توانند مجموعه کاراکتری را در مرورگرهایشان نادیده بگیرند. این موضوع نادر است اما به این معنا است که این راه حلی تضمین شده نیست. برای امنیت بیشتر، می‌توانید در مرحله پایانی یک بررسی را برای اطمینان از اینکه داده به شکل صحیح وارد شده است انجام دهید.

وب سایت‌های موجود

اگر وب سایت شما پیش از این متن را به زبان‌های گوناگون جمع آوری می‌کرده پس نیاز به تبدیل داده موجود به UTF-8 خواهید داشت. اگر در سایت شما متن‌هایی با زبان‌های مختلف وجود ندارد می‌توانید از یک صفحه PHP شبیه همانی که در بالا آمد برای تعیین مجموعه کاراکتری اصلی استفاده کنید و از مرورگر برای تبدیل داده به UTF-8 استفاده کنید.

اگر مقدار زیادی داده در مجموعه‌های کاراکتری مختلف داشته باشید، نیاز دارید تا ابتدا مجموعه کاراکتری را پیدا کنید و سپس آن را تبدیل نمایید. در PHP می‌توانید از mb_detect_encoding برای پیدا کردن و از iconv برای تبدیل استفاده کنید. هنگام خواندن توضیحات mb_detect_encoding به نظر می‌رسد کاملاً تابع سخت‌گیری است تا مطمئن شود که شما از آن درست استفاده می‌کنید و نتایج صحیحی می‌گیرید.

تابع utf8_decode ممکن است گمراه کننده باشد. این تابع UTF-8 را به ISO-8859-1 تبدیل می‌کند. هر کاراکتری که در ISO-8859-1 موجود نیست (مثل سیریلیک، یونانی، تایلندی و نظایر اینها) به علامت سوال تبدیل می‌شوند. این گمراه کننده است به دلیل اینکه ممکن است شما انتظار بیشتری را از این تابع داشته باشید اما بهتر از این نمی‌تواند کاری انجام دهد.

خلاصه

این نوشته به شدت بر روی اعداد تکیه کرده و سعی کرده تا همه امکانات را به کار بگیرد. امیدوارم درک جامعی از مجموعه‌های کاراکتری، یونیکد، UTF-8 و مشکلات مختلفی که می‌توانند به وجود آیند را فراهم کرده باشد. مفاهیم داستان عبارتند از:

  • شما به منظور درک متن غیر لاتین نیاز به شناخت مجموعه کاراکتری دارید
  • به صورت داخلی، مرورگرها از یونیکد برای نمایش کاراکترها استفاده می‌کنند
  • اطمینان حاصل کنید که همه صفحه‌های وبتان UTF-8 را به عنوان مجموعه کاراکتری مشخص کرده‌اند

برای رویکردی متفاوتی به این موضوع، این مقاله ۲۰۰۳ درباره مجموعه کاراکتری عالی است. با تشکر از شما برای همراهی‌تان با این سفر حماسی.

در انتها لینک چند سایت مفید برای یونیکد:

یونیکد تصویری : http://unicodinator.com/

سایتی برای کاراکترهای ♥، اعداد و علائم نقطه گذاری: http://graphemica.com/

کاوش یونیکد: http://unicode.martinleopold.com/

منبع:

http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/

تو پیشگامیت، میتونی سوالات رو بپرسی و هم میتونی مطلب منتشر کنی.

پیشگام پلاس   پرسش و پاسخ



ثبت یک نظر

آدرس ایمیل شما منتشر نخواهد شد. فیلدهای الزامی مشخص شده اند *

  ورود / ثبت نام با حساب گوگل