8 changed files with 526 additions and 406 deletions
@ -0,0 +1,302 @@
@@ -0,0 +1,302 @@
|
||||
import 'package:win_text_editor/app/providers/logger.dart'; |
||||
|
||||
class _CellMatch { |
||||
final String cellText; |
||||
final int matchLength; |
||||
|
||||
_CellMatch(this.cellText, this.matchLength); |
||||
} |
||||
|
||||
class _TableData { |
||||
final String header; |
||||
final List<String> content; |
||||
final int year; |
||||
final String month; |
||||
|
||||
_TableData(this.header, this.content, this.year, this.month); |
||||
} |
||||
|
||||
class ContentDealService { |
||||
static const String _logTag = 'ContentDealService'; |
||||
static const int _maxDisplayLines = 100; |
||||
|
||||
// Content classification identifiers |
||||
static const String rawTextSection = '原始文本'; |
||||
static const String processedTextSection = '处理后文本'; |
||||
|
||||
static const Map<String, String> _headerCharMap = { |
||||
'A': '日', |
||||
'B': '月', |
||||
'C': '水', |
||||
'D': '金', |
||||
'E': '火', |
||||
'F': '木', |
||||
'G': '土', |
||||
'O': '天', |
||||
'I': '海', |
||||
'J': '冥', |
||||
'L': '南', |
||||
'K': '北', |
||||
'M': '孛', |
||||
'N': '凯', |
||||
}; |
||||
|
||||
Map<String, String> processPdfContent(String allText) { |
||||
// Process raw text display |
||||
final rawLines = allText.split('\n'); |
||||
final rawContent = |
||||
rawLines.length > _maxDisplayLines |
||||
? '${rawLines.take(_maxDisplayLines).join('\n')}\n<<<剩余内容未显示>>>' |
||||
: allText; |
||||
|
||||
// Extract and process all tables with their dates |
||||
final tables = _extractTablesWithDates(allText); |
||||
final processedTables = tables |
||||
.map((table) { |
||||
return _processTable(table.content, table.year, table.month); |
||||
}) |
||||
.where((table) => table.isNotEmpty) |
||||
.join('\n'); // 过滤空表并直接连接,不再添加额外空行 |
||||
|
||||
// Limit processed content display |
||||
final processedLines = processedTables.split('\n'); |
||||
final limitedProcessedContent = |
||||
processedLines.length > _maxDisplayLines |
||||
? '${processedLines.take(_maxDisplayLines).join('\n')}\n<<<剩余内容未显示>>>' |
||||
: processedTables; |
||||
|
||||
return { |
||||
ContentDealService.rawTextSection: rawContent, |
||||
ContentDealService.processedTextSection: limitedProcessedContent, |
||||
}; |
||||
} |
||||
|
||||
List<_TableData> _extractTablesWithDates(String rawText) { |
||||
final lines = rawText.split('\n'); |
||||
final tables = <_TableData>[]; |
||||
List<String>? currentTable; |
||||
String? currentHeader; |
||||
int currentYear = 0; |
||||
String currentMonth = ''; |
||||
|
||||
for (final line in lines) { |
||||
// Check for new table header |
||||
final dateMatch = RegExp(r'^([A-Z]+)\s+(\d{4})\s+00:00 UT').firstMatch(line); |
||||
if (dateMatch != null) { |
||||
// Save previous table if exists |
||||
if (currentTable != null && currentTable.isNotEmpty) { |
||||
tables.add(_TableData(currentHeader ?? '', currentTable, currentYear, currentMonth)); |
||||
} |
||||
|
||||
// Start new table |
||||
currentYear = int.parse(dateMatch.group(2)!); |
||||
currentMonth = dateMatch.group(1)!; |
||||
currentHeader = line; |
||||
currentTable = []; |
||||
continue; |
||||
} |
||||
|
||||
// Check for table end |
||||
if (line.contains('Delta T =') && currentTable != null) { |
||||
tables.add(_TableData(currentHeader ?? '', currentTable, currentYear, currentMonth)); |
||||
currentTable = null; |
||||
continue; |
||||
} |
||||
|
||||
// Add to current table if exists |
||||
if (currentTable != null) { |
||||
currentTable.add(line); |
||||
} |
||||
} |
||||
|
||||
// Add last table if exists |
||||
if (currentTable != null && currentTable.isNotEmpty) { |
||||
tables.add(_TableData(currentHeader ?? '', currentTable, currentYear, currentMonth)); |
||||
} |
||||
|
||||
return tables.where((table) => table.content.isNotEmpty).toList(); // 过滤掉空表 |
||||
} |
||||
|
||||
String _processTable(List<String> tableLines, int year, String month) { |
||||
final buffer = StringBuffer(); |
||||
bool foundHeader = false; |
||||
int weekDayIndex = 0; |
||||
final weekDays = ['M', 'T', 'W', 'T', 'F', 'S', 'S']; |
||||
final monthNum = _getMonthNumber(month); |
||||
|
||||
for (final line in tableLines) { |
||||
if (line.trim().isEmpty) continue; |
||||
|
||||
// 特殊处理表头行 |
||||
if (line.contains('Day Sid.t A B C D E F G O I J L K M N Day')) { |
||||
foundHeader = true; |
||||
// 将表头按空格分割 |
||||
var headerCells = line.trim().split(RegExp(r'\s+')); |
||||
|
||||
// 替换字母为中文并移除最后一列 |
||||
headerCells = |
||||
headerCells |
||||
.take(headerCells.length - 1) // 移除最后一列Day |
||||
.map( |
||||
(cell) => |
||||
cell.length == 1 && _headerCharMap.containsKey(cell) |
||||
? _headerCharMap[cell]! |
||||
: cell, |
||||
) |
||||
.toList(); |
||||
|
||||
// 在开头只插入日期列,将Day改为Week |
||||
headerCells[0] = 'Week'; // 将Day改为Week |
||||
headerCells.insert(0, '日期'); |
||||
buffer.writeln(headerCells.join(',')); |
||||
continue; |
||||
} |
||||
|
||||
if (!foundHeader) continue; |
||||
|
||||
// 数据行处理 |
||||
final dayMatch = RegExp(r'^([A-Z])\s*\d+').firstMatch(line.trim()); |
||||
if (dayMatch != null) { |
||||
final weekday = dayMatch.group(1)!; // 只提取星期字母 |
||||
|
||||
// 生成日期 (YYYYMMDD) |
||||
final dateStr = |
||||
(year > 0 && monthNum > 0 && dayMatch.group(2) != null) |
||||
? '${year}${monthNum.toString().padLeft(2, '0')}${dayMatch.group(2)!.padLeft(2, '0')}' |
||||
: ''; |
||||
|
||||
// 使用原有的_parseNormalLine方法处理数据行(修改为只保留星期字母) |
||||
final csvLine = _parseNormalLine(line, weekday); // 直接传入星期字母 |
||||
|
||||
// 写入完整行(只添加日期列,不添加月份列) |
||||
buffer.write('$dateStr,'); // 只有日期列 |
||||
buffer.writeln(csvLine); |
||||
|
||||
weekDayIndex++; |
||||
} |
||||
} |
||||
|
||||
return buffer.toString().trim(); |
||||
} |
||||
|
||||
// 恢复原有的_parseNormalLine方法(保持原有单元格解析逻辑) |
||||
String _parseNormalLine(String line, String weekDay) { |
||||
final cells = <String>[]; |
||||
var remainingLine = line.trim(); |
||||
|
||||
// 1. 处理第一单元格(星期+日期) |
||||
var firstCellMatch = RegExp(r'^([A-Za-z]\d{1,2})\b').firstMatch(remainingLine); |
||||
if (firstCellMatch == null) { |
||||
firstCellMatch = RegExp(r'^([A-Za-z])\s*(\d{1,2})\b').firstMatch(remainingLine); |
||||
if (firstCellMatch != null) { |
||||
final mergedCell = '${firstCellMatch.group(1)}${firstCellMatch.group(2)}'; |
||||
cells.add(mergedCell); |
||||
remainingLine = remainingLine.substring(firstCellMatch.end).trim(); |
||||
} else { |
||||
cells.add(weekDay); |
||||
} |
||||
} else { |
||||
cells.add('$weekDay${firstCellMatch.group(1)!.substring(1)}'); |
||||
remainingLine = remainingLine.substring(firstCellMatch.end).trim(); |
||||
} |
||||
|
||||
// 2. 处理第二单元格(三组数字) |
||||
final secondCell = _parseThreeNumbersCell(remainingLine); |
||||
if (secondCell != null) { |
||||
cells.add(secondCell); |
||||
remainingLine = remainingLine.substring(secondCell.length).trim(); |
||||
} |
||||
|
||||
// 3. 处理第三单元格(三组符号) |
||||
final thirdCell = _parseThreeSymbolsCell(remainingLine); |
||||
if (thirdCell != null) { |
||||
cells.add(_replaceZodiacLetters(thirdCell)); |
||||
remainingLine = remainingLine.substring(thirdCell.length).trim(); |
||||
} |
||||
|
||||
// 4. 处理剩余单元格 |
||||
int columnNumber = 4; |
||||
while (remainingLine.isNotEmpty && columnNumber++ <= 16) { |
||||
final match = _parseTwoSymbolsCell(remainingLine); |
||||
if (match == null) { |
||||
Logger().error( |
||||
'[$_logTag] Failed to match cells, remaining: "$remainingLine"', |
||||
source: _logTag, |
||||
); |
||||
break; |
||||
} |
||||
cells.add(_replaceZodiacLetters(match.cellText)); |
||||
remainingLine = remainingLine.substring(match.matchLength).trim(); |
||||
} |
||||
|
||||
return cells.join(','); |
||||
} |
||||
|
||||
int _getMonthNumber(String month) { |
||||
const monthMap = { |
||||
'JANUARY': 1, |
||||
'FEBRUARY': 2, |
||||
'MARCH': 3, |
||||
'APRIL': 4, |
||||
'MAY': 5, |
||||
'JUNE': 6, |
||||
'JULY': 7, |
||||
'AUGUST': 8, |
||||
'SEPTEMBER': 9, |
||||
'OCTOBER': 10, |
||||
'NOVEMBER': 11, |
||||
'DECEMBER': 12, |
||||
}; |
||||
return monthMap[month] ?? 0; |
||||
} |
||||
|
||||
String? _parseThreeNumbersCell(String input) { |
||||
final match = RegExp(r'^(\d+\s+\d+\s+\d+)').firstMatch(input); |
||||
return match?.group(0); |
||||
} |
||||
|
||||
String? _parseThreeSymbolsCell(String input) { |
||||
final match = RegExp(r'^(\d+[^\d\s]\s*\d+[^\d\s]\s*\d+)').firstMatch(input); |
||||
return match?.group(0); |
||||
} |
||||
|
||||
_CellMatch? _parseTwoSymbolsCell(String input) { |
||||
var match = RegExp(r'^(\d+[^\d\s]{1,2})\s+(\d+)').firstMatch(input); |
||||
if (match != null) { |
||||
final fullText = match.group(0)!; |
||||
final cellText = '${match.group(1)}${match.group(2)}'; |
||||
return _CellMatch(cellText, fullText.length); |
||||
} |
||||
|
||||
match = RegExp(r'^(\d+[^\d\s]{1,2}\s*\d+)').firstMatch(input); |
||||
if (match != null) { |
||||
return _CellMatch(match.group(0)!, match.group(0)!.length); |
||||
} |
||||
|
||||
match = RegExp(r'^(\d+[^\d\s]{1,2})').firstMatch(input); |
||||
if (match != null) { |
||||
return _CellMatch(match.group(0)!, match.group(0)!.length); |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
String _replaceZodiacLetters(String text) { |
||||
const replacementMap = { |
||||
'a': '戌', //'羊', |
||||
'b': '酉', //'牛', |
||||
'c': '申', //'子', |
||||
'd': '未', //'蟹', |
||||
'e': '午', //'狮', |
||||
'f': '巳', //'女', |
||||
'g': '辰', //'秤', |
||||
'h': '卯', //'蝎', |
||||
'i': '寅', //'射', |
||||
'j': '丑', //'羯', |
||||
'k': '子', //'水', |
||||
'l': '亥', //'鱼', |
||||
}; |
||||
|
||||
return text.split('').map((char) => replacementMap[char] ?? char).join(''); |
||||
} |
||||
} |
Loading…
Reference in new issue