我把这个字符串扩展写了一段时间,我实际上已经得到了很多用处。
public static string Slice(this string str, int? start = null, int? end = null, int step = 1)
{
if (step == 0) throw new ArgumentException("Step cannot be zero.", "step");
if (start == null)
{
if (step > 0) start = 0;
else start = str.Length - 1;
}
else if (start < 0)
{
if (start < -str.Length) start = 0;
else start += str.Length;
}
else if (start > str.Length) start = str.Length;
if (end == null)
{
if (step > 0) end = str.Length;
else end = -1;
}
else if (end < 0)
{
if (end < -str.Length) end = 0;
else end += str.Length;
}
else if (end > str.Length) end = str.Length;
if (start == end || start < end && step < 0 || start > end && step > 0) return "";
if (start < end && step == 1) return str.Substring((int)start, (int)(end - start));
int length = (int)(((end - start) / (float)step) + 0.5f);
var sb = new StringBuilder(length);
for (int i = (int)start, j = 0; j < length; i += step, ++j)
sb.Append(str[i]);
return sb.ToString();
}
因为它现在在我的所有项目中,我想知道我是否可以做得更好。效率更高,或者在任何情况下都会产生意想不到的结果?
切片。它的工作方式类似于Python的数组符号。
"string"[start:end:step]
许多其他语言也有类似的东西。 string.Slice(1)
相当于string.Substring(1)
。 string.Substring(1,-1)
修剪了第一个和最后一个字符。 string.Substring(null,null,-1)
将反转字符串。 string.Substring(step:2)
将返回一个包含其他所有字符的字符串...也类似于JS's slice,但还有一个额外的arg。
根据您的建议重新修改:
public static string Slice(this string str, int? start = null, int? end = null, int step = 1)
{
if (step == 0) throw new ArgumentException("Step size cannot be zero.", "step");
if (start == null) start = step > 0 ? 0 : str.Length - 1;
else if (start < 0) start = start < -str.Length ? 0 : str.Length + start;
else if (start > str.Length) start = str.Length;
if (end == null) end = step > 0 ? str.Length : -1;
else if (end < 0) end = end < -str.Length ? 0 : str.Length + end;
else if (end > str.Length) end = str.Length;
if (start == end || start < end && step < 0 || start > end && step > 0) return "";
if (start < end && step == 1) return str.Substring(start.Value, end.Value - start.Value);
var sb = new StringBuilder((int)Math.Ceiling((end - start).Value / (float)step));
for (int i = start.Value; step > 0 && i < end || step < 0 && i > end; i += step)
sb.Append(str[i]);
return sb.ToString();
}
答案 0 :(得分:2)
如果您有足够的测试用例,那么如果您想尝试不同的实现,那么检测意外结果应该不是问题。
从API的角度来看,我会考虑optional arguments而不是可以为空的内容。
<强>更新强>
仔细阅读代码后,我可以看到将“start”和“end”赋值为null,在考虑“step”时具有特殊含义,因此,它们不能单独表示为可选的int参数但是,它们仍然可以作为可选参数。
在更仔细地查看代码之后,由于各个参数的值相互影响,因此它有点像一个时髦的API。我之前的评论暗示了这一点。你真的必须知道实现它的实现,通常不是一个好的API方面。并且可能会带来难以阅读的体验。
我可以看到“step”如何用于反转字符串,这可能是有用的。但是反向扩展方法不会更好吗?更具可读性,更少精神速度爆发。
答案 1 :(得分:1)
我可以看到3件事,非常非常轻微的
将内部if更改为三元,如
if (start == null)
{
start = step > 0 ? 0 : str.Length - 1;
}
else if (start < 0)
{
start = start < -str.Length ? 0 : str.Length + start;
}
else if (start > str.Length)
start = str.Length;
可能会将(int)int?更改为 int.Value
更改
var sb = new StringBuilder(length);
到
StringBuilder sb = new StringBuilder(length);
最重要的问题是if it does what it need, why fixing it?
更新以显示如何使用LINQ,方式更慢(有没有办法加快速度?)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Stopwatch sw;
string str;
sw = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
str = "Step cannot be zero.".Slice(null, null, -3, true);
sw.Stop();
Console.WriteLine("LINQ " + sw.Elapsed.TotalSeconds.ToString("0.#######") + " seconds");
sw = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
str = "Step cannot be zero.".Slice(null, null, -3, false);
sw.Stop();
Console.WriteLine("MANUAL " + sw.Elapsed.TotalSeconds.ToString("0.#######") + " seconds");
Console.ReadLine();
}
}
static class test
{
public static string Slice(this string str, int? start, int? end, int step, bool linq)
{
if (step == 0) throw new ArgumentException("Step cannot be zero.", "step");
if (linq)
{
if (start == null) start = 0;
else if (start > str.Length) start = str.Length;
if (end == null) end = str.Length;
else if (end > str.Length) end = str.Length;
if (step < 0)
{
str = new string(str.Reverse().ToArray());
step = Math.Abs(step);
}
}
else
{
if (start == null)
{
if (step > 0) start = 0;
else start = str.Length - 1;
}
else if (start < 0)
{
if (start < -str.Length) start = 0;
else start += str.Length;
}
else if (start > str.Length) start = str.Length;
if (end == null)
{
if (step > 0) end = str.Length;
else end = -1;
}
else if (end < 0)
{
if (end < -str.Length) end = 0;
else end += str.Length;
}
else if (end > str.Length) end = str.Length;
}
if (start == end || start < end && step < 0 || start > end && step > 0) return "";
if (start < end && step == 1) return str.Substring(start.Value, end.Value - start.Value);
if (linq)
{
return new string(str.Skip(start.Value).Take(end.Value - start.Value).Where((s, index) => index % step == 0).ToArray ());;
}
else
{
int length = (int)(((end.Value - start.Value) / (float)step) + 0.5f);
var sb = new StringBuilder(length);
for (int i = start.Value, j = 0; j < length; i += step, ++j)
sb.Append(str[i]);
return sb.ToString();
}
}
}
}
答案 2 :(得分:1)
当我向Python询问"abcdefghijklmn"[::6]
时,它会返回'agm'
,但当我向您的函数询问"abcdefghijklmn".Slice(step:6)
时,它会返回"ag"
。
我建议删除不正确的length
计算,然后像这样执行循环:
var sb = new StringBuilder((end - start).Value / step);
for (int i = start.Value; step > 0 && i < end || step < 0 && i > end; i += step)
sb.Append(str[i]);