不能再简单的字符串处理问题(50分)

  • 主题发起人 主题发起人 m911
  • 开始时间 开始时间
M

m911

Unregistered / Unconfirmed
GUEST, unregistred user!
有这样一组字符串
40 00 15 00 00 00 00 00 DB D4 6D 31 32 3A 3A 3A 3A 3A 00 E4 E9 12 00 5C DD 83 7C 3F FF 4D 30 30 30 30 30 00 E9 12 00 6D 05 93 7C 38 2C 90 02 38 2C 90 02 35 2E 30 2E 30 2E 33 00 01 00 00 00 2C
我想将 D4 6D 与 随后的 00 之间的内容存为变量AA
DD 83 与 随后的 00 之间的内容存为变量BB
6D 05 与 随后的 90 02 之间的内容存为变量 CC
最好的处理方法是什么?

我是新手 麻烦贴代码 谢谢
 
.........................

AA := 'D4 6D 31 32 3A 3A 3A 3A 3A 00';
BB := '05 93 7C 38 2C 90 02 38 2C 90 02 35 2E 30 2E 30 2E 33 00'; //串中没有0D 93, 估计是05 93
CC := '6D 05 93 7C 38 2C 90 02 38 2C 90 02';
 
kidneyball, 大哥 先谢谢你回答我上一个问题

这个问题是 取指定字符之间的内容
比如 D4 6D 与 随后的 00 之间的内容 取出来后就是
31 32 3A 3A 3A 3A 3A

我知道用 pos( ) 但是具体方法我不知道哦 我太菜啦 请贴代码 请谅解
 
kidneyball 大侠 上一个问题的代码 可以改成字定义循环位置的吗? 比如加一个定位循环位置的函数,可以从指定的组 开始循环


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
cmbSource: TComboBox; //在Form上放个ComboBox作为输入
memResult: TMemo; //在Form上放个Memo作为输出
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject); //在Form上放个按钮触发运算
private
{ Private declarations }
public
{ Public declarations }
end;

const
MaxGroup = 2; //最大组数
CharMap : array[1..MaxGroup, 1..2] of string[16] = //改变MaxGroup时需要同时改变转换表定义
(('FE98BA54761032DC', '89EFCD23016745AB'),
('543210FEDCBA9876', 'DCBA9876543210FE')
);

var
Form1: TForm1;
CharMappingTable : array[1..MaxGroup, 1..2, 48..122] of char;

implementation

{$R *.dfm}

procedure MakeTable; //建立映射表,只需在开始运行一次,转换期间不用运行
var i, j, k : integer;
begin
for i := 1 to MaxGroup do
for j := 1 to 2 do
for k := 1 to 16 do
begin
if (k<=6) then
begin
CharMappingTable[i,j,byte('a')-1+k] := CharMap[i,j,k];
CharMappingTable[i,j,byte('A')-1+k] := CharMap[i,j,k];
end
else CharMappingTable[i,j,k-7+byte('0')] := CharMap[i,j,k]
end;
end;

procedure Convert(var s : string); //为了照顾效率, 采用直接写变量参数的的做法. 最后结果在传入的参数中
var i, k : integer;
begin
//MakeTable; //建立映射表
//因已在Form.OnCreate建立了映射表, 这里不用重复建立
i := 1; //字符指针
k := 1; //当前用的转换表组号
while i <= length(s) do
begin
s := CharMappingTable[k, 1 , Byte(s)];
inc(i); //指向第二个字符, 若s符合格式, 必定不会出界
s := CharMappingTable[k, 2, Byte(s)];
inc(i,2); //跳过空格, 到下一组第一个字符
if k < MaxGroup then inc(k) else k:=1;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var s : string;
begin
//转换
s := self.cmbSource.Text;
Convert(s);
self.memResult.Text := s;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
MakeTable; //建立全局映射表

//初始化调试数据
self.cmbSource.Items.Add('62 C7 2C A2 92 0E DC 85 98 EE 63 C6 2D A3 93 0F DD 84 99 EF 60 C5 2E A0 90 0C DE 87 9A EC 61 C4 2F A1 91');
self.cmbSource.ItemIndex := 0;
end;

end.
 
楼上的方法可以实现楼主的需求
 
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
mmo1: TMemo;
btn1: TButton;
mmo2: TMemo;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

function ExtractSubSect(const ALine, AStarting, ATeminal: string): string;
var
LineCp: string;
StartIndex: Integer;
Len: Integer;
begin
LineCp := ALine;
StartIndex := Pos(AStarting, LineCp) + Length(AStarting);
Delete(LineCp, 1, StartIndex - 1);
Len := Pos(ATeminal, LineCp) - 1;
if Len > 0 then
Result := Copy(LineCp, 1, Len)
else
Result := '不存在';
end;

