c#使用regex lambda的一对多字符串解析帮助

本文关键字:字符串 帮助 一对多 使用 regex lambda | 更新日期: 2023-09-27 17:59:43

我知道如何使用string.split().的明显方法来实现这一点

我正在寻找一个更优雅、执行速度更快的代码,可能使用regex和/或linq/lambdas。

如果我的输入字符串是这样的"GradeId1:StudentName1*StudentName2*…StudentNameN,GradeId2:StudentName1*StudentName2…StudentName N,…GradeId:Student1*StudientName2*..StudentNameN"

Gist:一个连续的年级和学生串。GradeId为int,学生名称为string。分数用逗号分隔,学生姓名用星号分隔。

可能某个年级没有像"1:stud1*stud2*stud3,2,3"这里2年级和3年级没有学生。只有一年级有三名学生。我的目标是获得一个可以的收藏

foreach(Grade g in mycollection)
{ 
  foreach (int i = 0; i < g.studentnames.length; i++)
     console.writeline( g.StudentNames[i] ) 
}
class Grade { int gradeid, string[] studentnames } 

regex和linq大师,请提供建议。感谢

c#使用regex lambda的一对多字符串解析帮助

假设您的数据格式是您描述的格式,这可能是您想要的?您可能仍然应该使用String.Splitt()来处理输入,因为它是一个字符串分隔的列表,但您至少可以使它成为一个匿名类型的集合。

string input = "10:name1*20:name2*30:name3*40:name4*50:name5";
var data =
(
    from pair in input.Split( '*' )
    let student = pair.Split( ':' )
    select new { Grade = int.Parse( student[ 0 ] ), Name = student[ 1 ] }
);
foreach( var student in data )
{
    Console.WriteLine( student );
}

编辑:

你似乎有一种1:多年级->学生关系?也许你应该考虑使用Lookup集合来轻松地获得所有N级的学生。

string input = "10:name1.1*name1.2*name1.3,20:name2.1*name2.2*,30:name3.1,40:name4.1*name4.2*name4.3,50:name5.1";
var studentData = ( Lookup<int,string[]> )
(
    from 
        line in input.Split( ',' )
    where 
        line.IndexOf( ':' ) > -1
    let 
        grade = line.Substring( 0, line.IndexOf( ':' ) )
    let 
        names = line.Remove( 0, grade.Length + 1 ).Split( '*' )
    select 
        new { Grade = int.Parse( grade ), Students = names }
).ToLookup( s => s.Grade, s => s.Students );
foreach( IGrouping<int,string[]> gradeSet in studentData )
{
    Console.WriteLine( gradeSet.Key );
    Console.WriteLine( studentData[ gradeSet.Key ] );
}

此外,我意识到这不是"linqy-est"解决方案,但希望它能让你的工作更轻松。

OpticalDelusion说得对,Linq肯定会损害性能。总的来说,林克很方便,但速度不快。

Regex对于像这样复杂的字符串拆分情况下的实际解析并不有用——它对于在任意字符串中查找特定模式或将字符串列入白名单更有用。因此,如果您想确保输入字符串的格式正确,可以使用类似以下的regex模式:

"^([a-zA-Z0-9]+:[a-zA-Z0-9]+('*[a-zA-Z0-9])*)(,[a-zA-Z0-9]+:[a-zA-Z0-9]+('*[a-zA-Z0-9])?)*$"

基本上,任何字符或数字,一次或多次,然后是冒号,然后是另一个字母或数字序列,然后是"*"和另一个字符或数字序列0次或多次。然后重复0次或更多次。

一旦确保字符串的格式正确,就可以执行string.split()操作。

编辑:现在使用OP的新输入字符串。
string mystring = "GradeId1:StudentName1*StudentName2*StudentNameN,GradeId2:StudentName1*StudentName2*StudentNameN,GradeIdN:Student1*StudentName2*StudentNameN";
MatchCollection matches = 
   Regex.Matches(
      mystring,
      @"(?:GradeId('w+)(?:(?=,)|':(?:(['w ]+)(?:$|'*))*))");
var grades = matches.Cast<Match>().Select(
   gradeMatch => 
      new
      {
         Grade = gradeMatch.Groups[1].Value,
         Students = gradeMatch.Groups[2].Captures
            .Cast<Capture> ()
            .Select (c => c.Value).ToList ()
      });
