ميزات Dart المتقدمة

إدخال/إخراج الملفات ومعالجة JSON

50 دقيقة الدرس 13 من 16

مقدمة في إدخال/إخراج الملفات في Dart

العمل مع الملفات هو مهارة أساسية لأي مطور يبني أدوات سطر الأوامر أو تطبيقات الخادم أو خطوط معالجة البيانات. توفر مكتبة dart:io في Dart مجموعة شاملة من الفئات لقراءة وكتابة ومعالجة الملفات والمجلدات. مع dart:convert لمعالجة JSON، لديك كل ما تحتاجه لبناء تطبيقات قوية تعتمد على البيانات.

ملاحظة: مكتبة dart:io متاحة فقط في تطبيقات سطر الأوامر والخادم في Dart. وهي غير متاحة في تطبيقات Flutter للويب أو كود Dart المستند للمتصفح. لتطبيقات Flutter للجوال/سطح المكتب، استخدم حزمة path_provider للحصول على مسارات نظام الملفات المناسبة.

قراءة الملفات

فئة File من dart:io هي أداتك الأساسية لعمليات الملفات. يمكنك قراءة الملفات إما بشكل متزامن (حجب) أو غير متزامن (بدون حجب). القراءة غير المتزامنة مفضلة في معظم الحالات لأنها لا تجمد تطبيقك أثناء انتظار إدخال/إخراج القرص.

قراءة الملفات بشكل غير متزامن

تعيد طريقة readAsString() Future<String> يحتوي على محتويات الملف بالكامل. هذه هي الطريقة الأكثر شيوعاً لقراءة الملفات النصية.

قراءة الملفات بشكل غير متزامن

import 'dart:io';

Future<void> main() async {
  final file = File('config.txt');

  // التحقق من وجود الملف قبل القراءة
  if (await file.exists()) {
    // قراءة الملف بالكامل كنص
    String contents = await file.readAsString();
    print('File contents:\n$contents');

    // قراءة الملف كقائمة من الأسطر
    List<String> lines = await file.readAsLines();
    print('Total lines: ${lines.length}');

    for (var i = 0; i < lines.length; i++) {
      print('Line $i: ${lines[i]}');
    }

    // قراءة الملف كبايتات خام
    List<int> bytes = await file.readAsBytes();
    print('File size: ${bytes.length} bytes');
  } else {
    print('File not found!');
  }
}

قراءة الملفات بشكل متزامن

القراءة المتزامنة تحجب الخيط الحالي حتى تتم قراءة الملف بالكامل. استخدمها فقط في البرامج النصية البسيطة أو عندما تحتاج تحديداً لسلوك الحجب (مثلاً، قراءة ملف تكوين عند بدء التشغيل قبل تشغيل أي شيء آخر).

قراءة الملفات بشكل متزامن

import 'dart:io';

void main() {
  final file = File('data.txt');

  // النسخ المتزامنة — تحجب حتى الاكتمال
  if (file.existsSync()) {
    String contents = file.readAsStringSync();
    List<String> lines = file.readAsLinesSync();
    List<int> bytes = file.readAsBytesSync();

    print('Read ${lines.length} lines, ${bytes.length} bytes');
  }
}
تحذير: تجنب عمليات الملفات المتزامنة في تطبيقات الخادم أو تطبيقات Flutter. فهي تحجب حلقة الأحداث بالكامل، مما يعني أن تطبيقك لا يمكنه معالجة أي طلبات أخرى أو أحداث واجهة المستخدم حتى تكتمل قراءة الملف. دائماً فضّل النسخ async في كود الإنتاج.

تدفق الملفات الكبيرة

للملفات الكبيرة جداً، قراءة كل شيء في الذاكرة دفعة واحدة مهدرة أو حتى مستحيلة. يتيح لك Dart قراءة الملفات كتدفق من القطع، ومعالجة البيانات قطعة بقطعة.

قراءة الملفات المبنية على التدفق

import 'dart:io';
import 'dart:convert';

Future<void> main() async {
  final file = File('large_log.txt');

  // فتح تدفق يقرأ الملف في قطع
  final stream = file.openRead();

  // تحويل قطع البايت إلى نصوص UTF-8 ثم تقسيمها إلى أسطر
  await stream
      .transform(utf8.decoder)
      .transform(const LineSplitter())
      .forEach((line) {
    if (line.contains('ERROR')) {
      print('Found error: $line');
    }
  });

  print('Finished scanning log file.');
}
نصيحة: القراءة المبنية على التدفق مثالية لتحليل السجلات ومعالجة CSV أو أي سيناريو تحتاج فيه لمعالجة ملايين الأسطر بدون تحميلها جميعاً في الذاكرة. ادمج openRead() مع utf8.decoder و LineSplitter للمعالجة الفعالة سطراً بسطر.

