C#是否内置支持解析页码字符串?

C#是否内置支持解析页码字符串? 通过页码,我的意思是您可以输入打印对话框的格式,该对话框是逗号和划线分隔的混合。

像这样的东西:

1,3,5-10,12 

真正好的是一个解决方案,它给了我一些由字符串表示的所有页码的列表。 在上面的例子中,像这样获取一个列表会很好:

 1,3,5,6,7,8,9,10,12 

如果有一个简单的方法,我只想避免自己动手。

应该很简单:

 foreach( string s in "1,3,5-10,12".Split(',') ) { // try and get the number int num; if( int.TryParse( s, out num ) ) { yield return num; continue; // skip the rest } // otherwise we might have a range // split on the range delimiter string[] subs = s.Split('-'); int start, end; // now see if we can parse a start and end if( subs.Length > 1 && int.TryParse(subs[0], out start) && int.TryParse(subs[1], out end) && end >= start ) { // create a range between the two values int rangeLength = end - start + 1; foreach(int i in Enumerable.Range(start, rangeLength)) { yield return i; } } } 

编辑:谢谢你的修复;-)

它没有内置的方法来执行此操作,但使用String.Split会很简单。

只需拆分’,’即可获得一系列表示页码或范围的字符串。 迭代该系列并执行String.Split’ – ‘。 如果没有结果,则为普通页码,因此请将其粘贴在页面列表中。 如果有结果,请将“ – ”的左侧和右侧作为边界,并使用简单的for循环将每个页码添加到该范围内的最终列表中。

不能花5分钟做,然后可能另外10个添加一些健全性检查以在用户尝试输入无效数据时抛出错误(如“1-2-3”或其他东西。)

基思的方法似乎很好。 我使用列表组合了一种更天真的方法。 这有错误检查所以希望能解决大多数问题: –

 public List parsePageNumbers(string input) { if (string.IsNullOrEmpty(input)) throw new InvalidOperationException("Input string is empty."); var pageNos = input.Split(','); var ret = new List(); foreach(string pageString in pageNos) { if (pageString.Contains("-")) { parsePageRange(ret, pageString); } else { ret.Add(parsePageNumber(pageString)); } } ret.Sort(); return ret.Distinct().ToList(); } private int parsePageNumber(string pageString) { int ret; if (!int.TryParse(pageString, out ret)) { throw new InvalidOperationException( string.Format("Page number '{0}' is not valid.", pageString)); } return ret; } private void parsePageRange(List pageNumbers, string pageNo) { var pageRange = pageNo.Split('-'); if (pageRange.Length != 2) throw new InvalidOperationException( string.Format("Page range '{0}' is not valid.", pageNo)); int startPage = parsePageNumber(pageRange[0]), endPage = parsePageNumber(pageRange[1]); if (startPage > endPage) { throw new InvalidOperationException( string.Format("Page number {0} is greater than page number {1}" + " in page range '{2}'", startPage, endPage, pageNo)); } pageNumbers.AddRange(Enumerable.Range(startPage, endPage - startPage + 1)); } 

下面是我刚刚拼凑起来的代码..你可以输入的格式如.. 1-2,5abcd,6,7,20-15 ,,,,,,

