代码规范:符合分析(以格式化QQ为例)
创建日期:2025-03-22
更新日期:2025-03-22
阅读次数:24
一阶
这次是一个C#小项目,用于自动格式化QQ消息记录。 F函数用于输入一个文件路径(存放原消息记录)、产出一个新文件(存放格式化后的消息)。 处理文本函数是核心,对文件内容做格式化。
public class Program {
public static void F() {
Console.WriteLine("请输入文件路径:(例如:D:\\Desktop\\测试.txt)");
var A = Console.ReadLine().Trim();
if (!File.Exists(A)) {
Console.WriteLine("文件不存在,请检查路径是否正确。");
return;
}
if (!A.EndsWith(".txt") && !A.EndsWith(".md")) {
Console.WriteLine("只支持处理txt或md文件。");
return;
}
try {
File.WriteAllText(Path.Combine(
Path.GetDirectoryName(A),
Path.GetFileNameWithoutExtension(A) + "_调整" + Path.GetExtension(A)
), 处理文本(File.ReadAllText(A, Encoding.UTF8)), Encoding.UTF8);
Console.WriteLine($"成功!");
} catch (Exception ex) {
Console.WriteLine($"失败:{ex.Message}");
}
}
(已屏蔽)
public static string 处理文本(string X) {
X = Regex.Replace(X, @"\r", "");
X = Regex.Replace(X, @"\n+", "\n");
X = Regex.Replace(X, @"\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\n", "");(已屏蔽)
var A = new StringBuilder();
var currentSpeaker = "";
foreach (var i in X.Split('\n')) {
int colonIndex = i.IndexOf(": ");
if (colonIndex >= 0) {
var speaker = i[..colonIndex].Trim();
var content = i[(colonIndex + 1)..].Trim();
if (speaker != currentSpeaker) {
if (currentSpeaker != "") {(已屏蔽)
A.AppendLine("\n");
}
currentSpeaker = speaker;
A.Append($"{speaker}: {content}");
} else {
A.Append($"{content}");(已屏蔽)
}
} else {
A.Append($"\n{i.Trim()}");(已屏蔽)
}
}
return A.ToString().Trim().Replace(": ", ":");
}
}
这个项目的核心函数里,开篇用了三个正则表达式。 它们完全可以写成一个,高效简洁;但为了可读性,轲目苦津要求写成三个。
二阶
上面那个大ForEach还是太复杂了。 它可以改为两步:先删除连续的同名角色名,再对新角色增加空行。 即:(只看处理文本)
public static string 处理文本(string X) {
X = Regex.Replace(X, @"\r", "");
X = Regex.Replace(X, @"\n+", "\n");
X = Regex.Replace(X, @"\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\n", "");(已屏蔽)
var A = 解析消息记录(X);
for (int i = 0; i < A.Count - 1; i++) { (已屏蔽)
if (A[i].发言人 == A[i + 1].发言人) {
A[i].发言内容 += "\n" + A[i + 1].发言内容;
A.RemoveAt(i + 1);
i--;
}
}
return string.Join("\n\n", A);
}
public static List<消息> 解析消息记录(string 消息记录) {
var 消息列表 = new List<消息>();
string[] 消息片段 = Regex.Split(消息记录, @"\n(?=.*: )").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
foreach (var 片段 in 消息片段) {
int 冒号索引 = 片段.IndexOf(": ");
if (冒号索引 == -1) {
if (消息列表.Count != 0) {
消息列表[^1].发言内容 += "\n" + 片段.Trim();
}
} else {
消息列表.Add(new 消息 {
发言人 = 片段[..冒号索引].Trim(),
发言内容 = 片段[(冒号索引 + 1)..].Trim()
});
}
}
return 消息列表;
}
public class 消息 {
public string 发言人;
public string 发言内容;
public override string ToString() => 发言人 + ":" + 发言内容;
}
三阶
二阶看上去已经很棒了,但其实还不够。 foreach里面两层if,还是太丑、容易出错、难以debug。 进一步归一化预处理,把每一行消息都变为【发言人:内容】的格式:
public static string 处理文本(string X) {
X = Regex.Replace(X, @"\r", "");
X = Regex.Replace(X, @"\n+", "\n");
X = Regex.Replace(X, @"\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\n", "");(已屏蔽)
X = 发言人显化(X);
var A = 解析消息记录(X);
for (int i = 0; i < A.Count - 1; i++) { (已屏蔽)
if (A[i].发言人 == A[i + 1].发言人) {
A[i].发言内容 += "\n" + A[i + 1].发言内容;
A.RemoveAt(i + 1);
i--;
}
}
return string.Join("\n\n", A);
}
public static string 发言人显化(string X) {
var 最终消息 = "";
var 当前角色 = "";
foreach (var i in X.Split('\n')) {
int A = i.IndexOf(": ");
if (A == -1) {
最终消息 +="\n"+ 当前角色 + ": " + i;
} else {
当前角色 = i[..A];
最终消息 +="\n"+ i;
}
}
return 最终消息;
}
public static List<消息> 解析消息记录(string 消息记录) {
var 消息列表 = new List<消息>();
string[] 消息片段 = Regex.Split(消息记录, @"\n(?=.*: )").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
foreach (var 片段 in 消息片段) {
int 冒号索引 = 片段.IndexOf(": ");
消息列表.Add(new 消息 {
发言人 = 片段[..冒号索引].Trim(),
发言内容 = 片段[(冒号索引 + 1)..].Trim()
});
}
return 消息列表;
}
四阶
设计一个消息记录类。
public class 消息类 {
public string 发言人;
public string 发言内容;
public override string ToString() => 发言人 + ":" + 发言内容;
}
public class 消息记录类 : List<消息类> {
public 消息记录类(string X) {
X = Regex.Replace(X, @"\r", "");
X = Regex.Replace(X, @"\n+", "\n");
X = Regex.Replace(X, @"\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\n", "");(已屏蔽)
X = 发言人显化(X);
foreach (var i in X.Split('\n')) {
int 冒号索引 = i.IndexOf(": ");
Add(new 消息类 {
发言人 = i[..冒号索引].Trim(),
发言内容 = i[(冒号索引 + 1)..].Trim()
});
}
}
public 消息记录类 合并() {
for (int i = 0; i < Count - 1; i++) {
if (this[i].发言人 == this[i + 1].发言人) {
this[i].发言内容 += "\n" + this[i + 1].发言内容;
RemoveAt(i + 1);
i--;
}
}
return this;
}
public override string ToString() => string.Join("\n\n", this);
public static string 发言人显化(string X) {
var 最终消息 = "";
var 当前角色 = "";
foreach (var i in X.Split('\n')) {
int A = i.IndexOf(": ");
if (A == -1) {
最终消息 += 当前角色 + ": " + i + "\n";
} else {
当前角色 = i[..A];
最终消息 += i + "\n";
}
}
return 最终消息.Trim();
}
}
五阶
增加一些奇技淫巧。得到最终优雅的代码。
public class Program {
public static void F() {
Console.WriteLine("请输入文件路径:(例如:D:\\Desktop\\测试.txt)");
var A = Console.ReadLine().Trim();
if (!File.Exists(A)) {
Console.WriteLine("文件不存在,请检查路径是否正确。");
return;
}
if (!A.EndsWith(".txt") && !A.EndsWith(".md")) {
Console.WriteLine("只支持处理txt或md文件。");
return;
}
try {
File.WriteAllText(Path.Combine(
Path.GetDirectoryName(A),
Path.GetFileNameWithoutExtension(A) + "_调整" + Path.GetExtension(A)
), new 消息记录类(File.ReadAllText(A, Encoding.UTF8)), Encoding.UTF8);
Console.WriteLine($"成功!");
} catch (Exception ex) {
Console.WriteLine($"失败:{ex.Message}");
}
}
}
public class 消息类 {
public string 发言人;
public string 发言内容;
public 消息类(string 合法消息) {
int 冒号索引 = 合法消息.IndexOf(": ");
发言人 = 合法消息[..冒号索引].Trim();
发言内容 = 合法消息[(冒号索引 + 1)..].Trim();
}
public override string ToString() => 发言人 + ":" + 发言内容;
}
public class 消息记录类 : List<消息类> {
public 消息记录类(string X) {
X = Regex.Replace(X, @"\r", "");
X = Regex.Replace(X, @"\n+", "\n");
X = Regex.Replace(X, @"\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\n", "");(已屏蔽)
X = 发言人显化(X);
X.Split('\n').ForEach(t => Add(new 消息类(t)));
合并();
}
public void 合并() {
for (int i = 0; i < Count - 1; i++) {
if (this[i].发言人 == this[i + 1].发言人) {
this[i].发言内容 += "\n" + this[i + 1].发言内容;
RemoveAt(i + 1);
i--;
}
}
}
public static string 发言人显化(string X) {
var 最终消息 = "";
var 当前角色 = "";
foreach (var i in X.Split('\n')) {
int A = i.IndexOf(": ");
if (A == -1) {
最终消息 += 当前角色 + ": " + i + "\n";
} else {
当前角色 = i[..A];
最终消息 += i + "\n";
}
}
return 最终消息.Trim();
}
public override string ToString() => string.Join("\n\n", this as List<消息类>);
public static implicit operator string(消息记录类 X) => X.ToString();
}
public static class Helper {
public static void ForEach<T>(this IEnumerable<T> X, Action<T> Y) {
foreach (T i in X) {
Y(i);
}
}
}
最终的代码就非常容易维护,也非常容易扩展。 改动代码时遇到bug的话很好定位与修复。可读性非常强,思路清晰。 对比一阶的代码,对用户而言虽然功能是一样的,但对程序员而言上天差地别。 用户面向现在,程序员面向未来。