كتابة الملفات

كتابة الملفات تتبع نفس النمط كالقراءة: يمكنك الكتابة بشكل متزامن أو غير متزامن، ويمكنك كتابة نصوص أو أسطر أو بايتات خام.

كتابة الملفات (غير متزامن ومتزامن)

import 'dart:io';

Future<void> main() async {
  final file = File('output.txt');

  // كتابة نص (يستبدل المحتوى الموجود)
  await file.writeAsString('Hello, Dart file I/O!\n');

  // الإلحاق بملف موجود
  await file.writeAsString(
    'This line is appended.\n',
    mode: FileMode.append,
  );

  // كتابة أسطر متعددة دفعة واحدة
  final lines = ['Line 1', 'Line 2', 'Line 3'];
  await file.writeAsString(lines.join('\n') + '\n');

  // كتابة بايتات خام
  final bytes = [72, 101, 108, 108, 111]; // "Hello" بترميز ASCII
  await File('binary.dat').writeAsBytes(bytes);

  // الكتابة المتزامنة
  File('sync_output.txt').writeAsStringSync('Written synchronously.\n');

  print('All files written successfully.');
}

الكتابة باستخدام IOSink (الكتابة المتدفقة)

لكتابة كميات كبيرة من البيانات، أو عندما تريد الكتابة بشكل تدريجي، استخدم IOSink. هذا مفيد بشكل خاص لإنشاء التقارير والسجلات أو ملفات الإخراج الكبيرة.

الكتابة المتدفقة باستخدام IOSink

import 'dart:io';

Future<void> main() async {
  final file = File('report.txt');
  final sink = file.openWrite();

  // كتابة الرأس
  sink.writeln('=== Daily Report ===');
  sink.writeln('Generated: ${DateTime.now()}');
  sink.writeln('');

  // كتابة صفوف البيانات
  for (var i = 1; i <= 1000; i++) {
    sink.writeln('Entry #$i: value=${i * 3.14}');
  }

  // كتابة التذييل
  sink.writeln('');
  sink.writeln('=== End of Report ===');

  // مهم: دائماً أغلق المصرف عند الانتهاء
  await sink.flush();
  await sink.close();

  print('Report written with ${await file.length()} bytes.');
}
تحذير: دائماً استدعِ flush() و close() على IOSink عند الانتهاء. إذا تخطيت هذه الخطوة، قد تبقى البيانات في مخزن مؤقت داخلي ولا تصل أبداً للملف. هذا مصدر شائع للأخطاء حيث تظهر الملفات فارغة أو مبتورة.

عمليات المجلدات

تتيح لك فئة Directory إنشاء وعرض وإدارة المجلدات. مع File، يمكنك بناء أدوات إدارة ملفات كاملة.

العمل مع المجلدات

import 'dart:io';

Future<void> main() async {
  // إنشاء مجلد (بما في ذلك المجلدات الأب)
  final dir = Directory('output/reports/2024');
  await dir.create(recursive: true);
  print('Directory created: ${dir.path}');

  // عرض محتويات المجلد
  final currentDir = Directory.current;
  print('Current directory: ${currentDir.path}');

  await for (var entity in currentDir.list()) {
    if (entity is File) {
      print('  File: ${entity.path}');
    } else if (entity is Directory) {
      print('  Dir:  ${entity.path}');
    } else if (entity is Link) {
      print('  Link: ${entity.path}');
    }
  }

  // العرض بشكل متكرر (جميع المجلدات الفرعية)
  await for (var entity in currentDir.list(recursive: true)) {
    if (entity is File && entity.path.endsWith('.dart')) {
      print('Dart file: ${entity.path}');
    }
  }

  // التحقق من وجود المجلد
  if (await dir.exists()) {
    // حذف المجلد ومحتوياته
    await dir.delete(recursive: true);
    print('Directory deleted.');
  }
}

معالجة المسارات

يوفر Dart أدوات مسارات أساسية من خلال فئة Platform في dart:io وكائنات الملفات/المجلدات نفسها. للمعالجة المتقدمة للمسارات، حزمة path هي الخيار القياسي.

