/// <summary> /// 返回表示单个不区分大小写的字符的正则表达式。 /// </summary> /// <param name="ch">要表示的字符。</param> /// <param name="culture">转换大小写使用的区域性信息。</param> /// <returns>表示单个字符的正则表达式。</returns> public static Regex SymbolIgnoreCase(char ch, CultureInfo culture) { char upperCase = char.ToUpper(ch, culture); char lowerCase = char.ToLower(ch, culture); RegexCharClass cc = new RegexCharClass(); if (upperCase == lowerCase) { // 大小写相同。 cc.AddChar(upperCase); } else { // 大小写不用。 cc.AddChar(upperCase); cc.AddChar(lowerCase); } return new CharClassExp(cc.ToStringClass()); }
/// <summary> /// 返回表示字符类的正则表达式。 /// </summary> /// <param name="cc">正则表达式表示的字符类。</param> /// <returns>表示字符类的正则表达式。</returns> public static Regex CharClass(RegexCharClass cc) { if (cc == null) { throw CommonExceptions.ArgumentNull("cc"); } return new CharClassExp(cc.ToStringClass()); }
/// <summary> /// 扫描 '\'(不包含 '\' 本身)之后的字符,并返回相应的正则表达式。 /// </summary> /// <returns>相应的正则表达式。</returns> private Regex ScanBackslash() { RegexCharClass cc; int ich; switch (ich = reader.Read()) { case 'w': return Regex.CharClass( UseOptionEcma ? RegexCharClass.EcmaWordClass : RegexCharClass.WordClass); case 'W': return Regex.CharClass( UseOptionEcma ? RegexCharClass.NotEcmaWordClass : RegexCharClass.NotWordClass); case 's': return Regex.CharClass( UseOptionEcma ? RegexCharClass.EcmaSpaceClass : RegexCharClass.SpaceClass); case 'S': return Regex.CharClass( UseOptionEcma ? RegexCharClass.NotEcmaSpaceClass : RegexCharClass.NotSpaceClass); case 'd': return Regex.CharClass( UseOptionEcma ? RegexCharClass.EcmaDigitClass : RegexCharClass.DigitClass); case 'D': return Regex.CharClass( UseOptionEcma ? RegexCharClass.NotEcmaDigitClass : RegexCharClass.NotDigitClass); case 'p': case 'P': cc = new RegexCharClass(); cc.AddCategoryFromName(ScanProperty(), ich != 'p', UseOptionIgnoreCase, pattern); if (UseOptionIgnoreCase) { cc.AddLowercase(culture); } return Regex.CharClass(cc.ToStringClass()); case -1: ThrowIllegalEndEscape(); return null; default: reader.Unget(); char ch = ScanCharEscape(); if (UseOptionIgnoreCase) { return Regex.SymbolIgnoreCase(ch, culture); } else { return Regex.Symbol(ch); } } }
/// <summary> /// 返回表示单个字符的正则表达式。 /// </summary> /// <param name="ch">要表示的字符。</param> /// <returns>表示单个字符的正则表达式。</returns> public static Regex Symbol(char ch) { RegexCharClass cc = new RegexCharClass(); cc.AddChar(ch); return new CharClassExp(cc.ToStringClass()); }
private RegexCharClass ScanCharClass(bool closeBracket) { RegexCharClass rcc = new RegexCharClass(); // 是否正在定义字符范围。 bool inRange = false; // 是否是首字符。 bool firstChar = true; // [] 集合是否闭合。 bool closed = false; // 否定的字符类。 if (reader.Peek() == '^') { reader.Read(); rcc.Negate = true; } int ich = '\0'; char chPrev = '\0'; for (; (ich = reader.Read()) >= 0; firstChar = false) { // 当前字符时候是由转义得到的。 bool escapedChar = false; if (ich == ']') { // [ 之后的第一个 ] 认为是字符 ]。 if (!firstChar) { // 闭合字符类。 closed = true; break; } } else if (ich == '\\' && reader.Peek() >= 0) { // 判断 '\' 字符的转义。 ich = ScanBackslash(rcc, inRange); if (ich == -1) { continue; } } if (inRange) { inRange = false; if (ich == '[' && !escapedChar && !firstChar) { // 本来认为在定义字符范围,但实际在定义一个减去范围。 // 所以,需要将 chPrev 添加到字符类中,跳过 [ 符号,并递归的扫描新的字符类。 rcc.AddChar(chPrev); rcc.AddSubtraction(ScanCharClass(true)); if (reader.Peek() >= 0 && reader.Peek() != ']') { ThrowSubtractionMustBeLast(); } } else { // 规则的字符范围,类似于 a-z。 if (chPrev > ich) { ThrowReversedCharRange(); } rcc.AddRange(chPrev, (char)ich); } } else { if (reader.Peek() == '-') { reader.Read(); if (reader.Peek() >= 0 && reader.Peek() != ']') { // -] 中的,'-' 会按连字符处理。 // 否则的话,认为是字符范围的起始。 chPrev = (char)ich; inRange = true; } else { reader.Unget(); } } if (!inRange) { if (ich == '-' && reader.Peek() == '[' && !escapedChar && !firstChar) { // 不在字符范围中,并且开始一个减去范围的定义, // 一般是由于减去范围紧跟着一个字符范围,例如 [a-z-[b]]。 reader.Read(); rcc.AddSubtraction(ScanCharClass(true)); if (reader.Peek() >= 0 && reader.Peek() != ']') { ThrowSubtractionMustBeLast(); } } else { rcc.AddRange((char)ich, (char)ich); } } } } reader.Drop(); // 模式字符串已到达结尾也按闭合处理。 if (!closed && closeBracket) { ThrowUnterminatedBracket(); } if (UseOptionIgnoreCase) { rcc.AddLowercase(culture); rcc.AddUppercase(culture); } return rcc; }
/// <summary> /// 扫描 '\'(不包含 '\' 本身)之后的字符,并返回相应的字符。 /// </summary> /// <param name="rcc">要添加到的字符类。</param> /// <param name="inRange">当前是否正在定义字符范围。</param> /// <returns>相应的字符,如果不是字符,则为 <c>-1</c>。</returns> private int ScanBackslash(RegexCharClass rcc, bool inRange) { // 判断 '\' 字符的转义。 int ich = reader.Read(); switch (ich) { case 'D': case 'd': if (inRange) { ThrowBadClassInCharRange((char)ich); } rcc.AddDigit(UseOptionEcma, ich == 'D', pattern); return -1; case 'S': case 's': if (inRange) { ThrowBadClassInCharRange((char)ich); } rcc.AddSpace(UseOptionEcma, ich == 'S'); return -1; case 'W': case 'w': if (inRange) { ThrowBadClassInCharRange((char)ich); } rcc.AddWord(UseOptionEcma, ich == 'W'); return -1; case 'p': case 'P': if (inRange) { ThrowBadClassInCharRange((char)ich); } rcc.AddCategoryFromName(ScanProperty(), (ich != 'p'), UseOptionIgnoreCase, pattern); return -1; default: reader.Unget(); // 读入转义字符。 return ScanCharEscape(); } }
/// <summary> /// 添加要排除的字符类。 /// </summary> /// <param name="subCharClass">要排除的字符类。</param> public void AddSubtraction(RegexCharClass subCharClass) { RccAddSubtraction.Value(charClass, subCharClass.charClass); }
/// <summary> /// 添加指定字符类。 /// </summary> /// <param name="cc">要添加的字符类。</param> public void AddCharClass(RegexCharClass cc) { // 这个方法的原版实现会直接将 cc 中的字符范围添加到当前类中, // 而不是复制副本,有时会导致出错。 if (!(cc.CanMerge && this.CanMerge)) { CompilerCommonExceptions.RegexCharClassCannotMerge("charClass"); } int ccRangeCount = RccRangeCount.Value(cc.charClass); int thisRangeCount = RccRangeCount.Value(this.charClass); if (!RccGetCanonical.Value(cc.charClass)) { // 如果要合并的字符类并不规范,则自己也不规范。 RccSetCanonical.Value(this.charClass, false); } else if (RccGetCanonical.Value(this.charClass) && thisRangeCount > 0 && ccRangeCount > 0 && RccSRGetFirst.Value(RccGetRangeAt.Value(cc.charClass, 0)) <= RccSRGetLast.Value(RccGetRangeAt.Value(this, thisRangeCount - 1))) { // Range 不能恰好接起来,那么也会导致不规范。 RccSetCanonical.Value(this.charClass, false); } IList list = RccGetRangelist.Value(this.charClass); for (int i = 0; i < ccRangeCount; i += 1) { object range = RccGetRangeAt.Value(cc.charClass, i); // 这里创建一个新的字符范围。 list.Add(RccSRConstructor.Value(RccSRGetFirst.Value(range), RccSRGetLast.Value(range))); } RccGetCategories.Value(this.charClass).Append(RccGetCategories.Value(cc.charClass).ToString()); }
/// <summary> /// 根据当前的正则表达式构造 NFA。 /// </summary> /// <param name="nfa">要构造的 NFA。</param> internal override void BuildNfa(Nfa nfa) { string str = literal; if (ignoreCase) { str = literal.ToUpper(culture); } nfa.HeadState = nfa.NewState(); nfa.TailState = nfa.HeadState; for (int i = 0; i < literal.Length; i++) { NfaState state = nfa.NewState(); if (culture == null) { // 区分大小写。 nfa.TailState.Add(state, str[i]); } else { // 不区分大小写。 RegexCharClass cc = new RegexCharClass(); cc.AddChar(str[i]); cc.AddChar(char.ToLower(str[i], culture)); nfa.TailState.Add(state, cc.ToStringClass()); } nfa.TailState = state; } }
/// <summary> /// 返回表示字符类的正则表达式。 /// </summary> /// <param name="cc">正则表达式表示的字符类。</param> /// <returns>表示字符类的正则表达式。</returns> public static Regex CharClass(RegexCharClass cc) { ExceptionHelper.CheckArgumentNull(cc, "cc"); return new CharClassExp(cc.ToStringClass()); }