如何实现USFM与分号分隔文件互转及ListView展示?
词典软件输入自动化项目:USFM转换与MDF集成问题
项目背景
我正在开发个人项目,用于加速和自动化词典软件的输入流程,目前项目接近完成。核心需求如下:
- 将词典软件导出的USFM记录转换为分号分隔格式,在ListView中展示数据
- 编辑完成后,将数据转回原始USFM格式,导出回词典软件,再通过Multi-Dictionary Formatting (MDF)工具生成格式化词典
已尝试操作
- 已成功将USFM文件(Sqlite DB导出的纯文本)加载到项目文本框,但未找到合适的USFM转换方法
- 需要忽略文件前两行头部信息,仅在ListView中展示词汇记录;转回USFM时必须保留这两行头部
- 每条词汇记录以
\lx(词位)开头,以\dt(日期戳)结尾
USFM示例
\_sh v3.0 400 MDF \_DateStampHasFourDigitYear \lx ahapei pyt \ps sub \ge Earring \gn Brinco \xv \xe \xn \sd Artesanato \dt 18/Apr/2023 \lx ahasep \ps vb \ge Extinguish (fire, lamp); Switch off (light) \gn Apagar (fogueira, lamparina); Desligar (luz) \xv \xe \xn \dt 18/Apr/2023 \lx ahehay \ps vb. \ge Converse; Talk \gn Falar; Conversar \xv ahehay yn aiwyria'in wywo. \xe we alway talk with our family/relatives. \xn nós sempre falamos com nossos familiares/parentes. \dt 03/Feb/2023 \lx ahek \ps adj. \ge Breathless \gn Fôlego (sem) \xv ase'i tuwepot'am yity'ok apypekiat kape hawyi sese i'ahek. \xe after the older man/grandfather climbed up to the top of the hill he was really breathless. \xn depois que o homem mais velho/vovô subiu até o topo do morro ele estava muito sem fôlego. \cf imo'ahek \dt 10/May/2023
需求细节
- 不同词汇记录的USFM标记数量可能不同,部分字段(如
\xv、\xe、\xn)可能为空,空字段需用分号占位以匹配ListView列 - 词位(
\lx内容)可能是单个或多个单词 - 可选择两种实现路径:要么先转成分号分隔格式再展示到ListView,要么直接在ListView中解析展示USFM记录(无需文本框中转)
- 需要了解如何在Visual Studio的Windows Forms项目中集成MDF工具,实现完整的格式化词典输出
已实现的文件打开代码(VB.NET)
Dim myStream As Stream = Nothing Dim ofd As New OpenFileDialog() With ofd .InitialDirectory = "C:\Users\jmwil\Documents\Lexique Pro\Data" .Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" .FilterIndex = 2 .RestoreDirectory = True If ofd.ShowDialog() = System.Windows.Forms.DialogResult.OK Then Try myStream = ofd.OpenFile() If (myStream IsNot Nothing) Then ' 打开选中的文件名到文本框 TextBox35.Text = ofd.FileName Dim fileReader As String fileReader = My.Computer.FileSystem.ReadAllText(ofd.FileName.ToString, System.Text.Encoding.UTF32) 'MsgBox(fileReader) TextBox36.Text = fileReader End If Catch Ex As Exception MessageBox.Show("Cannot read file from disk. Original error: " & Ex.Message) Finally ' 确保流已关闭 If (myStream IsNot Nothing) Then myStream.Close() End If End Try End If End With
解决方案步骤
一、USFM解析与分号格式转换
分离头部与记录
读取文件后拆分内容为行,提取前两行作为头部保存,剩余行用于处理词汇记录:Dim allLines As String() = fileReader.Split({vbCrLf, vbLf}, StringSplitOptions.RemoveEmptyEntries) Dim headerLines As New List(Of String)() Dim recordLines As New List(Of String)() Dim isHeaderDone As Boolean = False For Each line As String In allLines If Not isHeaderDone Then headerLines.Add(line.Trim()) If headerLines.Count = 2 Then isHeaderDone = True End If Else recordLines.Add(line.Trim()) End If Next解析单个词汇记录
遍历行内容,按\lx分割出每个词汇块,提取各标记对应的内容:Dim records As New List(Of Dictionary(Of String, String))() Dim currentRecord As Dictionary(Of String, String) = Nothing For Each line As String In recordLines If line.StartsWith("\lx") Then ' 新记录开始,保存上一条(如果存在) If currentRecord IsNot Nothing Then records.Add(currentRecord) End If currentRecord = New Dictionary(Of String, String)() currentRecord("lx") = line.Substring(3).Trim() ElseIf line.StartsWith("\dt") Then ' 记录结束,添加日期并保存 currentRecord("dt") = line.Substring(3).Trim() records.Add(currentRecord) currentRecord = Nothing ElseIf line.StartsWith("\") Then ' 处理其他标记 Dim tag As String = line.Substring(1).Split(" "c)(0) Dim content As String = line.Substring(tag.Length + 2).Trim() currentRecord(tag) = content End If Next转换为分号分隔格式并绑定到ListView
定义展示列,遍历记录生成每行内容,空字段用分号占位,最后绑定到ListView:Dim csvLines As New List(Of String)() Dim columns As String() = {"lx", "ps", "ge", "gn", "xv", "xe", "xn", "sd", "cf", "dt"} ' 添加表头 csvLines.Add(String.Join(";", columns)) For Each rec In records Dim lineParts As New List(Of String)() For Each col In columns If rec.ContainsKey(col) Then ' 转义内容中的分号 lineParts.Add(rec(col).Replace(";", "\;")) Else lineParts.Add("") End If Next csvLines.Add(String.Join(";", lineParts)) Next ' 设置ListView列并绑定数据 ListView1.Columns.AddRange(columns.Select(Function(c) New ColumnHeader() With {.Text = c}).ToArray()) For Each line In csvLines.Skip(1) ' 跳过表头 Dim parts As String() = line.Split(";"c) Dim item As New ListViewItem(parts(0)) For i = 1 To parts.Length - 1 item.SubItems.Add(parts(i).Replace("\;", ";")) Next ListView1.Items.Add(item) Next从分号格式转回USFM
读取ListView内容,按USFM格式重新拼接,加上保存的头部后导出:Dim sb As New StringBuilder() ' 添加头部 sb.AppendLine(headerLines(0)) sb.AppendLine(headerLines(1)) sb.AppendLine() For Each item As ListViewItem In ListView1.Items ' 提取每个字段 Dim rec As New Dictionary(Of String, String)() For i = 0 To columns.Length - 1 rec(columns(i)) = item.SubItems(i).Text.Replace("\;", ";") Next ' 按USFM格式拼接 sb.AppendLine($"\lx {rec("lx")}") If Not String.IsNullOrEmpty(rec("ps")) Then sb.AppendLine($"\ps {rec("ps")}") If Not String.IsNullOrEmpty(rec("ge")) Then sb.AppendLine($"\ge {rec("ge")}") If Not String.IsNullOrEmpty(rec("gn")) Then sb.AppendLine($"\gn {rec("gn")}") ' 处理空的xv/xe/xn sb.AppendLine($"\xv {rec("xv")}") sb.AppendLine($"\xe {rec("xe")}") sb.AppendLine($"\xn {rec("xn")}") sb.AppendLine() If Not String.IsNullOrEmpty(rec("sd")) Then sb.AppendLine($"\sd {rec("sd")}") If Not String.IsNullOrEmpty(rec("cf")) Then sb.AppendLine($"\cf {rec("cf")}") sb.AppendLine($"\dt {rec("dt")}") sb.AppendLine() Next ' 保存为USFM文件 My.Computer.FileSystem.WriteAllText("output.usfm", sb.ToString(), False, System.Text.Encoding.UTF32)
二、Windows Forms集成MDF工具
MDF工具通常为命令行程序,可通过Process类调用:
- 将MDF工具添加到项目
将MDF可执行文件(如mdf.exe)复制到项目输出目录,或在代码中指定完整路径。 - 调用命令行执行MDF
Dim mdfPath As String = "C:\Path\To\mdf.exe" Dim inputUsfmPath As String = "output.usfm" Dim outputPath As String = "formatted_dictionary.html" ' 或MDF支持的其他格式 Dim processStartInfo As New ProcessStartInfo(mdfPath) processStartInfo.Arguments = $" -i {inputUsfmPath} -o {outputPath}" processStartInfo.WindowStyle = ProcessWindowStyle.Hidden processStartInfo.CreateNoWindow = True Using process As Process = Process.Start(processStartInfo) process.WaitForExit() ' 检查退出码判断是否成功 If process.ExitCode = 0 Then MessageBox.Show("词典格式化完成") Else MessageBox.Show("MDF执行失败") End If End Using
内容的提问来源于stack exchange,提问作者John Michael Wilkinson
相关产品推荐
相关产品推荐

