使用 LINQ 将字符串拆分为 N 长度字符串的列表
本文关键字:字符串 列表 LINQ 拆分 使用 | 更新日期: 2023-09-27 18:27:01
我知道String.Split的概念以前已经用许多不同的方法解决了,但我对这个问题的LINQ解决方案特别感兴趣。
我尝试编写一个扩展类来处理拆分,但两次尝试都存在一些主要问题。因此,对于以下内容:
string s = "ABCDEFGHIJKLMNOPQRSTUVWX";
var results = s.SplitEvery(4);
我想要一个这样的列表:{ "ABCD", "EFGH", "IJKL", "MNOP", "QRST", "UVWX" }
这是我的扩展类:
public static class Extensions
{
public static List<string> SplitEvery(this string s, int n)
{
List<string> list = new List<string>();
var Attempt1 = s.Select((c, i) => i % n== 0 ? s.Substring(i, n) : "|").Where(x => x != "|").ToList();
var Attempt2 = s.Where((c, i) => i % n== 0).Select((c, i) => s.Substring(i, n)).ToList();
return list;
}
}
每次不满足条件时,尝试 1 都会插入虚拟字符串"|",然后删除虚拟字符串的所有实例以创建最终列表。它可以工作,但创建坏字符串似乎是一个不必要的额外步骤。此外,如果字符串不能被 n 整除,则此尝试将失败。
尝试2 是我尝试仅选择索引可被 N 整除的子字符串,但 Select 语句中的"i"值与 Where 语句中的"i"值不对应,因此我得到的结果如下:{ "ABCD"、"BCDE"等... }
我觉得我接近一个好的解决方案,但可以在正确的方向上使用有用的推动。有什么建议吗?
[编辑]
我最终提出了一系列建议来处理我的字符串拆分器。它可能不是最快的,但作为 LINQ 的新手,这个实现对我来说是最简洁、最容易理解的。
public static List<string> SplitEvery(this string s, int size)
{
return s.Select((x, i) => i)
.Where(i => i % size == 0)
.Select(i => String.Concat(s.Skip(i).Take(size))).ToList();
}
感谢您的所有优秀建议。
string s = "ABCDEFGHIJKLMNOPQRSTUVWX";
var results = s.Select((c, i) => new { c, i })
.GroupBy(x => x.i / 4)
.Select(g => String.Join("",g.Select(y=>y.c)))
.ToList();
您也可以使用morelinq的批处理
var res = s.Batch(4).Select(x => String.Join("", x)).ToList();
如果您不介意使用副作用,这也是可能的
var res2 = s.SplitEvery(4).ToList();
public static IEnumerable<string> SplitEvery(this string s, int n)
{
int index = 0;
return s.GroupBy(_=> index++/n).Select(g => new string(g.ToArray()));
}
当然,每个字符串操作问题都应该有一个正则表达式答案:)
var res3 = Regex.Split(s, @"(?<='G.{4})");
这是另一种解决方案:
var result = s.Select((x, i) => i)
.Where(i => i % 4 == 0)
.Select(i => s.Substring(i, s.Length - i >= 4 ? 4 : s.Length - i));
此扩展方法,该方法通过简单的子字符串获取实现(我相信它比枚举字符并将它们连接成字符串更快(:
public static IEnumerable<string> SplitEvery(this string s, int length)
{
int index = 0;
while (index + length < s.Length)
{
yield return s.Substring(index, length);
index += length;
}
if (index < s.Length)
yield return s.Substring(index, s.Length - index);
}
public static IEnumerable<string> SplitEvery(this string s, int length)
{
return s.Where((c, index) => index % length == 0)
.Select((c, index) => String.Concat(
s.Skip(index * length).Take(length)
)
);
}
陪审团不知道new String(chars.ToArray())
会比String.Concat(chars)
更快还是更慢。
当然,您可以附加一个.ToList()
来返回列表而不是IEnumerable
。
Substring
选择字符串的 4 个字符部分应该没问题。你只需要小心最后一部分:
new Func<string, int, IEnumerable<string>>(
(string s, int n) =>
Enumerable.Range(0, (s.Length + n-1)/n)
.Select(i => s.Substring(i*n, Math.Min(n, s.Length - i*n))))
("ABCDEFGHIJKLMNOPQRSTUVWX", 4)
注意:如果将此答案转换为通用枚举的操作,则必须多次迭代集合(Count()
并Substring
转换为Skip(i*n).Take(n)
(。
似乎有效:
public static IEnumerable<string> SplitEvery(this string s, int n) {
var enumerators = Enumerable.Repeat(s.GetEnumerator(), n);
while (true) {
var chunk = string.Concat(enumerators
.Where(e => e.MoveNext())
.Select(e => e.Current));
if (chunk == "") yield break;
yield return chunk;
}
}
以下是几种 LINQy 方法:
public static IEnumerable<string> SplitEvery( this IEnumerable<char> s , int n )
{
StringBuilder sb = new StringBuilder(n) ;
foreach ( char c in s )
{
if ( sb.Length == n )
{
yield return sb.ToString() ;
sb.Length = 0 ;
}
sb.Append(c) ;
}
}
或
public static IEnumerable<string> SplitEvery( this string s , int n )
{
int limit = s.Length - ( s.Length % n ) ;
int i = 0 ;
while ( i < limit )
{
yield return s.Substring(i,n) ;
i+=n ;
}
if ( i < s.Length )
{
yield return s.Substring(i) ;
}
}
这也有效,但需要"解开"一个IGrouping<x,y>
:
public static IEnumerable<String> Split(this String me,int SIZE) {
//Works by mapping the character index to a 'modulo Staircase'
//and then grouping by that 'stair step' value
return me.Select((c, i) => new {
step = i - i % SIZE,
letter = c.ToString()
})
.GroupBy(kvp => kvp.step)
.Select(grouping => grouping
.Select(g => g.letter)
.Aggregate((a, b) => a + b)
);
}
编辑:使用 LINQ 的惰性求值机制 ( yield return
( 你也可以使用递归来实现这一点
public static IEnumerable<String> Split(this String me, int SIZE) {
if (me.Length > SIZE) {
var head = me.Substring(0,SIZE);
var tail = me.Substring(SIZE,me.Length-SIZE);
yield return head;
foreach (var item in tail.Split(SIZE)) {
yield return item;
}
} else {
yield return me;
}
}
虽然,就我个人而言,我远离Substring
因为它鼓励有状态的代码(父或全局范围内的计数器、索引等(。