当最后一个值为空时,String.Split 工作很奇怪 [英] String.Split works strange when last value is empty
问题描述
我想将字符串拆分为数组,但是当最后一个值"为空时效果不佳.请看我的例子.它是错误还是功能?有没有办法在没有变通方法的情况下使用此功能?
vararr: TArray;arr:='a;b;c'.Split([';']);//数组长度=3,就可以了arr:='a;b;c;'.Split([';']);//数组的长度= 3,但我希望是4arr:='a;b;;c'.Split([';']);//数组长度=4,因为里面有空值arr:=('a;b;c;'+' ').Split([';']);//数组的长度= 4(原始的空间解决方法)
此行为无法更改.您无法自定义此拆分功能的工作方式.我怀疑您需要提供自己的拆分实现.Michael Erikkson 在评论中帮助指出 System.StrUtils.SplitString
以您希望的方式行事.
在我看来,设计很差.例如
Length('a;'.Split([';'])) = 1
还有
Length(';a'.Split([';'])) = 2
这种不对称明显表明设计不佳.令人惊讶的是,测试没有发现这一点.
设计如此明显可疑的事实意味着提交错误报告可能是值得的.我希望它会被拒绝,因为任何更改都会影响现有代码.但你永远不知道.
我的建议:
- 使用您自己的拆分实现来满足您的需求.
- 提交错误报告.
<小时>
虽然 System.StrUtils.SplitString
可以满足您的需求,但它的性能并不是很好.这很可能无关紧要.在这种情况下,您应该使用它.但是,如果性能很重要,那么我提供:
{$APPTYPE 控制台}用途System.SysUtils, System.Diagnostics, System.StrUtils;function MySplit(const s: string; Separator: char): TArray;无功i、ItemIndex:整数;len:整数;SeparatorCount:整数;开始:整数;开始len := 长度;如果 len=0 然后开始结果:=零;出口;结尾;分隔符计数:= 0;对于 i := 1 到 len 开始如果 s[i]=Separator 然后开始公司(SeparatorCount);结尾;结尾;SetLength(Result, SeparatorCount+1);项目索引:= 0;开始:= 1;对于 i := 1 到 len 开始如果 s[i]=Separator 然后开始结果[ItemIndex] := Copy(s, Start, i-Start);公司(项目索引);开始:= i+1;结尾;结尾;结果[ItemIndex] := Copy(s, Start, len-Start+1);结尾;常量InputString = 'asdkjhasd,we1324,wqweqw,qweqlkjh,asdqwe,qweqwe,asdasdqw';无功i:整数;秒表:T秒表;常量计数 = 3000000;开始秒表:= TStopwatch.StartNew;for i := 1 to Count do beginInputString.Split([',']);结尾;Writeln('string.Split: ', Stopwatch.ElapsedMilliseconds);秒表:= TStopwatch.StartNew;for i := 1 to Count do beginSystem.StrUtils.SplitString(InputString, ',');结尾;Writeln('StrUtils.SplitString: ', Stopwatch.ElapsedMilliseconds);秒表:= TStopwatch.StartNew;for i := 1 to Count do beginMySplit(InputString, ',');结尾;Writeln('MySplit: ', Stopwatch.ElapsedMilliseconds);结尾.
在我的 E5530 上使用 XE7 构建的 32 位版本的输出是:
<前>字符串.拆分:2798StrUtils.SplitString: 7167我的分裂:1428I'd like to split my string to array but it works bad when last "value" is empty. See my example please. Is it bug or feature? Is there any way how to use this function without workarounds?
var
arr: TArray<string>;
arr:='a;b;c'.Split([';']); //length of array = 3, it's OK
arr:='a;b;c;'.Split([';']); //length of array = 3, but I expect 4
arr:='a;b;;c'.Split([';']); //length of array = 4 since empty value is inside
arr:=('a;b;c;'+' ').Split([';']); //length of array = 4 (primitive workaround with space)
This behaviour can't be changed. There's no way for you to customise how this split function works. I suspect that you'll need to provide your own split implementation. Michael Erikkson helpfully points out in a comment that System.StrUtils.SplitString
behaves in the manner that you desire.
The design seems to me to be poor. For instance
Length('a;'.Split([';'])) = 1
and yet
Length(';a'.Split([';'])) = 2
This asymmetry is a clear indication of poor design. It's astonishing that testing did not identify this.
The fact that the design is so clearly suspect means that it may be worth submitting a bug report. I'd expect it to be denied since any change would impact existing code. But you never know.
My recommendations:
- Use your own split implementation that performs as you require.
- Submit a bug report.
Whilst System.StrUtils.SplitString
does what you want, its performance is not great. That very likely does not matter. In which case you should use it. However, if performance matters, then I offer this:
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Diagnostics, System.StrUtils;
function MySplit(const s: string; Separator: char): TArray<string>;
var
i, ItemIndex: Integer;
len: Integer;
SeparatorCount: Integer;
Start: Integer;
begin
len := Length(s);
if len=0 then begin
Result := nil;
exit;
end;
SeparatorCount := 0;
for i := 1 to len do begin
if s[i]=Separator then begin
inc(SeparatorCount);
end;
end;
SetLength(Result, SeparatorCount+1);
ItemIndex := 0;
Start := 1;
for i := 1 to len do begin
if s[i]=Separator then begin
Result[ItemIndex] := Copy(s, Start, i-Start);
inc(ItemIndex);
Start := i+1;
end;
end;
Result[ItemIndex] := Copy(s, Start, len-Start+1);
end;
const
InputString = 'asdkjhasd,we1324,wqweqw,qweqlkjh,asdqwe,qweqwe,asdasdqw';
var
i: Integer;
Stopwatch: TStopwatch;
const
Count = 3000000;
begin
Stopwatch := TStopwatch.StartNew;
for i := 1 to Count do begin
InputString.Split([',']);
end;
Writeln('string.Split: ', Stopwatch.ElapsedMilliseconds);
Stopwatch := TStopwatch.StartNew;
for i := 1 to Count do begin
System.StrUtils.SplitString(InputString, ',');
end;
Writeln('StrUtils.SplitString: ', Stopwatch.ElapsedMilliseconds);
Stopwatch := TStopwatch.StartNew;
for i := 1 to Count do begin
MySplit(InputString, ',');
end;
Writeln('MySplit: ', Stopwatch.ElapsedMilliseconds);
end.
The output of a 32 bit release build with XE7 on my E5530 is:
string.Split: 2798 StrUtils.SplitString: 7167 MySplit: 1428
这篇关于当最后一个值为空时,String.Split 工作很奇怪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!