// file_utils.dart import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; import 'package:path/path.dart' as path; import 'package:win_text_editor/framework/controllers/logger.dart'; class FileUtils { static Future pickFile(BuildContext context) async { try { final result = await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: false); return result?.files.single.path; } catch (e) { Logger().error('选择文件失败: ${e.toString()}'); if (context.mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('选择文件失败: ${e.toString()}'))); } return null; } } static Future readFileContent( BuildContext context, String filePath, { Duration timeout = const Duration(seconds: 30), }) async { try { final content = await File(filePath).readAsString().timeout( timeout, onTimeout: () { throw TimeoutException('文件加载超时,可能文件过大'); }, ); return content; } on FormatException { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('这不是可读的文本文件'))); } return null; } on FileSystemException catch (e) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('文件访问错误: ${e.message}'))); } return null; } catch (e) { Logger().error('读取文件失败: ${e.toString()}'); if (context.mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('读取失败: ${e.toString()}'))); } return null; } } static bool matchesFileType(String filePath, String fileType) { // 处理特殊情况 if (fileType == '*.*' || fileType == '*') return true; // 获取文件的实际名称和扩展名 final fileName = path.basename(filePath); final fileExt = path.extension(fileName).toLowerCase().replaceFirst('.', ''); // 分割通配符为文件名和扩展名部分 final parts = fileType.split('.'); final patternName = parts.length > 0 ? parts[0] : ''; final patternExt = parts.length > 1 ? parts.sublist(1).join('.') : ''; // 匹配文件名部分 bool nameMatches = true; if (patternName.isNotEmpty && patternName != '*') { // 如果模式不含通配符,则执行精确匹配 if (!patternName.contains('*') && !patternName.contains('?')) { nameMatches = fileName.startsWith('${patternName.toLowerCase()}.'); } else { nameMatches = matchesWildcard(fileName, patternName); } } // 匹配扩展名部分 bool extMatches = true; if (patternExt.isNotEmpty) { if (patternExt == '*') { extMatches = true; } else if (!patternExt.contains('*') && !patternExt.contains('?')) { // 如果扩展名模式不含通配符,执行精确匹配 extMatches = fileExt == patternExt.toLowerCase(); } else { extMatches = matchesWildcard(fileExt, patternExt.toLowerCase()); } } return nameMatches && extMatches; } static bool matchesWildcard(String input, String pattern) { // 处理特殊情况 if (pattern == '*') return true; // 动态规划实现通配符匹配 final m = input.length; final n = pattern.length; final dp = List.generate(m + 1, (_) => List.filled(n + 1, false)); // 空字符串匹配空模式 dp[0][0] = true; // 处理模式以 * 开头的情况 for (int j = 1; j <= n; j++) { if (pattern[j - 1] == '*') { dp[0][j] = dp[0][j - 1]; } } // 填充动态规划表 for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (pattern[j - 1] == '*') { // * 可以匹配任意字符或空字符 dp[i][j] = dp[i][j - 1] || dp[i - 1][j]; } else if (pattern[j - 1] == '?' || input[i - 1] == pattern[j - 1]) { // ? 匹配单个字符,或者字符直接相等 dp[i][j] = dp[i - 1][j - 1]; } else { // 不匹配 dp[i][j] = false; } } } return dp[m][n]; } static Future readFileWithAutoEncoding(File file) async { try { // 首先尝试 UTF-8 return await file.readAsString(); } on FileSystemException { // 如果 UTF-8 失败,尝试常见其他编码 final bytes = await file.readAsBytes(); // 常见编码尝试顺序 final encodingsToTry = [ Encoding.getByName('gbk'), Encoding.getByName('gb2312'), Encoding.getByName('big5'), latin1, ].whereType(); for (final encoding in encodingsToTry) { try { Logger().debug("尝试字符集:${encoding.name}"); return encoding.decode(bytes); } catch (_) { continue; } } // 所有尝试都失败,返回原始字节 return String.fromCharCodes(bytes); } } }