易于添加其他格式

  private int[] ParseRange(string ranges) { string[] groups = ranges.Split(','); return groups.SelectMany(t => GetRangeNumbers(t)).ToArray(); } private int[] GetRangeNumbers(string range) { //string justNumbers = new String(text.Where(Char.IsDigit).ToArray()); int[] RangeNums = range .Split('-') .Select(t => new String(t.Where(Char.IsDigit).ToArray())) // Digits Only .Where(t => !string.IsNullOrWhiteSpace(t)) // Only if has a value .Select(t => int.Parse(t)) // digit to int .ToArray(); return RangeNums.Length.Equals(2) ? Enumerable.Range(RangeNums.Min(), (RangeNums.Max() + 1) - RangeNums.Min()).ToArray() : RangeNums; } 

这是我为类似的东西做饭的东西。

它处理以下类型的范围:

 1 single number 1-5 range -5 range from (firstpage) up to 5 5- range from 5 up to (lastpage) .. can use .. instead of - ;, can use both semicolon, comma, and space, as separators 

它不检查重复值,因此集合1,5,-10将产生序列1,5,1,2,3,4,5,6,7,8,9,10

 public class RangeParser { public static IEnumerable Parse(String s, Int32 firstPage, Int32 lastPage) { String[] parts = s.Split(' ', ';', ','); Regex reRange = new Regex(@"^\s*((?\d+)|(?\d+)(?(-|\.\.))(?\d+)|(?(-|\.\.))(?\d+)|(?\d+)(?(-|\.\.)))\s*$"); foreach (String part in parts) { Match maRange = reRange.Match(part); if (maRange.Success) { Group gFrom = maRange.Groups["from"]; Group gTo = maRange.Groups["to"]; Group gSep = maRange.Groups["sep"]; if (gSep.Success) { Int32 from = firstPage; Int32 to = lastPage; if (gFrom.Success) from = Int32.Parse(gFrom.Value); if (gTo.Success) to = Int32.Parse(gTo.Value); for (Int32 page = from; page <= to; page++) yield return page; } else yield return Int32.Parse(gFrom.Value); } } } } 

在你有测试用例之前你不能确定。 在我的情况下,我宁愿是空格分隔而不是逗号分隔。 它使解析更复杂一些。

  [Fact] public void ShouldBeAbleToParseRanges() { RangeParser.Parse( "1" ).Should().BeEquivalentTo( 1 ); RangeParser.Parse( "-1..2" ).Should().BeEquivalentTo( -1,0,1,2 ); RangeParser.Parse( "-1..2 " ).Should().BeEquivalentTo( -1,0,1,2 ); RangeParser.Parse( "-1..2 5" ).Should().BeEquivalentTo( -1,0,1,2,5 ); RangeParser.Parse( " -1 .. 2 5" ).Should().BeEquivalentTo( -1,0,1,2,5 ); } 

请注意,Keith的答案(或小变化)将在范围令牌之间存在空格的最后一次测试中失败。 这需要一个标记器和一个具有前瞻性的适当解析器。

 namespace Utils { public class RangeParser { public class RangeToken { public string Name; public string Value; } public static IEnumerable Tokenize(string v) { var pattern = @"(?-?[1-9]+[0-9]*)|" + @"(?\.\.)"; var regex = new Regex( pattern ); var matches = regex.Matches( v ); foreach (Match match in matches) { var numberGroup = match.Groups["number"]; if (numberGroup.Success) { yield return new RangeToken {Name = "number", Value = numberGroup.Value}; continue; } var rangeGroup = match.Groups["range"]; if (rangeGroup.Success) { yield return new RangeToken {Name = "range", Value = rangeGroup.Value}; } } } public enum State { Start, Unknown, InRange} public static IEnumerable Parse(string v) { var tokens = Tokenize( v ); var state = State.Start; var number = 0; foreach (var token in tokens) { switch (token.Name) { case "number": var nextNumber = int.Parse( token.Value ); switch (state) { case State.Start: number = nextNumber; state = State.Unknown; break; case State.Unknown: yield return number; number = nextNumber; break; case State.InRange: int rangeLength = nextNumber - number+ 1; foreach (int i in Enumerable.Range( number, rangeLength )) { yield return i; } state = State.Start; break; default: throw new ArgumentOutOfRangeException(); } break; case "range": switch (state) { case State.Start: throw new ArgumentOutOfRangeException(); break; case State.Unknown: state = State.InRange; break; case State.InRange: throw new ArgumentOutOfRangeException(); break; default: throw new ArgumentOutOfRangeException(); } break; default: throw new ArgumentOutOfRangeException( nameof( token ) ); } } switch (state) { case State.Start: break; case State.Unknown: yield return number; break; case State.InRange: break; default: throw new ArgumentOutOfRangeException(); } } } } 

这是一个稍微修改过的lassevk代码版本,用于处理Regex匹配中的string.Split操作。 它是作为扩展方法编写的,您可以使用LINQ的Disinct()扩展轻松处理重复问题。

  ///  /// Parses a string representing a range of values into a sequence of integers. ///  /// String to parse /// Minimum value for open range specifier /// Maximum value for open range specifier /// An enumerable sequence of integers ///  /// The range is specified as a string in the following forms or combination thereof: /// 5 single value /// 1,2,3,4,5 sequence of values /// 1-5 closed range /// -5 open range (converted to a sequence from minValue to 5) /// 1- open range (converted to a sequence from 1 to maxValue) /// /// The value delimiter can be either ',' or ';' and the range separator can be /// either '-' or ':'. Whitespace is permitted at any point in the input. /// /// Any elements of the sequence that contain non-digit, non-whitespace, or non-separator /// characters or that are empty are ignored and not returned in the output sequence. ///  public static IEnumerable ParseRange2(this string s, int minValue, int maxValue) { const string pattern = @"(?:^|(?<=[,;])) # match must begin with start of string or delim, where delim is , or ; \s*( # leading whitespace (?\d*)\s*(?:-|:)\s*(?\d+) # capture 'from  to' or ' to', where  is - or : | # or (?\d+)\s*(?:-|:)\s*(?\d*) # capture 'from  to' or 'from ', where  is - or : | # or (?\d+) # capture lone number )\s* # trailing whitespace (?:(?=[,;\b])|$) # match must end with end of string or delim, where delim is , or ;"; Regex regx = new Regex(pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); foreach (Match m in regx.Matches(s)) { Group gpNum = m.Groups["num"]; if (gpNum.Success) { yield return int.Parse(gpNum.Value); } else { Group gpFrom = m.Groups["from"]; Group gpTo = m.Groups["to"]; if (gpFrom.Success || gpTo.Success) { int from = (gpFrom.Success && gpFrom.Value.Length > 0 ? int.Parse(gpFrom.Value) : minValue); int to = (gpTo.Success && gpTo.Value.Length > 0 ? int.Parse(gpTo.Value) : maxValue); for (int i = from; i <= to; i++) { yield return i; } } } } } 

我想出的答案:

 static IEnumerable ParseRange(string str) { var numbers = str.Split(','); foreach (var n in numbers) { if (!n.Contains("-")) yield return n; else { string startStr = String.Join("", n.TakeWhile(c => c != '-')); int startInt = Int32.Parse(startStr); string endStr = String.Join("", n.Reverse().TakeWhile(c => c != '-').Reverse()); int endInt = Int32.Parse(endStr); var range = Enumerable.Range(startInt, endInt - startInt + 1) .Select(num => num.ToString()); foreach (var s in range) yield return s; } } } 

正则表达式效率不如以下代码。 字符串方法比Regex更有效,应尽可能使用。

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { string[] inputs = { "001-005/015", "009/015" }; foreach (string input in inputs) { List numbers = new List(); string[] strNums = input.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); foreach (string strNum in strNums) { if (strNum.Contains("-")) { int startNum = int.Parse(strNum.Substring(0, strNum.IndexOf("-"))); int endNum = int.Parse(strNum.Substring(strNum.IndexOf("-") + 1)); for (int i = startNum; i <= endNum; i++) { numbers.Add(i); } } else numbers.Add(int.Parse(strNum)); } Console.WriteLine(string.Join(",", numbers.Select(x => x.ToString()))); } Console.ReadLine(); } } } 

