当最后一个值为空时,String.Split 工作很奇怪 [英] String.Split works strange when last value is empty

查看:28
本文介绍了当最后一个值为空时,String.Split 工作很奇怪的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将字符串拆分为数组,但是当最后一个值"为空时效果不佳.请看我的例子.它是错误还是功能?有没有办法在没有变通方法的情况下使用此功能?

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

这种不对称明显表明设计不佳.令人惊讶的是,测试没有发现这一点.

设计如此明显可疑的事实意味着提交错误报告可能是值得的.我希望它会被拒绝,因为任何更改都会影响现有代码.但你永远不知道.

我的建议:

  1. 使用您自己的拆分实现来满足您的需求.
  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我的分裂:1428

I'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:

  1. Use your own split implementation that performs as you require.
  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