عمليات المسارات

import 'dart:io';

void main() {
  // الحصول على معلومات من مسارات الملفات
  final file = File('/home/user/documents/report.pdf');
  print('Path: ${file.path}');
  print('Parent: ${file.parent.path}');
  print('Absolute: ${file.absolute.path}');

  // فاصل المسار حسب النظام
  print('Path separator: ${Platform.pathSeparator}');

  // استخدام حزمة path (أضف إلى pubspec.yaml: path: ^1.8.0)
  // import 'package:path/path.dart' as p;
  // print(p.basename('/home/user/file.txt'));    // file.txt
  // print(p.extension('/home/user/file.txt'));   // .txt
  // print(p.dirname('/home/user/file.txt'));     // /home/user
  // print(p.join('home', 'user', 'file.txt'));  // home/user/file.txt
  // print(p.normalize('a/b/../c'));              // a/c

  // المجلد المؤقت
  final tempDir = Directory.systemTemp;
  print('Temp directory: ${tempDir.path}');

  // إنشاء ملف مؤقت
  final tempFile = File(
    '${tempDir.path}${Platform.pathSeparator}myapp_temp.txt'
  );
  tempFile.writeAsStringSync('Temporary data');
  print('Temp file: ${tempFile.path}');
}

معالجة JSON مع dart:convert

JSON (ترميز كائنات JavaScript) هو أكثر تنسيقات تبادل البيانات شيوعاً على الويب. توفر مكتبة dart:convert المدمجة في Dart دوال jsonDecode و jsonEncode للتحويل بين سلاسل JSON وكائنات Dart.

ترميز وفك ترميز JSON الأساسي

أساسيات JSON

import 'dart:convert';

void main() {
  // === فك الترميز (سلسلة JSON -> كائن Dart) ===

  // كائن JSON -> Map<String, dynamic>
  String jsonString = '{"name": "Alice", "age": 30, "active": true}';
  Map<String, dynamic> user = jsonDecode(jsonString);
  print('Name: ${user['name']}');   // Alice
  print('Age: ${user['age']}');     // 30
  print('Type: ${user.runtimeType}'); // _Map<String, dynamic>

  // مصفوفة JSON -> List<dynamic>
  String jsonArray = '[1, 2, 3, "four", true]';
  List<dynamic> items = jsonDecode(jsonArray);
  print('Items: $items');

  // === الترميز (كائن Dart -> سلسلة JSON) ===
  Map<String, dynamic> product = {
    'id': 42,
    'name': 'Dart Handbook',
    'price': 29.99,
    'tags': ['programming', 'dart'],
  };

  // JSON مضغوط
  String compact = jsonEncode(product);
  print(compact);

  // JSON مُنسّق
  String pretty = const JsonEncoder.withIndent('  ').convert(product);
  print(pretty);
}
نصيحة: استخدم JsonEncoder.withIndent(' ') لإنتاج مخرجات JSON قابلة للقراءة. هذا لا يقدر بثمن لتصحيح الأخطاء والتسجيل وإنشاء ملفات التكوين التي يحتاج البشر لقراءتها.

تعيين JSON إلى نموذج

في التطبيقات الحقيقية، نادراً ما تعمل مع Map<String, dynamic> خام. بدلاً من ذلك، تنشئ فئات نموذج مع طرق fromJson و toJson لضمان أمان النوع وتنظيم أفضل للكود.

فئة نموذج مع تسلسل JSON

import 'dart:convert';

class User {
  final String name;
  final String email;
  final int age;
  final bool isActive;

  User({
    required this.name,
    required this.email,
    required this.age,
    required this.isActive,
  });

  // مُنشئ مصنع لإنشاء User من خريطة JSON
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'] as String,
      email: json['email'] as String,
      age: json['age'] as int,
      isActive: json['is_active'] as bool,
    );
  }

  // تحويل User إلى خريطة JSON
  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'email': email,
      'age': age,
      'is_active': isActive,
    };
  }

  @override
  String toString() => 'User($name, $email, age: $age)';
}