foreach (var grade in grades)
{
   Console.WriteLine("Grade: " + grade.Grade);
   foreach (string student in grade.Students)
      Console.WriteLine("   " + student);
}

对于该字符串,GradeId1:StudentName1*StudentName2*StudentNameN,GradeId2,GradeIdN:Student1*StudentName2*StudentNameN产生以下输出:

Grade: 1
   StudentName1
   StudentName2
Grade: 2
Grade: N
   Student1
   StudentName2
   StudentNameN

对于感兴趣的人:

match[0].Value => GradeId1:StudentName1*StudentName2*
match[0].Groups[0].Value => GradeId1:StudentName1*StudentName2*
match[0].Groups[0].Captures[0].Value => GradeId1:StudentName1*StudentName2*
match[0].Groups[1].Value => 1
match[0].Groups[1].Captures[0].Value => 1
match[0].Groups[2].Value => StudentName2
match[0].Groups[2].Captures[0].Value => StudentName1
match[0].Groups[2].Captures[1].Value => StudentName2
match[1].Value => GradeId2
match[1].Groups[0].Value => GradeId2
match[1].Groups[0].Captures[0].Value => GradeId2
match[1].Groups[1].Value => 2
match[1].Groups[1].Captures[0].Value => 2
match[1].Groups[2].Value =>
match[2].Value => GradeIdN:Student1*StudentName2*StudentNameN
match[2].Groups[0].Value => GradeIdN:Student1*StudentName2*StudentNameN
match[2].Groups[0].Captures[0].Value => GradeIdN:Student1*StudentName2*StudentNameN
match[2].Groups[1].Value => N
match[2].Groups[1].Captures[0].Value => N
match[2].Groups[2].Value => StudentNameN
match[2].Groups[2].Captures[0].Value => Student1
match[2].Groups[2].Captures[1].Value => StudentName2
match[2].Groups[2].Captures[2].Value => StudentNameN

这里有一个使用一行(长)Linq的答案(我更喜欢直接使用扩展方法,但也可以使用短Linq语法)。我不确定使用Linq/extensions是否比使用嵌套的if等长手操作更"优雅"或更简单。我承认,林克的一个漂亮的长表情可以完成一项复杂的工作,这很酷。

string input = "1:A*B*C,2:A*B,3:B*C*D";
var grades = input
  .Split(',')
  .Select(x => x.Split(':'))
  .Select(x => x[1].Split('*').Select(n => new { GradeId = x[0], StudentName = n }))
  .SelectMany(x => x)
  .ToList();

这将为所有组合生成匿名类型的List<T>,其中包含GradeId和StudentName字段。

编辑:修改后的问题稍微简单一点。以下是如何使用以下技术根据请求获得嵌套列表:

var grades = input
  .Split(',')
  .Select(x => x.Split(':'))
  .Select(x => new { GradeId = x[0], StudentNames = x[1].Split('*').ToList() })
  .ToList();

然后你可以这样迭代:

foreach(var grade in grades)
{
  //You could always use a foreach here too
  for(int i = 0; i < grade.StudentNames.Length ; i++)
  {
    Console.WriteLine(grade.StudentNames[i]);
  }
}

你可以像这样做Linq和lambda的东西,但我认为你不会看到积极的性能差异,它将比你正常解析它更多的代码。

var grades = (from s in text select s).TakeWhile(a => !a.Equals(','));

对不起,除非你真的需要帮助,并且真的想这样做,否则我不会为你做所有的事情。

根据我的经验,String.Splitt()在大多数情况下都是可行的最佳选项。一个例外是,当您处理无法一次读取一行(或类似内容)的非常大的文本块时,使用Split()攻击它最终会将堆中塞满大型字符串数组。

在这些情况下,您可以生成枚举器块的组合。它们的内部可能是一个循环,它使用String.IndexOf()查找连续的分隔符,然后使用Substring()拉出并生成它们之间的文本。它有助于限制任何时候堆上的字符串数量,但没有将字符串视为IEnumerable(根据我的经验,这往往表现不好)。

就这一点而言,可以只使用一个类似的块,然后恢复使用String.Split()来处理其结果。