SplitLinq一线方法

 string input = "1,3,5-10,12"; IEnumerable result = input.Split(',').SelectMany(x => x.Contains('-') ? Enumerable.Range(int.Parse(x.Split('-')[0]), int.Parse(x.Split('-')[1]) - int.Parse(x.Split('-')[0]) + 1) : new int[] { int.Parse(x) }); 

我的解决方案

  • 返回整数列表
  • 反转/错字/重复可能:1,-3,5-,7-10,12-9 => 1,3,5,7,8,9,10,12,11,10,9(当你想要时使用提取,重复页面)
  • 设置页面总数的选项:1,-3,5-,7-10,12-9(Nmax = 9)=> 1,3,5,7,8,9,9
  • 自动完成:1,-3,5-,8(Nmax = 9)=> 1,3,5,6,7,8,9,8

      public static List pageRangeToList(string pageRg, int Nmax = 0) { List ls = new List(); int lb,ub,i; foreach (string ss in pageRg.Split(',')) { if(int.TryParse(ss,out lb)){ ls.Add(Math.Abs(lb)); } else { var subls = ss.Split('-').ToList(); lb = (int.TryParse(subls[0],out i)) ? i : 0; ub = (int.TryParse(subls[1],out i)) ? i : Nmax; ub = ub > 0 ? ub : lb; // if ub=0, take 1 value of lb for(i=0;i<=Math.Abs(ub-lb);i++) ls.Add(lb 0 ? Nmax : ls.Max(); // real Nmax return ls.Where(s => s>0 && s<=Nmax).ToList(); }