void main() {
  // فك ترميز JSON إلى User
  String jsonStr = '{"name":"Bob","email":"bob@test.com","age":25,"is_active":true}';
  var user = User.fromJson(jsonDecode(jsonStr));
  print(user); // User(Bob, bob@test.com, age: 25)

  // ترميز User إلى JSON
  String encoded = jsonEncode(user.toJson());
  print(encoded);

  // قائمة من المستخدمين
  String usersJson = '[{"name":"A","email":"a@t.com","age":20,"is_active":true}]';
  List<User> users = (jsonDecode(usersJson) as List)
      .map((json) => User.fromJson(json))
      .toList();
  print('Users: $users');
}

التعامل مع JSON المتداخل

غالباً ما تعيد واجهات برمجة التطبيقات الحقيقية هياكل JSON متداخلة بعمق. تحتاج فئات النموذج للتعامل مع الكائنات والمصفوفات المتداخلة بشكل أنيق.

نماذج JSON المتداخلة

import 'dart:convert';

class Address {
  final String street;
  final String city;
  final String country;

  Address({required this.street, required this.city, required this.country});

  factory Address.fromJson(Map<String, dynamic> json) => Address(
    street: json['street'] as String,
    city: json['city'] as String,
    country: json['country'] as String,
  );

  Map<String, dynamic> toJson() => {
    'street': street,
    'city': city,
    'country': country,
  };
}

class Order {
  final int id;
  final String product;
  final double price;

  Order({required this.id, required this.product, required this.price});

  factory Order.fromJson(Map<String, dynamic> json) => Order(
    id: json['id'] as int,
    product: json['product'] as String,
    price: (json['price'] as num).toDouble(),
  );

  Map<String, dynamic> toJson() => {
    'id': id,
    'product': product,
    'price': price,
  };
}

class Customer {
  final String name;
  final Address address;
  final List<Order> orders;

  Customer({
    required this.name,
    required this.address,
    required this.orders,
  });

  factory Customer.fromJson(Map<String, dynamic> json) => Customer(
    name: json['name'] as String,
    address: Address.fromJson(json['address'] as Map<String, dynamic>),
    orders: (json['orders'] as List)
        .map((o) => Order.fromJson(o as Map<String, dynamic>))
        .toList(),
  );

  Map<String, dynamic> toJson() => {
    'name': name,
    'address': address.toJson(),
    'orders': orders.map((o) => o.toJson()).toList(),
  };

  double get totalSpent => orders.fold(0, (sum, o) => sum + o.price);
}

void main() {
  final jsonStr = '''
  {
    "name": "Alice",
    "address": {
      "street": "123 Main St",
      "city": "Springfield",
      "country": "US"
    },
    "orders": [
      {"id": 1, "product": "Laptop", "price": 999.99},
      {"id": 2, "product": "Mouse", "price": 29.99}
    ]
  }
  ''';

  final customer = Customer.fromJson(jsonDecode(jsonStr));
  print('${customer.name} spent \$${customer.totalSpent}');

  // رحلة ذهاب وإياب: الترميز مرة أخرى إلى JSON
  print(const JsonEncoder.withIndent('  ').convert(customer.toJson()));
}

مثال عملي: قارئ ملف التكوين

لنبنِ قارئ ملفات تكوين قابل لإعادة الاستخدام يحمّل ملفات تكوين JSON مع قيم افتراضية وتحقق.

قارئ ملف التكوين

import 'dart:io';
import 'dart:convert';

class AppConfig {
  final String appName;
  final int port;
  final String dbHost;
  final bool debugMode;
  final List<String> allowedOrigins;

  AppConfig({
    required this.appName,
    required this.port,
    required this.dbHost,
    required this.debugMode,
    required this.allowedOrigins,
  });

  factory AppConfig.fromJson(Map<String, dynamic> json) {
    return AppConfig(
      appName: json['app_name'] as String? ?? 'MyApp',
      port: json['port'] as int? ?? 8080,
      dbHost: json['db_host'] as String? ?? 'localhost',
      debugMode: json['debug'] as bool? ?? false,
      allowedOrigins: (json['allowed_origins'] as List<dynamic>?)
              ?.cast<String>() ??
          ['http://localhost'],
    );
  }

  static Future<AppConfig> load(String path) async {
    final file = File(path);
    if (!await file.exists()) {
      print('Config file not found. Using defaults.');
      return AppConfig.fromJson({});
    }

    try {
      final contents = await file.readAsString();
      final json = jsonDecode(contents) as Map<String, dynamic>;
      return AppConfig.fromJson(json);
    } on FormatException catch (e) {
      print('Invalid JSON in config: $e');
      return AppConfig.fromJson({});
    }
  }