procedure TForm1.btn1Click(Sender: TObject);
begin
mmo2.Clear;
// mmo1.Lines.Text := '123456789';
mmo2.Lines.Add('AA = ' + ExtractSubSect(mmo1.Lines.Text, 'D4 6D', '00'));
// mmo2.Lines.Add('AA = ' + ExtractSubSect(mmo1.Lines.Text, '23', '78'));
mmo2.Lines.Add('BB = ' + ExtractSubSect(mmo1.Lines.Text, 'DD 83', '00'));
mmo2.Lines.Add('CC = ' + ExtractSubSect(mmo1.Lines.Text, '6D 05', '90 02'));
end;

end.
 
算法思想:
取起始标志符首字符所在位
截去包含起始标志符在内的前导字符串
在余下的串(记为R)中,取结束标志符所在位,减1为所需的长度(L)
然后从R中第一字符开始,截出长度为L的串即可
注意,这样最终得到的串第一个字符,最后一个字符都是空格,如果不要,请自行修改
 
晕~~ 那段代码是我贴给kidneyball 的,那是上一个问题 ,和这个问题没有关系啊
 
运行结果:
AA = 31 32 3A 3A 3A 3A 3A
BB = 7C 3F FF 4D 30 30 30 30 30
CC = 93 7C 38 2C
下载源程序,请到
http://delphibbs.com/keylife/iblog_show.asp?xid=25820
“简单的字符串处理”
 
你还没睡哪?
 
to icc 你写的函数很好用啊 谢谢你耐心的回答, 分全是你的
 
顺便再问一句啊 如果要取 90 02 到最后的全部字符串 应该怎样写呢?
就是要取最后的这段内容
35 2E 30 2E 30 2E 33 00 01 00 00 00 2C
 
Result := Copy(LineCp, 1, Len);
把这一句改为
Result := Copy(LineCp, 1, Length(LineCp));
 
那一句直接改为 Resut := LineCp就OK
 
比如不知道末尾的字符该怎么办哪?
 
加我QQ:14574256
还有什么问题一块说,帮你这一次
 
关于第一个问题: 上一个问题的代码 可以改成字定义循环位置的吗? 比如加一个定位循环位置的函数,可以从指定的组 开始循环

可以对Convert过程做点小改动, 改动过的行在用*****注释标出

procedure Convert(var s : string; StartGroup : integer = 1); //*****
var i, k : integer;
begin
if StartGroup < 1 then Exit; //防止组数超下界 *****
//MakeTable; //建立映射表
//因已在Form.OnCreate建立了映射表, 这里不用重复建立
i := (StartGroup-1) * 3 + 1; //字符指针 3为每组(连分隔符)字符数 ******
k := ((StartGroup - 1) mod MaxGroup) + 1; //当前用的转换表组号 ******
while i <= length(s) do
begin
s := CharMappingTable[k, 1 , Byte(s)];
inc(i); //指向第二个字符, 若s符合格式, 必定不会出界
s := CharMappingTable[k, 2, Byte(s)];
inc(i,2); //跳过空格, 到下一组第一个字符
if k < MaxGroup then inc(k) else k:=1;
end;
end;

调用的时候, 可以用Convert(s)表示从第一组开始, 也可以用Convert(s, 数字)表示从第几组开始, 组数超出上界或小于1则不作任何改变

=====================================================
关于本贴的问题, 可用以下函数:

uses StrUtil; //在uses中添加此单元, 下面用到的PosEx函数在此单元中
.....

function GetTextBetween(Source, StartStr, EndStr : string) : string;
var
ps, pe : integer; //截取子串在源串的位置, ps为开头, pe为结束
begin
ps := Pos(StartStr, Source);
if ps > 0 then
begin
inc(ps, length(StartStr));
pe := PosEx(EndStr, Source, ps);
Result := trim(copy(Source, ps, pe-ps)); //结果须去掉两端空格
end
else
Result := ''; //找不到StartStr时返回空串. 若找不到EndStr, 则pe为0,copy(Source, ps, pe-ps) 为空串, 不用另行处理
end;

使用时: Memo1.Text := GetTextBetween(源串, 开始子串, 结束子串);
 
另, 因为这次没有效率要求, 因此我写的时候以结构清晰为主. 如果这个函数会被频繁调用. 针对你源串的格式, 结果两端各可能有一个空格, 把

Result := trim(copy(Source, ps, pe-ps)); //结果须去掉两端空格

这行改为:
if Source[ps] = ' ' then inc(ps);
if Source[pe] = ' ' then dec(pe);
Result := copy(Source, ps, pe-ps);

亦可, 会比用trim稍快
===============================
另, 上面的函数原来没有考虑第一次pos找不到StartStr的情况, 我直接在上面编辑了
 
后退
顶部