بازگشت به دوره

تبدیل انواع داده به یکدیگر

تبدیل انواع داده‌ی مختلف به یکدیگر، امری معمول در برنامه‌نویسی است. مثلاً تبدیل یک مقدار عددی به یک رشته و یا تبدیل یک رشته به یک مقدار عددی. این تبدیلات به دو شکل صریح و ضمنی صورت می‌پذیرند. در تبدیل صریح، برنامه‌نویس با تشخیص خود یک نوع داده را به نوعی دیگر تبدیل می‌کند. اما در تبدیل ضمنی، مفسر (یا کامپایلر) به صورت خودکار این تبدیل را انجام می‌دهد.

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

فرض کنید در یک برنامه‌ی جاوا اسکریپت دستور زیر نوشته شده باشد.


let a = '3' * 3;

در این دستور یک رشته در یک عدد ضرب شده است. نتیجه‌ی این عبارت چه خواهد بود؟ توجه کنید که در این دستور هیچ تبدیل صریحی توسط برنامه‌نویس انجام نشده است. لذا مفسر باید تصمیم بگیرد که آیا تبدیل خاصی لازم است یا خیر؟ و یا اینکه اصلاً امکان تبدیل نوع وجود دارد یا خیر؟

در زبان‌های برنامه‌نویسی مختلف، در چنین شرایطی تصمیم‌های متفاوتی اتخاذ می‌شود که می‌توان به موارد زیر اشاره کرد.

۱- تولید خطا و توقف برنامه به دلیل امکان‌پذیر نبودن ضرب یک عدد در یک رشته

۲- تبدیل رشته به عدد و سپس انجام عمل ضرب

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


let a = 'Hello' * 3;

آیا می‌توان رشته‌ی "Hello" را به یک عدد تبدیل کرد؟ در زبان‌های برنامه‌نویسی مختلف پاسخ این سوال متفاوت است. اما در جاوا اسکریپت پاسخ آن مثبت است. چرا که در جاوا اسکریپت مقدار ویژه‌ای به نام NaN وجود دارد. پس هر مقدار رشته‌ای که قابل تبدیل به عدد نباشد به مقدار NaN تبدیل خواهد شد. در واقع اگر محتوای رشته‌ی مورد نظر دقیقاً یک عدد باشد (در هر یک از مبناهای ۲، ۸، ۱۰ و ۱۶)، به مقدار عددی معادل تبدیل می‌شود. اگر هم رشته تهی باشد (طول آن صفر باشد)، یا فقط شامل فضاهای خالی باشد به مقدار عددی صفر تبدیل می‌شود. در غیر این صورت به مقدار NaN تبدیل خواهد شد. به مثال‌هایی از این تبدیل‌ها در قطعه کد زیر توجه کنید.


'10' * 1;
← 10
'10.3' * 1;
← 10.3
'0xff' * 1;
← 255
'0b1110' * 1;
← 14
'Hello' * 1;
← NaN
'22aa' * 1;
← NaN
' ' * 1;
← 0

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

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


let a = '7' + 3;

به نظر شما نتیجه‌ی عبارت فوق چه خواهد بود؟ می‌دانیم که عملگر "+" هم بر روی اعداد قابل استفاده است (برای عمل جمع) و هم بر روی رشته‌ها (برای عمل الحاق). حال سوال این است که در عبارت فوق از کدام عمل این عملگر باید استفاده شود؟ اگر از عمل جمع استفاده شود، باید رشته به عدد تبدیل شود و اگر از عمل الحاق استفاده شود، باید عدد به رشته تبدیل شود. و واضح است که نتیجه‌ی این دو حالت یکسان نیست و به ترتیب برابر با "10" و "73" خواهد بود.

با توجه به توضیحات فوق و با توجه به این که این تبدیلات به صورت ضمنی انجام می‌شوند و برنامه‌نویس نقشی در آن ندارد. لذا لازم است تا برنامه‌نویس از نحوه‌ی انجام این تبدیلات اطلاع دقیق داشته باشد تا این موارد را در زمان نوشتن برنامه لحاظ کند.

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

 

تبدیل صریح رشته به عدد

ساده‌ترین روش تبدیل رشته‌ها (یا هر نوع دیگری) به داده‌های عددی، استفاده از تابع تبدیل Number است. این تابع یک ورودی را (از هر نوع داده‌ای) دریافت کرده و آن را به یک عدد تبدیل می‌کند. قوانینی که برای تبدیل به کار برده می‌شود، دقیقاً همان قوانینی است که در تبدیل ضمنی رشته‌ها (یا انواع دیگر) به اعداد به کار برده می‌شود. چند نمونه از کاربرد این تابع را در قطعه کد زیر مشاهده می‌کنید.


Number('10');
← 10
Number('10.3');
← 10.3
Number('0xff');
← 255
Number('22aa');
← NaN
Number('     ');
← 0

حال به مثال قبلی باز می‌گردیم. قطعه کد زیر نتیجه‌ی استفاده از عملگر "+" بین یک داده‌ی رشته‌ای و یک داده‌ی عددی را در دو حالت تبدیل ضمنی و تبدیل صریح نشان می‌دهد.