  @override
  String toString() =>
      'AppConfig(app: $appName, port: $port, db: $dbHost, debug: $debugMode)';
}

Future<void> main() async {
  final config = await AppConfig.load('config.json');
  print(config);
  print('Allowed origins: ${config.allowedOrigins}');
}

مثال عملي: محلل CSV

يوضح هذا المثال قراءة ملف CSV وتحليله إلى بيانات منظمة وتحويله إلى JSON.

محوّل CSV إلى JSON

import 'dart:io';
import 'dart:convert';

class CsvParser {
  final String separator;

  CsvParser({this.separator = ','});

  /// تحليل ملف CSV إلى قائمة من الخرائط.
  /// يُعامل الصف الأول كرؤوس الأعمدة.
  Future<List<Map<String, String>>> parse(String filePath) async {
    final file = File(filePath);
    final lines = await file.readAsLines();

    if (lines.isEmpty) return [];

    // السطر الأول = الرؤوس
    final headers = lines.first
        .split(separator)
        .map((h) => h.trim())
        .toList();

    // الأسطر المتبقية = صفوف البيانات
    return lines.skip(1).where((line) => line.trim().isNotEmpty).map((line) {
      final values = line.split(separator).map((v) => v.trim()).toList();
      return Map.fromIterables(
        headers,
        values.length >= headers.length
            ? values.sublist(0, headers.length)
            : [...values, ...List.filled(headers.length - values.length, '')],
      );
    }).toList();
  }

  /// تحويل بيانات CSV المُحللة إلى سلسلة JSON.
  String toJson(List<Map<String, String>> data) {
    return const JsonEncoder.withIndent('  ').convert(data);
  }
}

Future<void> main() async {
  // إنشاء ملف CSV نموذجي
  final csvFile = File('employees.csv');
  await csvFile.writeAsString(
    'name,department,salary\n'
    'Alice,Engineering,95000\n'
    'Bob,Marketing,72000\n'
    'Charlie,Engineering,88000\n',
  );

  // التحليل والتحويل
  final parser = CsvParser();
  final data = await parser.parse('employees.csv');
  print('Parsed ${data.length} rows:');
  print(parser.toJson(data));

  // كتابة مخرجات JSON
  await File('employees.json').writeAsString(parser.toJson(data));
  print('JSON file written.');

  // التنظيف
  await csvFile.delete();
}

مثال عملي: كاتب ملف السجلات

ينشئ هذا المثال أداة تسجيل منظمة تكتب إدخالات مؤرخة إلى ملف سجل.

كاتب السجلات المنظم

import 'dart:io';
import 'dart:convert';

enum LogLevel { debug, info, warning, error }

class LogEntry {
  final DateTime timestamp;
  final LogLevel level;
  final String message;
  final Map<String, dynamic>? metadata;

  LogEntry({
    required this.level,
    required this.message,
    this.metadata,
  }) : timestamp = DateTime.now();

  Map<String, dynamic> toJson() => {
    'timestamp': timestamp.toIso8601String(),
    'level': level.name,
    'message': message,
    if (metadata != null) 'metadata': metadata,
  };
}

class FileLogger {
  final File _logFile;
  IOSink? _sink;

  FileLogger(String path) : _logFile = File(path);

  Future<void> open() async {
    await _logFile.parent.create(recursive: true);
    _sink = _logFile.openWrite(mode: FileMode.append);
  }

  void log(LogLevel level, String message, {Map<String, dynamic>? meta}) {
    final entry = LogEntry(level: level, message: message, metadata: meta);
    _sink?.writeln(jsonEncode(entry.toJson()));
  }

  void debug(String msg) => log(LogLevel.debug, msg);
  void info(String msg, {Map<String, dynamic>? meta}) =>
      log(LogLevel.info, msg, meta: meta);
  void warning(String msg) => log(LogLevel.warning, msg);
  void error(String msg, {Map<String, dynamic>? meta}) =>
      log(LogLevel.error, msg, meta: meta);

  Future<void> close() async {
    await _sink?.flush();
    await _sink?.close();
    _sink = null;
  }

  /// قراءة وتحليل جميع إدخالات السجل.
  Future<List<Map<String, dynamic>>> readEntries() async {
    if (!await _logFile.exists()) return [];
    final lines = await _logFile.readAsLines();
    return lines
        .where((l) => l.trim().isNotEmpty)
        .map((l) => jsonDecode(l) as Map<String, dynamic>)
        .toList();
  }
}

