我需要从一个字符串中提取数字并将它们放入列表中,但是有一些规则需要遵守,例如确定提取的数字是整数还是浮点数。
这个任务听起来很简单,但随着时间的推移,我越来越感到困惑,真的需要一些指导。
以下是一个测试字符串的例子:
There are test values: P7 45.826.53.91.7, .5, 66.. 4 and 5.40.3.
解析字符串时应遵循以下规则:
数字前不能有字母。
如果找到一个数字,且后面没有小数点,则该数字为整数。
如果找到一个数字,并有小数点,则该数字为浮点数,例如5.
~ 如果小数点后面跟着更多数字,则该数字仍为浮点数,例如5.40
~ 如果发现另一个小数点,则将该数字分成两个部分,例如5.40.3会变为(5.40 Float)和(3 Float)
如果在小数点后面出现字母,例如
3.H
,则仍将3.
添加到列表中作为浮点数(即使从技术上讲它不是有效的)
示例1
为了使这个规则更加清晰,以引用的测试字符串为例,期望输出如下:
从上面的图片可以看到,浅蓝色表示浮点数,浅红色表示单个整数(注意连在一起的浮点数是如何分开成单独的浮点数的)。
- 45.826 (Float)
- 53.91 (Float)
- 7 (Integer)
- 5 (Integer)
- 66 . (Float)
- 4 (Integer)
- 5.40 (Float)
- 3 . (Float)
请注意,由于数字的格式化方式不同,66 .和3 .之间有故意添加的空格。
示例2:
Anoth3r Te5.t string .4 abc 8.1Q 123.45.67.8.9
- 4 (Integer)
- 8.1 (Float)
- 123.45 (Float)
- 67.8 (Float)
- 9 (Integer)
为了更好地理解,我创建了一个新项目进行测试,如下所示:
接下来进入实际任务。我想也许我可以从字符串中读取每个字符,并根据上述规则识别出有效的数字,然后将它们放入一个列表中。
就我目前的能力而言,以下是我最好的努力:
代码如下:
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
btnParseString: TButton;
edtTestString: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
lstDesiredOutput: TListBox;
lstActualOutput: TListBox;
procedure btnParseStringClick(Sender: TObject);
private
FDone: Boolean;
FIdx: Integer;
procedure ParseString(const Str: string; var OutValue, OutKind: string);
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.ParseString(const Str: string; var OutValue, OutKind: string);
var
CH1, CH2: Char;
begin
Inc(FIdx);
CH1 := Str[FIdx];
case CH1 of
'0'..'9': // Found a number
begin
CH2 := Str[FIdx - 1];
if not (CH2 in ['A'..'Z']) then
begin
OutKind := 'Integer';
// Try to determine float...
//while (CH1 in ['0'..'9', '.']) do
//begin
// case Str[FIdx] of
// '.':
// begin
// CH2 := Str[FIdx + 1];
// if not (CH2 in ['0'..'9']) then
// begin
// OutKind := 'Float';
// //Inc(FIdx);
// end;
// end;
// end;
//end;
end;
OutValue := Str[FIdx];
end;
end;
FDone := FIdx = Length(Str);
end;
procedure TForm1.btnParseStringClick(Sender: TObject);
var
S, SKind: string;
begin
lstActualOutput.Items.Clear;
FDone := False;
FIdx := 0;
repeat
ParseString(edtTestString.Text, S, SKind);
if (S <> '') and (SKind <> '') then
begin
lstActualOutput.Items.Add(S + ' (' + SKind + ')');
end;
until
FDone = True;
end;
end.
明显地,它不能给出期望的输出(已标记失败代码),而我的方法可能是错误的,但我感觉只需要在这里做一些改变就能得到可行的解决方案。
此时,尽管认为答案很接近,但我发现自己相当困惑和迷失,任务变得越来越令人恼火,我真的会非常感激一些帮助。
编辑1
在这里,我更接近了一点,因为不再有重复的数字了,但结果仍然明显错误。
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
btnParseString: TButton;
edtTestString: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
lstDesiredOutput: TListBox;
lstActualOutput: TListBox;
procedure btnParseStringClick(Sender: TObject);
private
FDone: Boolean;
FIdx: Integer;
procedure ParseString(const Str: string; var OutValue, OutKind: string);
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
// Prepare to pull hair out!
procedure TForm1.ParseString(const Str: string; var OutValue, OutKind: string);
var
CH1, CH2: Char;
begin
Inc(FIdx);
CH1 := Str[FIdx];
case CH1 of
'0'..'9': // Found the start of a new number
begin
CH1 := Str[FIdx];
// make sure previous character is not a letter
CH2 := Str[FIdx - 1];
if not (CH2 in ['A'..'Z']) then
begin
OutKind := 'Integer';
// Try to determine float...
//while (CH1 in ['0'..'9', '.']) do
//begin
// OutKind := 'Float';
// case Str[FIdx] of
// '.':
// begin
// CH2 := Str[FIdx + 1];
// if not (CH2 in ['0'..'9']) then
// begin
// OutKind := 'Float';
// Break;
// end;
// end;
// end;
// Inc(FIdx);
// CH1 := Str[FIdx];
//end;
end;
OutValue := Str[FIdx];
end;
end;
OutValue := Str[FIdx];
FDone := Str[FIdx] = #0;
end;
procedure TForm1.btnParseStringClick(Sender: TObject);
var
S, SKind: string;
begin
lstActualOutput.Items.Clear;
FDone := False;
FIdx := 0;
repeat
ParseString(edtTestString.Text, S, SKind);
if (S <> '') and (SKind <> '') then
begin
lstActualOutput.Items.Add(S + ' (' + SKind + ')');
end;
until
FDone = True;
end;
end.
我的问题是如何从字符串中提取数字,将它们添加到列表中并确定数字是整数还是浮点数?左侧淡绿色列表框(期望输出)显示了结果应该是什么,右侧淡蓝色列表框(实际输出)显示了实际获得的内容。
请给予建议。谢谢。
注意,我重新添加了Delphi标签,因为我使用XE7,请不要删除它,尽管这个特定的问题在Lazarus中,但我的最终解决方案应该适用于XE7和Lazarus。