@@ -84,24 +84,26 @@ size_t CharacterCount(std::string_view str, int ignore) {
8484 if ((ignore & agi::IGNORE_BLOCKS ) == 0 )
8585 return count_in_range (str, mask);
8686
87+ // 使用分词器正确处理 \N、\n、\h 等 ASS 转义序列
88+ auto tokens = agi::ass::TokenizeDialogueBody (str);
89+ agi::ass::MarkDrawings (str, tokens);
90+
8791 size_t characters = 0 ;
88- while (!str.empty ()) {
89- auto pos = str.find (' {' );
90- if (pos == str.npos ) break ;
91-
92- // if there's no trailing }, the rest of the string counts as characters,
93- // including the leading {
94- auto end = str.find (' }' );
95- if (end == str.npos ) break ;
96-
97- if (pos > 0 )
98- characters += count_in_range (str.substr (0 , pos), mask);
99- str.remove_prefix (end + 1 );
92+ size_t pos = 0 ;
93+ for (auto token : tokens) {
94+ if (token.type == agi::ass::DialogueTokenType::TEXT )
95+ characters += count_in_range (str.substr (pos, token.length ), mask);
96+ else if (token.type == agi::ass::DialogueTokenType::LINE_BREAK ) {
97+ // \h 是硬空格,非忽略空白时计为 1 个字符
98+ if (str[pos + 1 ] == ' h' ) {
99+ if (!(mask & U_GC_Z_MASK ))
100+ characters += 1 ;
101+ }
102+ // \N 和 \n 是换行符,不计入字符数
103+ }
104+ pos += token.length ;
100105 }
101106
102- if (!str.empty ())
103- characters += count_in_range (str, mask);
104-
105107 return characters;
106108}
107109
0 commit comments