Future<void> main() async {
  final logger = FileLogger('logs/app.log');
  await logger.open();

  logger.info('Application started', meta: {'version': '1.0.0'});
  logger.debug('Loading configuration...');
  logger.info('Server listening on port 8080');
  logger.warning('High memory usage detected');
  logger.error('Failed to connect to database', meta: {
    'host': 'db.example.com',
    'error_code': 'ECONNREFUSED',
  });

  await logger.close();

  // قراءة وتصفية الإدخالات
  final entries = await logger.readEntries();
  final errors = entries.where((e) => e['level'] == 'error').toList();
  print('Total entries: ${entries.length}');
  print('Errors: ${errors.length}');
  print(const JsonEncoder.withIndent('  ').convert(errors));
}
ملاحظة: يتم تخزين كل إدخال سجل كسطر JSON واحد (تنسيق JSONL). هذا التنسيق مثالي للسجلات لأنه يمكنك الإلحاق بدون تعديل المحتوى الموجود، ويمكن تحليل كل سطر بشكل مستقل. العديد من أدوات تجميع السجلات (مثل ELK Stack) تدعم JSONL أصلاً.

أفضل ممارسات معالجة الأخطاء لإدخال/إخراج الملفات

يمكن أن تفشل عمليات الملفات لأسباب عديدة: رفض الإذن، القرص ممتلئ، محرك الشبكة غير متاح، أو الملف مقفل بواسطة عملية أخرى. معالجة الأخطاء القوية ضرورية.

عمليات ملفات قوية مع معالجة الأخطاء

import 'dart:io';
import 'dart:convert';

Future<Map<String, dynamic>?> safeReadJson(String path) async {
  try {
    final file = File(path);
    if (!await file.exists()) {
      print('File does not exist: $path');
      return null;
    }

    final contents = await file.readAsString();
    return jsonDecode(contents) as Map<String, dynamic>;
  } on FormatException catch (e) {
    print('Invalid JSON in $path: $e');
    return null;
  } on FileSystemException catch (e) {
    print('File system error reading $path: ${e.message}');
    return null;
  } catch (e) {
    print('Unexpected error reading $path: $e');
    return null;
  }
}

Future<bool> safeWriteJson(
  String path,
  Map<String, dynamic> data,
) async {
  try {
    final file = File(path);
    await file.parent.create(recursive: true);

    // الكتابة إلى ملف مؤقت أولاً ثم إعادة التسمية (كتابة ذرية)
    final tempFile = File('$path.tmp');
    await tempFile.writeAsString(
      const JsonEncoder.withIndent('  ').convert(data),
    );
    await tempFile.rename(path);
    return true;
  } on FileSystemException catch (e) {
    print('Failed to write $path: ${e.message}');
    return false;
  }
}

Future<void> main() async {
  final data = {'key': 'value', 'count': 42};

  if (await safeWriteJson('data/config.json', data)) {
    print('Written successfully.');
    final loaded = await safeReadJson('data/config.json');
    print('Loaded: $loaded');
  }
}
نصيحة: نمط الكتابة الذرية (الكتابة إلى ملف مؤقت ثم إعادة التسمية) يمنع تلف البيانات. إذا تعطل برنامجك أثناء الكتابة، يبقى الملف الأصلي سليماً. هذه أفضل ممارسة لأي ملف بيانات حرج.

الملخص

في هذا الدرس، تعلمت مجموعة الأدوات الكاملة لمعالجة البيانات المبنية على الملفات في Dart:

  • قراءة الملفات مع readAsString و readAsLines و readAsBytes (متزامن وغير متزامن)
  • القراءة المبنية على التدفق لمعالجة الملفات الكبيرة بكفاءة مع openRead()
  • كتابة الملفات مع writeAsString و writeAsBytes و IOSink للكتابة المتدفقة
  • عمليات المجلدات لإنشاء وعرض وحذف المجلدات
  • معالجة المسارات للعمل مع مسارات الملفات عبر المنصات
  • معالجة JSON مع jsonDecode/jsonEncode وتسلسل فئات النموذج
  • JSON المتداخل مع فئات نموذج مركّبة
  • أنماط عملية مثل قراء التكوين ومحللات CSV وكتّاب السجلات المنظمة
  • معالجة الأخطاء والكتابة الذرية لعمليات ملفات قوية