我的 RTF 解析器需要处理两种类型的 rtf 文件(每个程序执行一个文件):从 Word 保存的 rtf 文件和由 COTS 报告生成器实用程序创建的 rtf 文件。每个的 rtf 都是有效的,但不同。我的解析器使用正则表达式模式来检测、提取和处理两种 rtf 文件中的各种 rtf 元素。
我决定在两个字典中实现 rtf 正则表达式列表,一个用于 Word rtf 文件所需的 rtf regex 模式,另一个用于 COTS 实用程序 rtf 文件所需的 rtf regex 模式。在运行时,我的解析器检测正在处理哪种类型的 rtf 文件(Word rtf 包含 rtf 元素//schemas.microsoft.com/office/word
而 COTS rtf 不包含),然后从适当的字典中获取所需的正则表达式模式。
为了简化在编写代码时引用模式的任务,我实现了一个枚举,其中每个枚举值代表一个特定的正则表达式模式。为了简化保持模式与其相应枚举同步的任务,我将正则表达式模式实现为here-string
每行是一个 csv 组合:{enum name}, {word rtf regex pattern}, {cots rtf regex pattern}
. 然后,在将模式加载到其字典中的运行时,我从 csv 获取枚举的 int 值并使用它来创建字典键。
这使得编写代码更容易,但我不确定这是实现和引用 rtf 表达式的最佳方式。有没有更好的办法?
示例代码:
public enum Rex {FOO, BAR};
string ex = @"FOO, word rtf regex pattern for FOO, cots rtf regex pattern for FOO
BAR, word rtf regex pattern for BAR, cots rtf regex pattern for BAR
";
我像这样加载字典:
using (StringReader reader = new StringReader(ex))
{
string line;
while ((line = reader.ReadLine()) != null)
{
string[] splitLine = line.Split(',');
int enumIntValue = (int)(Rex)Enum.Parse(typeof(Rex), splitLine[0].Trim());
ObjWordRtfDict.Add(enumIntValue, line.Split(',')[1].Trim());
ObjRtfDict.Add(enumIntValue, line.Split(',')[2].Trim());
}
}
然后,在运行时,我根据解析器检测到的 rtf 文件的类型访问 ObjWordRtfDict 或 ObjRtfDict。
string regExPattFoo = ObjRegExExpr.GetRegExPattern(ClsRegExExpr.Rex.FOO);
public string GetRegExPattern(Rex patternIndex)
{
string regExPattern = "";
if (isWordRtf)
{
ObjWordRtfDict.TryGetValue((int)patternIndex, out regExPattern);
}
else
{
ObjRtfDict.TryGetValue((int)patternIndex, out regExPattern);
}
return regExPattern;
}
根据 Asif 的建议修改了新代码
我保留了模式名称的枚举,因此编译器可以检查对模式名称的引用
作为嵌入资源包含的示例 csv 文件
SECT,^\\pard.*\{\\rtlch.*\\sect\s\}, ^\\pard.*\\sect\s\}
HORZ_LINE2, \{\\pict.*\\pngblip, TBD
示例用法
string sectPattern = ObjRegExExpr.GetRegExPattern(ClsRegExPatterns.Names.SECT);
ClsRegExPatterns 类
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
namespace foo
{
public class ClsRegExPatterns
{
readonly bool isWordRtf = false;
List<ClsPattern> objPatternList;
public enum Names { SECT, HORZ_LINE2 };
public class ClsPattern
{
public string Name { get; set; }
public string WordRtfRegex { get; set; }
public string COTSRtfRegex { get; set; }
}
public ClsRegExPatterns(StringBuilder rawRtfTextFromFile)
{
// determine if input file is Word rtf or not Word rtf
if ((Regex.Matches(rawRtfTextFromFile.ToString(), "//schemas.microsoft.com/office/word", RegexOptions.IgnoreCase)).Count == 1)
{
isWordRtf = true;
}
//read patterns from embedded content csv file
string patternsAsCsv = new StreamReader((Assembly.GetExecutingAssembly()).GetManifestResourceStream("eLabBannerLineTool.Packages.patterns.csv")).ReadToEnd();
//create list to hold patterns
objPatternList = new List<ClsPattern>();
//load pattern list
using (StringReader reader = new StringReader(patternsAsCsv))
{
string line;
while ((line = reader.ReadLine()) != null)
{
string[] splitLine = line.Split(',');
ClsPattern objPattern = new ClsPattern
{
Name = splitLine[0].Trim(),
WordRtfRegex = splitLine[1].Trim(),
COTSRtfRegex = splitLine[2].Trim()
};
objPatternList.Add(objPattern);
}
}
}
public string GetRegExPattern(Names patternIndex)
{
string regExPattern = "";
string patternName = patternIndex.ToString();
if (isWordRtf)
{
regExPattern = objPatternList.SingleOrDefault(x => x.Name == patternName)?.WordRtfRegex;
}
else
{
regExPattern = objPatternList.SingleOrDefault(x => x.Name == patternName)?.COTSRtfRegex;
}
return regExPattern;
}
}
}