أساسيات برمجة Dart

النصوص واستيفاء النصوص

30 دقيقة الدرس 3 من 6

إنشاء النصوص في Dart

النصوص هي أحد أكثر أنواع البيانات استخداماً في أي لغة برمجة. في Dart النص هو تسلسل من وحدات كود UTF-16 مما يعني أنه يدعم الأحرف الدولية والرموز التعبيرية والرموز الخاصة مباشرة. يمكنك إنشاء النصوص باستخدام علامات اقتباس مفردة أو مزدوجة أو ثلاثية.

النصوص الحرفية

void main() {
  // علامات اقتباس مفردة (مفضلة في Dart)
  String greeting = 'مرحبا بالعالم!';

  // علامات اقتباس مزدوجة (صالحة أيضاً)
  String message = "أهلا بك في Dart";

  // كلاهما متطابق -- اختر أسلوباً واحداً وكن متسقاً
  print(greeting);  // مرحبا بالعالم!
  print(message);   // أهلا بك في Dart
}
نصيحة: يوصي دليل أسلوب Dart باستخدام علامات الاقتباس المفردة للنصوص بشكل افتراضي. استخدم علامات الاقتباس المزدوجة فقط عندما يحتوي النص نفسه على علامات مفردة لتجنب الهروب.

أحرف الهروب

أحياناً تحتاج لتضمين أحرف خاصة داخل نص. استخدم الشرطة المائلة العكسية (\) كحرف هروب:

تسلسلات الهروب الشائعة

void main() {
  // سطر جديد
  print('السطر الأول\nالسطر الثاني');

  // مسافة جدولة
  print('الاسم:\tإدريس');

  // علامة اقتباس مفردة داخل نص بعلامات مفردة
  print('It\'s a great day!');

  // علامة اقتباس مزدوجة داخل نص بعلامات مزدوجة
  print("قالت \"مرحبا\"");

  // الشرطة المائلة نفسها
  print('المسار: C:\Users\Dart');

  // علامة الدولار (بدون استيفاء)
  print('السعر: \$99.99');
}

النصوص الخام

إذا أردت تعطيل جميع تسلسلات الهروب ضع البادئة r قبل النص. هذا مفيد لمسارات الملفات والتعبيرات النمطية.

النصوص الخام مع بادئة r

void main() {
  // نص عادي -- \n هو سطر جديد
  print('مرحبا\nالعالم');
  // المخرجات:
  // مرحبا
  // العالم

  // نص خام -- \n يُعامل كنص حرفي
  print(r'مرحبا\nالعالم');
  // المخرجات: مرحبا\nالعالم

  // مفيد لأنماط regex
  String pattern = r'\d{3}-\d{4}';
  print(pattern);  // \d{3}-\d{4}
}

النصوص متعددة الأسطر

