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大师,请提供建议。感谢
假设您的数据格式是您描述的格式,这可能是您想要的?您可能仍然应该使用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()操作。
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()来处理其结果。