Skip to content

Instantly share code, notes, and snippets.

@cnbluefire
Last active December 4, 2025 09:46
Show Gist options
  • Select an option

  • Save cnbluefire/10b7d84c69536fd80a90bac72a867c06 to your computer and use it in GitHub Desktop.

Select an option

Save cnbluefire/10b7d84c69536fd80a90bac72a867c06 to your computer and use it in GitHub Desktop.
猜成语小玩具
using System.Text.Json;
var data = LoadIdioms();
Console.WriteLine("""
使用空格分割四字的个匹配,支持以下模式:
汉字:精确匹配
字母:模糊匹配拼音
字母+感叹号:精确匹配拼音
数字:音调,可以追加到字母后面,如sheng!1
下划线:忽略
例如:
>>> yi sheng!1 1 _
一生一代
一生一世
英声欺人
蝇声蛙躁
成语数据库来自
https://github.com/crazywhalecc/idiom-database/raw/refs/heads/master/data/idiom.json
如果无法自动下载,可以手动下载后放置到 %TEMP%\FrogDataFolder\idiom.json
""");
while (true)
{
Console.WriteLine("请输入以空格分隔的四个字或拼音:");
var line = Console.ReadLine();
var arr = line?.Split(' ') ?? [];
if (arr.Length == 4)
{
var query = data;
for (int i = 0; i < arr.Length; i++)
{
if (TryParseData(arr[i], out var alpha, out var fullMatchAlpha, out var tone, out var text))
{
var idx = i;
if (!string.IsNullOrEmpty(alpha))
{
query = query.Where(c => c.Pinyin_r_Array[idx].Contains(alpha));
}
if (!string.IsNullOrEmpty(fullMatchAlpha))
{
query = query.Where(c => c.Pinyin_r_Array[idx] == fullMatchAlpha);
}
if (tone.HasValue)
{
query = query.Where(c => c.ToneArray[idx] == tone.Value);
}
if (!string.IsNullOrEmpty(text))
{
query = query.Where(c => c.Word![idx] == text[0]);
}
}
}
foreach (var item in query)
{
Console.WriteLine($"{item.Word}");
}
}
}
static bool TryParseData(string input, out string? alpha, out string? fullMatchAlpha, out int? tone, out string? text)
{
alpha = null;
fullMatchAlpha = null;
tone = null;
text = null;
if (string.IsNullOrEmpty(input)) return false;
if (input == "" || input == "_" || input == "?" || input == ".") return false;
bool fullMatch = input.Contains('!');
input = input.Replace("!", "");
if (char.IsNumber(input[^1]))
{
tone = input[^1] - '0';
input = input[..^1];
}
if (input.Length > 0)
{
if (char.IsAsciiLetter(input[0]))
{
if (fullMatch) fullMatchAlpha = input;
else alpha = input;
}
else
{
text = input;
}
}
return true;
}
static IEnumerable<IdiomModel> LoadIdioms()
{
var tmpPath = System.IO.Path.Combine(Path.GetTempPath(), "FrogDataFolder");
if (!Directory.Exists(tmpPath)) Directory.CreateDirectory(tmpPath);
var jsonPath = Path.Combine(tmpPath, "idiom.json");
if (!File.Exists(jsonPath))
{
using var httpClient = new HttpClient();
using var stream = httpClient.GetStreamAsync("https://github.com/crazywhalecc/idiom-database/raw/refs/heads/master/data/idiom.json").GetAwaiter().GetResult();
using var fs = File.Create(jsonPath);
stream.CopyTo(fs);
}
return JsonSerializer.Deserialize<IdiomModel[]>(File.ReadAllText(jsonPath), JsonSerializerOptions.Web)?
.Where(c => c.Word is { Length: 4 } && c.Abbreviation is { Length: 4 }) ?? throw new ArgumentException();
}
public class IdiomModel
{
public string? Derivation { get; set; }
public string? Example { get; set; }
public string? Explanation { get; set; }
public string? Pinyin { get; set; }
public string? Word { get; set; }
public string? Abbreviation { get; set; }
public string? Pinyin_r { get; set; }
public string? First { get; set; }
public string? Last { get; set; }
private string[]? pinyin_data;
private string[]? pinyin_r_data;
private int[]? tone_data;
public string[] PinyinArray => pinyin_data ??= Pinyin?.Split(' ') ?? [];
public string[] Pinyin_r_Array => pinyin_r_data ??= Pinyin_r?.Split(' ') ?? [];
public int[] ToneArray => tone_data ??= PinyinArray.Select(c => ToTone(c)).ToArray();
private static int ToTone(string text)
{
const string Tone1 = "āēīōūǖ";
const string Tone2 = "áéíóúǘń";
const string Tone3 = "ǎěǐǒǔǚň";
const string Tone4 = "àèìòùǜ";
if (text.Any(c => Tone1.Contains(c))) return 1;
if (text.Any(c => Tone2.Contains(c))) return 2;
if (text.Any(c => Tone3.Contains(c))) return 3;
if (text.Any(c => Tone4.Contains(c))) return 4;
return 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment