Transliteration类拉丁转西里尔字母时Yu组合识别失败的修复咨询
问题
我正在开发一个Transliteration类,但测试用例LatinToCyrillicTextShouldEqualTheirEqual执行失败,原因是该类无法识别Yu作为组合字符,而是将Y和u分别单独进行转写。请问该如何修复这个问题?
现有代码
Transliteration类
public sealed class Transliteration { private const string CyrillicLowercase = "а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ь ю я"; private const string CyrillicUppercase = "А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ь Ю Я"; private const string LatinLowercase = "a b v g d e zh z i y k l m n o p r s t u f h ts ch sh sht a y yu ya"; private const string LatinUppercase = "A B V G D E Zh Z I Y K L M N O P R S T U F H Ts Ch Sh Sht A Y Yu Ya"; private readonly Dictionary<string,string> _dict; private Transliteration() { _dict = new Dictionary<string, string>(); } private Transliteration(Dictionary<string, string> dict) { ArgumentNullException.ThrowIfNull(dict); _dict = dict; } public static Transliteration Create() { var lowercaseDict = CyrillicLowercase.Split(' ') .Zip(LatinLowercase.Split(' '), (k, v) => new { k, v }) .ToDictionary(x => x.k, x => x.v); var uppercaseDict = CyrillicUppercase.Split(' ') .Zip(LatinUppercase.Split(' '), (k, v) => new { k, v }) .ToDictionary(x => x.k, x => x.v); var both = lowercaseDict.Concat(uppercaseDict) .ToLookup(x => x.Key, x => x.Value) .ToDictionary(x => x.Key, g => g.First()); return new Transliteration(both); } /// <summary> /// Transliterates cyrillic text to latin. /// </summary> /// <param name="text">The text to be transliterated.</param> /// <returns>The transliterated text.</returns> public string CyrillicToLatin(string text) { return _dict.Aggregate(text, (current, pair) => current.Replace(pair.Key, pair.Value)); } /// <summary> /// Transliterates latin text to cyrillic. /// </summary> /// <param name="text">The text to be transliterated.</param> /// <returns>The transliterated text.</returns> public string LatinToCyrillic(string text) { return _dict.Aggregate(text, (current, pair) => current.Replace(pair.Value, pair.Key)); } }
测试类
public class TransliterationTests { private readonly Transliteration _transliteration; public TransliterationTests() { _transliteration = Transliteration.Create(); } [Fact] public void CyrillicToLatinTextShouldEqualTheirEqual() { // Arrange // Act var actual = _transliteration.CyrillicToLatin("Юлия Колева Хаджииванова"); // Assert actual.Should().Be("Yuliya Koleva Hadzhiivanova"); } [Fact] public void LatinToCyrillicTextShouldEqualTheirEqual() { // Arrange // Act var actual = _transliteration.LatinToCyrillic("Yuliya Koleva Hadzhiivanova"); // Assert actual.Should().Be("Юлия Колева Хаджииванова"); } }
问题原因
核心问题在于:
- 双向转换共用同一个字典,且字典键的遍历顺序不固定,无法保证多字符拉丁组合(如
Yu、Zh)优先被替换。 - 执行
LatinToCyrillic时,可能先处理单字符Y或u,导致Yu被拆分为单个字符替换,无法匹配到对应的西里尔字符Ю。
修复方案
为拉丁到西里尔的转换创建单独的映射字典,并确保多字符组合优先处理:
修改后的Transliteration类
public sealed class Transliteration { private const string CyrillicLowercase = "а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ь ю я"; private const string CyrillicUppercase = "А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ь Ю Я"; private const string LatinLowercase = "a b v g d e zh z i y k l m n o p r s t u f h ts ch sh sht a y yu ya"; private const string LatinUppercase = "A B V G D E Zh Z I Y K L M N O P R S T U F H Ts Ch Sh Sht A Y Yu Ya"; private readonly Dictionary<string, string> _cyrillicToLatin; private readonly Dictionary<string, string> _latinToCyrillic; private Transliteration(Dictionary<string, string> cyrillicToLatin, Dictionary<string, string> latinToCyrillic) { ArgumentNullException.ThrowIfNull(cyrillicToLatin); ArgumentNullException.ThrowIfNull(latinToCyrillic); _cyrillicToLatin = cyrillicToLatin; _latinToCyrillic = latinToCyrillic; } public static Transliteration Create() { // 构建西里尔到拉丁的映射 var lowercaseCyrToLat = CyrillicLowercase.Split(' ') .Zip(LatinLowercase.Split(' '), (k, v) => new { k, v }) .ToDictionary(x => x.k, x => x.v); var uppercaseCyrToLat = CyrillicUppercase.Split(' ') .Zip(LatinUppercase.Split(' '), (k, v) => new { k, v }) .ToDictionary(x => x.k, x => x.v); var cyrillicToLatin = lowercaseCyrToLat.Concat(uppercaseCyrToLat) .ToLookup(x => x.Key, x => x.Value) .ToDictionary(x => x.Key, g => g.First()); // 构建拉丁到西里尔的映射,按拉丁字符长度降序排列,确保多字符组合优先替换 var latinToCyrillicPairs = lowercaseCyrToLat.Select(p => new { Latin = p.Value, Cyrillic = p.Key }) .Concat(uppercaseCyrToLat.Select(p => new { Latin = p.Value, Cyrillic = p.Key })) .OrderByDescending(p => p.Latin.Length) .GroupBy(p => p.Latin) .ToDictionary(g => g.Key, g => g.First().Cyrillic); return new Transliteration(cyrillicToLatin, latinToCyrillicPairs); } public string CyrillicToLatin(string text) { return _cyrillicToLatin.Aggregate(text, (current, pair) => current.Replace(pair.Key, pair.Value)); } public string LatinToCyrillic(string text) { return _latinToCyrillic.Aggregate(text, (current, pair) => current.Replace(pair.Key, pair.Value)); } }
修复说明
- 拆分双向转换的字典,分别维护
_cyrillicToLatin和_latinToCyrillic,避免互相干扰。 - 构建
_latinToCyrillic时,通过OrderByDescending(p => p.Latin.Length)确保多字符组合(如Yu、sht)比单字符(如Y、u)先被处理,从根本上避免拆分替换的问题。 - 保持原有西里尔到拉丁的转换逻辑不变,确保功能兼容性。
修改后运行测试用例,LatinToCyrillicTextShouldEqualTheirEqual即可正常通过。
内容的提问来源于stack exchange,提问作者nop
相关产品推荐
相关产品推荐