استخدم علامات الاقتباس الثلاثية (''' أو """) لإنشاء نصوص تمتد على عدة أسطر بدون الحاجة لـ \n:

النصوص متعددة الأسطر

void main() {
  String poem = '''
الورود حمراء،
البنفسج أزرق،
Dart رائعة،
وأنت كذلك!
''';

  print(poem);
}

استيفاء النصوص

استيفاء النصوص هو أحد أقوى ميزات Dart. بدلاً من دمج النصوص بعامل + تقوم بتضمين المتغيرات والتعبيرات مباشرة داخل النص باستخدام رمز $.

استيفاء المتغيرات البسيط

استخدم $variableName لإدراج قيمة متغير:

الاستيفاء الأساسي

void main() {
  String name = 'إدريس';
  int age = 28;
  String language = 'Dart';

  // مع الاستيفاء (موصى به)
  print('اسمي $name وعمري $age سنة.');
  print('أتعلم $language!');

  // بدون استيفاء (غير موصى به)
  print('اسمي ' + name + ' وعمري ' + age.toString() + ' سنة.');
}
ملاحظة: استيفاء النصوص مفضل دائماً على الدمج بـ +. إنه أنظف وأكثر قراءة ويتجنب الحاجة لاستدعاء .toString() يدوياً على القيم غير النصية.

استيفاء التعبيرات

استخدم ${expression} لأي شيء أكثر تعقيداً من اسم متغير بسيط -- الحسابات أو استدعاءات الدوال أو الوصول للخصائص:

استيفاء التعبيرات مع ${}

void main() {
  int a = 10;
  int b = 20;

  // تعبير حسابي
  print('مجموع $a و $b هو ${a + b}');
  // المخرجات: مجموع 10 و 20 هو 30

  // استدعاءات الدوال
  String name = 'إدريس صالح';
  print('أحرف كبيرة: ${name.toUpperCase()}');

  // الوصول للخصائص
  List<int> numbers = [1, 2, 3, 4, 5];
  print('القائمة تحتوي ${numbers.length} عناصر');

  // تعبير شرطي
  int score = 85;
  print('النتيجة: ${score >= 60 ? "ناجح" : "راسب"}');
}

دمج النصوص

رغم تفضيل الاستيفاء تدعم Dart عدة طرق لدمج النصوص:

طرق دمج النصوص

void main() {
  // 1. باستخدام عامل +
  String first = 'مرحبا';
  String second = 'بالعالم';
  String combined = first + ' ' + second;
  print(combined);  // مرحبا بالعالم

  // 2. نصوص حرفية متجاورة (تُدمج تلقائياً)
  String auto = 'مرحبا '
      'بالعالم '
      'من Dart';
  print(auto);  // مرحبا بالعالم من Dart

  // 3. StringBuffer لعمليات الدمج الكثيرة (فعال)
  var buffer = StringBuffer();
  buffer.write('مرحبا');
  buffer.write(' ');
  buffer.write('بالعالم');
  print(buffer.toString());  // مرحبا بالعالم
}
نصيحة: عند بناء نصوص داخل حلقات أو مع أجزاء كثيرة استخدم StringBuffer بدلاً من +. عامل + ينشئ كائن String جديداً في كل مرة بينما StringBuffer يعدل نفس المخزن المؤقت مما يجعله أكثر كفاءة في استخدام الذاكرة.

خصائص النصوص المفيدة

كل نص في Dart يأتي مع خصائص مفيدة:

خصائص النصوص

void main() {
  String text = 'مرحبا Dart!';

  print(text.length);      // 11 (عدد الأحرف)
  print(text.isEmpty);     // false
  print(text.isNotEmpty);  // true

  String empty = '';
  print(empty.isEmpty);    // true
  print(empty.length);     // 0
}

دوال النصوص الشائعة

توفر Dart العديد من الدوال المدمجة للعمل مع النصوص. إليك أهمها:

تحويل الحالة

void main() {
  String name = 'Edrees Salih';

  print(name.toUpperCase());  // EDREES SALIH
  print(name.toLowerCase());  // edrees salih
}

إزالة المسافات

void main() {
  String padded = '   مرحبا بالعالم!   ';

  print(padded.trim());       // 'مرحبا بالعالم!'
  print(padded.trimLeft());   // 'مرحبا بالعالم!   '
  print(padded.trimRight());  // '   مرحبا بالعالم!'
}

البحث في النصوص

void main() {
  String sentence = 'Dart ممتعة و Dart قوية';

  print(sentence.contains('ممتعة'));        // true
  print(sentence.startsWith('Dart'));       // true
  print(sentence.endsWith('قوية'));         // true
  print(sentence.indexOf('Dart'));          // 0 (أول ظهور)
  print(sentence.lastIndexOf('Dart'));      // 12 (آخر ظهور)
}

الاستبدال والتقسيم

void main() {
  String text = 'Hello World';

  // الاستبدال
  print(text.replaceAll('World', 'Dart'));  // Hello Dart

  // التقسيم إلى قائمة
  String csv = 'تفاح,موز,برتقال';
  List<String> fruits = csv.split(',');
  print(fruits);  // [تفاح, موز, برتقال]

  // نص فرعي
  print(text.substring(0, 5));  // Hello
  print(text.substring(6));     // World
}

الحشو والتكرار

void main() {
  String id = '42';

  // الحشو لطول أدنى
  print(id.padLeft(5, '0'));   // 00042
  print(id.padRight(5, '-'));  // 42---

  // تكرار نص
  String line = '=-' * 10;
  print(line);  // =-=-=-=-=-=-=-=-=-=-
}

مقارنة النصوص

النصوص في Dart تُقارن بمحتواها وليس بمرجعها:

مقارنة النصوص

void main() {
  String a = 'Dart';
  String b = 'Dart';
  String c = 'dart';

  print(a == b);  // true (نفس المحتوى)
  print(a == c);  // false (حساسة لحالة الأحرف)

  // مقارنة غير حساسة لحالة الأحرف
  print(a.toLowerCase() == c.toLowerCase());  // true
}

تحويل الأنواع مع النصوص

التحويل بين النصوص والأنواع الأخرى مهمة شائعة:

التحويل من وإلى النصوص

void main() {
  // رقم إلى نص
  int age = 28;
  String ageStr = age.toString();
  print(ageStr);  // '28'

  // نص إلى رقم
  String numStr = '42';
  int number = int.parse(numStr);
  print(number);  // 42

  // تحويل آمن (يُرجع null بدلاً من الطرح)
  int? safe = int.tryParse('abc');
  print(safe);  // null (لا خطأ)

  int? valid = int.tryParse('100');
  print(valid);  // 100
}
خطأ شائع: استخدام int.parse() أو double.parse() مع مدخلات غير صالحة سيطرح FormatException. استخدم دائماً tryParse() عند التعامل مع مدخلات المستخدم أو البيانات من مصادر خارجية لأنها تُرجع null بدلاً من التعطل.

النصوص غير قابلة للتغيير

مفهوم مهم: النصوص في Dart غير قابلة للتغيير. كل عملية تبدو وكأنها تعدل نصاً تُنشئ في الواقع نصاً جديداً. النص الأصلي يبقى كما هو.

توضيح عدم قابلية التغيير

void main() {
  String original = 'مرحبا';
  String upper = original.toUpperCase();

  print(original);  // مرحبا (لم يتغير!)
  print(upper);     // مرحبا (نص جديد)
}

تمرين عملي

افتح DartPad وابنِ برنامجاً: (1) أنشئ متغيرات للاسم الأول واسم العائلة والعمر والمدينة. (2) استخدم استيفاء النصوص لطباعة مقدمة منسقة مثل “مرحباً أنا [الاسم] عمري [العمر] سنة من [المدينة].” (3) حوّل الاسم الكامل لأحرف كبيرة واحسب طوله. (4) قسّم نصاً من الهوايات مفصولة بفواصل إلى قائمة واطبع كل هواية. (5) استخدم tryParse لتحويل نص إلى عدد صحيح بأمان.