let str = '7' , num = 3;
str + num;
← "73"
Number(str) + num;
← 10

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

اما در جاوا اسکریپت، دو تابع دیگر نیز برای تبدیل رشته‌ها به اعداد وجود دارد که عبارتند از تابع parseInt و parseFloat.

تفاوت اصلی تابع parseInt با تابع Number در این است که اگر رشته‌ی ورودی با اعداد شروع شود، سپس شامل چند کاراکتر غیر عددی نیز باشد. در این صورت تابع parseInt قسمت‌های غیر عددی را حذف کرده و فقط بخش عددی را تبدیل می‌کند. در صورتی که تابع Number در چنین شرایطی مقدار NaN را بازمی‌گرداند. همچنین خروجی تابع parseInt فقط می‌تواند عدد صحیح باشد. لذا از بخش اعشاری رشته‌ی ورودی صرف نظر می‌شود. تفاوت دیگر این دو تابع نیز در این است که تابع parseInt به ازای رشته‌ای که فقط شامل فضای خالی باشد (یا تهی باشد)، مقدار NaN را بازمی‌گرداند. ولی تابع Number مقدار 0 را بازمی‌گرداند. نمونه‌های از تبدیل رشته‌ها به اعداد را در قطعه کد زیر مشاهده می‌کنید.


parseInt('1234blue');
← 1234
parseInt('');
← NaN
parseInt('0xA');
← 10
parseInt(22.5);
← 22
parseInt('-123zz343');
← -123

همچنین با استفاده از تابع parseInt، امکان تعیین مبنای رشته‌ی ورودی وجود دارد. یعنی می‌توان بعد از رشته‌ی ورودی، عددی را (بین ۲ تا ۳۶) به عنوان مبنای عددی تعیین کرد تا رشته‌ی ورودی در آن مبنا تفسیر شود. در حالت پیش‌فرض اگر مبنا را مشخص نکنیم، مبنای ۱۰ در نظر گرفته می‌شود. مگر اینکه رشته‌ی ورودی با کاراکترهای "0x" شروع شود (مانند مثال فوق). در قطعه کد زیر نمونه‌هایی از تبدیل در مبناهای مختلف را مشاهده می‌کنید.


parseInt('1010101' , 2);
← 85
parseInt('zzzz' , 36);
← 1679615
parseInt('abcd' , 36);
← 481261
parseInt('abc112' , 16);
← 43981

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


parseFloat('1234blue');
← 1234
parseFloat('0xA');
← 0
parseFloat('22.5');
← 22.5
parseFloat('22.34.5');
← 22.34
parseFloat('0908.5');
← 908.5
parseFloat('3.125e7');
← 31250000
 

تبدیل صریح عدد به رشته

ساده‌ترین راه برای تبدیل اعداد (یا انواع دیگر) به رشته‌ها، استفاده از تابع String است. تبدیل عدد به رشته پیچیدگی خاصی ندارد. زیرا هر عددی به راحتی قابل تبدیل به رشته است و نیاز به اعمال قانون خاصی برای این کار نیست. نمونه‌هایی از کاربرد این تابع را در قطعه کد زیر می‌بینید.


String(10);
← "10"
String(22.11);
← "22.11"
String(2e6);
← "2000000"
String(NaN);
← "NaN"

روش دیگر تبدیل اعداد (و سایر انواع داده) به رشته، استفاده از متد toString است. تمام انواع داده در جاوا اسکریپت، متدی به نام toString دارند که به منظور تبدیل مقدار داده‌ی مورد نظر به رشته به کار برده می‌شود. توجه کنید که اگر از اعداد صحیح به صورت لفظی (Literal) استفاده می‌کنید، برای استفاده از این متد باید از ۲ کاراکتر نقطه استفاده کنید. نمونه‌هایی از کاربرد این متد روی داده‌های عددی را مشاهده می‌کنید.


10..toString();
← "10"
22.11.toString();
← "22.11"
2e6.toString();
← "2000000"
NaN.toString();
← "NaN"

در صورت استفاده از متد toString برای تبدیل اعداد به رشته، می‌توان یک ورودی نیز به عنوان مبنای عدد به این متد ارسال کرد. تا رشته‌ی خروجی در مبنای ذکر شده ظاهر شود. به نمونه‌های زیر توجه کنید.


100..toString(2);
← "1100100"
100..toString(4);
← "1210"
100..toString(16);
← "64"
100..toString(36);
← "2s"

تفاوت اصلی متد toString و تابع String در تبدیل انواع مختلف به رشته‌ها، این است که متد toString توانایی تبدیل داده‌هایی از نوع undefined و null را ندارد و در صورت تلاش برای تبدیل این داده‌ها به رشته توسط متد toString، با خطا مواجه شده و برنامه متوقف خواهد شد. اما تابع String این داده‌ها را نیز می‌تواند به رشته تبدیل کند که به ترتیب آنها را به رشته‌های "undefined" و "null" تبدیل می‌کند (در مورد null و undefined در بخش بعدی صحبت خواهیم کرد).

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