### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook (preview only)

### Author Topic: String manipulation problem  (Read 645 times)

#### Bart

• Hero Member
• Posts: 3516
##### Re: String manipulation problem
« Reply #15 on: October 09, 2019, 12:11:36 am »
A little adapatation to the code that extracts typeHtml, so it also extracts the number:

Code: Pascal  [Select]
1. function TryExtractNumberAndTypeHtmlFromTag(Tag: String; var Number: Integer; var typeHtml: String): Boolean;
2. {
3. Precondition:
4.   Tag is in the form of "<for SomeID='SomeValue' type='comma,separated,values'/>"
5.   SomeId cannot be the string 'type'
6.   SomeID='SomeValue' is the first Key/Value pair inside the tag.
7.   None of the comma-separated values itself can contain a single quote as part of the value
8.   There will be no space before or after the equals sign after "SomeID" an d"type"
9. PostCondition:
10.   The function returns True if both a number and a value for typeHtml are foud, otherwise it returns False
11.   The Number is set to 'SomeValue'
12.   The typeHtml is set to 'comma,separated,values'
13. }
14. var
15.   p, ErrorCode: Integer;
16.   NumberStr: String;
17. begin
18.   Result := False;
19.   typeHtml := '';
20.   Number := 0;
21.   if Length(Tag) < Length('<type=''''/>') then Exit;
22.   if (Copy(Tag, 1,5) <> '<for ') and (Tag[Length(Tag)] <> '>') and (Tag[Length(Tag)-1] <> '/') then Exit;
23.
24.   Delete(Tag,1,5);
25.   p := pos('=''', Tag); //also find the first single quote atfer the equals sign
26.   if p = 0 then Exit;
27.   Inc(p, Length('='''));
28.   Delete(Tag, 1, p-1);
29.   p := Pos('''', Tag);
30.   if p = 0 then Exit;
31.   NumberStr := Copy(Tag, 1, p-1);
32.   Val(NumberStr, Number, ErrorCode);
33.   if ErrorCode <> 0 then Exit;
34.
35.   p := Pos('type=''', Tag);  //also find the first single quote atfer the equals sign
36.   if p = 0 then Exit;
37.   Inc(p, Length('type='''));
38.   typeHtml := Copy(Tag, p, MaxInt);
39.   p := Pos('''', typeHtml);
40.   if p = 0 then Exit;
41.   Delete(typeHtml, p, MaxInt);
42.   Result := True;
43. end;

And the program:
Code: Pascal  [Select]
1. var
2.   typeHtml: String;
3.   Tag: string = '<for it=''123'' type=''password,button,text'' hidden=''true''/>';
4.   tType: tab;
5.   i, Count, Number: Integer;
6. begin
7.   if not TryExtractNumberAndTypeHtmlFromTag(Tag, Number, typeHtml) then
8.   begin
9.     writeln('Cannot extract Number and typeHtml from "',Tag,'"');
10.     Exit;
11.   end;
12.   writeln('Number   = "',Number,'"');
13.   writeln('typeHtml = "',typeHtml,'"');
14.   Split(typeHtml, tType, Count);
15.   writeln('Count    = ',Count);
16.   for i := 1 to Count do writeln('"',tType[i],'"');
17. end.

Outputs:
Code: [Select]
`C:\Users\Bart\LazarusProjecten\bugs\forum\htmltag>testNumber   = "123"typeHtml = "password,button,text"Count    = 3"password""button""text"`
Bart
« Last Edit: October 09, 2019, 12:13:18 am by Bart »

#### Bart

• Hero Member
• Posts: 3516
##### Re: String manipulation problem
« Reply #16 on: October 09, 2019, 11:29:57 am »
A shorter and simpler version to extract number and typeHtml.
It assumes the tag starts with <for it=' and it assumes that the text type=' is always the start of the typeHtml we are lokking for.
Tipically in homework assignments these assumptions are allowed though.

Code: Pascal  [Select]
1. function TryExtractNumberAndTypeHtmlFromTagSimple(Tag: String; var Number: Integer; var typeHtml: String): Boolean;
2. {
3. Precondition:
4.   Tag must be in the form of "<for it='SomeIntegerValue' type='comma,separated,values'/>"
5.   None of the comma-separated values itself can contain a single quote as part of the value
6.   There will be no space before or after any of the equals signs
7. PostCondition:
8.   The function returns True if both a number and a value for typeHtml are foud, otherwise it returns False
9.   The Number is set to 'SomeValue'
10.   The typeHtml is set to 'comma,separated,values'
11. }
12. var
13.   p, ErrorCode: Integer;
14.   NumberStr: String;
15. const
16.   ForLen =  9; //<for it='
17.   TypeLen = 6; //type='
18. begin
19.   Result := False;
20.   typeHtml := '';
21.   Number := 0;
22.   p := Pos('<for it=''', Tag);
23.   if p = 0 then Exit;
24.   Delete(Tag,1,p + ForLen - 1);
25.   p := Pos('''', Tag);
26.   if p = 0 then Exit;
27.   NumberStr := Copy(Tag, 1, p-1);
28.   Val(NumberStr, Number, ErrorCode);
29.   if ErrorCode <> 0 then Exit;
30.
31.
32.   p := Pos('type=''', Tag);  //also find the first single quote atfer the equals sign
33.   if p = 0 then Exit;
34.   typeHtml := Copy(Tag, p + TypeLen, MaxInt);
35.   p := Pos('''', typeHtml);
36.   if p = 0 then Exit;
37.   Delete(typeHtml, p, MaxInt);
38.   Result := True;
39. end;

Bart

#### Bart

• Hero Member
• Posts: 3516
##### Re: String manipulation problem
« Reply #17 on: October 09, 2019, 12:13:41 pm »
so this is my what i done dude and i get error

dude ????
I do have a name, you know.

I do not really understand the flow of your program.
You seem to ask the user to input an array of html tags, is that really what you want? B.t.w. n and i will be wrong when the procedure exits.
Also this crashes if somebody inputs more than 20 tags.
It really might be easier to just write a procedure that asks for one html tag.
If you really need to ask for multipel tags, just call that procedure in a loop of some kind.

Then your getNumber tries to iterate throught this array, but it only ever returns the first numer it finds.
Also it assumes that the number will only ever be a single digit (for it='123' -> 1 instead of 123).

Then you repeatedly write down that number until the user presses *, why?

The procedure cleanTag simply crashes:
Code: Pascal  [Select]
1.   for j:=1 to NumberVergul do
2.   begin
3.     for i:=1 to length(tag) do
4.     if tag[i] <> ',' then
5.       tTag[j]:=tag[i] <<-- crash: Runtime error 201 at \$00401A5B

When you expect user input then tell the user.
Also tell the user what condition ends the input (e.g. pressing *, which b.t.w. does NOT work as you seem to expect in procedure remplire).

Fixed some issues in remplire, added writeln statements for debugging and to inform the user what he is expected to do.

Code: Pascal  [Select]
1. program whitehat;
2.
3. uses
4.   crt;
5.
6. type
7.   tab= array[1..20] of string;
8. var
9.   t,tTag:tab;
10.   n:integer;
11.   numberFor:string;
12.   tag:string;
13.
14. procedure remplire (var t:tab;var n:integer);
15. var
16.   i:integer;
17.   s: string;
18. begin
19.   writeln('procedure remplire A');
20.   i:=0;
21.   n:=0;
22.   repeat
23.     write('Enter tag # ',i+1,' (empty line quits input): ');
25.     if s<> '' then
26.     begin
27.       i:=i+1;
28.       t[i] := s;
29.       n:=n+1;
30.     end;
31.   until s='';
32.   writeln('procedure remplire End: n=',n,', i=',i);
33. end;
34.
35. // this procedure used to get  the 5 number after it='  <for it='5' type='text,password'>
36. procedure getNumber (t:tab;n:integer;var numberFor:string;var tag:string);
37. var
38.   i:integer;
39. begin
40.   writeln('procedure GetNumber A: n=',n);
41.   for i:=1 to n do
42.   begin
43.     if pos('for',t[i])<>0 then
44.     tag:=t[i];
45.     numberFor:=copy(t[i],10,1);
46.   end;
47.   repeat
48.     writeln('numberFor = ',numberFor,' Press * to stop (why??)');
50.   writeln('procedure GetNumber End');
51. end;
52.
53. //this function is for cleaning tag like if i find <for it='5' type='text,password'>  / the tTag (table) value is  :text,password
54. procedure cleanTag (tag:string;var tTag:tab);
55. var
56.   j,i,NumberVergul:integer;
57. begin
58.   writeln('procedure cleanTag A: Tag=',Tag);
59.
60.   NumberVergul:=1;
61.   for i:=1 to length(tag) do
62.   begin
63.     if tag[i] <> ',' then
64.     NumberVergul:=NumberVergul+1;
65.   end;
66.
67.   for j:=1 to NumberVergul do
68.   begin
69.     for i:=1 to length(tag) do
70.     if tag[i] <> ',' then
71.       tTag[j]:=tag[i]
72.     else
73.       tTag[j]:=tag[i+1];
74.   end;
75.
76.
77.   repeat
78.     for i:=1 to NumberVergul do
79.     begin
80.       writeln(tTag[i]);
81.     end;
83.    writeln('procedure cleanTag End');
84. end;
85.
86. begin
87.   remplire (t,n);
88.   getNumber(t,n,numberFor,tag);
89.   cleanTag (tag,tTag);
90. end.

Still does not do what you want, but at least you can folow the code flow:
Code: [Select]
`C:\Users\Bart\LazarusProjecten\bugs\forum\htmltag>whitehatprocedure remplire AEnter tag # 1 (empty line quits input): <for it='123' type='foo,bar,foobar'/>Enter tag # 2 (empty line quits input):procedure remplire End: n=1, i=1procedure GetNumber A: n=1numberFor = 1 Press * to stop (why??)numberFor = 1 Press * to stop (why??)numberFor = 1 Press * to stop (why??)numberFor = 1 Press * to stop (why??)numberFor = 1 Press * to stop (why??)numberFor = 1 Press * to stop (why??)procedure GetNumber Endprocedure cleanTag A: Tag=<for it='123' type='foo,bar,foobar'/>Runtime error 201 at \$00401A5B  \$00401A5B  CLEANTAG,  line 71 of whitehat.lpr  \$00401BD0  main,  line 89 of whitehat.lpr  \$00409711No heap dump by heaptrc unitExitcode = 201`
Runtime error 201 is a range check error b.t.w.

Bart

#### Bart

• Hero Member
• Posts: 3516
##### Re: String manipulation problem
« Reply #18 on: October 09, 2019, 12:42:18 pm »

Code: Pascal  [Select]
1. const
2.   Max = 50;
3. type
4.   Tab= array[1..Max] of string;
5.
6. procedure ClearTab(var T: Tab);
7. var
8.   i: Integer;
9. begin
10.   for i := 1 to Max do T[i] := '';
11. end;
12.
13. procedure Split(S: String; var T: Tab; var Count: Integer); //or : ; out Count); if that syntax is allowed for you
14. {
15. Preconditions:
16.   Comma (',') is used as separator
17.   S is not allowed to end in a comma
18. Postconditions:
19.   Count is set to the number of strings found
20.   T[1]..T[Count] hold the respective strings
21.   T[Count+1]..T[50] are empty strings
22. }
23.
24. var
25.   p: Integer;
26. begin
27.   ClearTab(T);
28.   Count := 1;
29.   while S <> '' do
30.   begin
31.     p := Pos(',', S);
32.     if p = 0 then
33.     begin
34.       T[Count] := S;
35.       Break;
36.     end;
37.     T[Count] := copy(S,1,p-1);
38.     Delete(S, 1, p);
39.     Inc(Count);
40.   end;
41. end;
42.
43.
44. function TryExtractNumberAndTypeHtmlFromTag(Tag: String; var Number: Integer; var typeHtml: String): Boolean;
45. {
46. Precondition:
47.   Tag is in the form of "<for SomeID='SomeValue' type='comma,separated,values'/>"
48.   SomeId cannot be the string 'type'
49.   SomeID='SomeValue' is the first Key/Value pair inside the tag.
50.   None of the comma-separated values itself can contain a single quote as part of the value
51.   There will be no space before or after the equals sign after "SomeID" an d"type"
52. PostCondition:
53.   The function returns True if both a number and a value for typeHtml are foud, otherwise it returns False
54.   The Number is set to 'SomeValue'
55.   The typeHtml is set to 'comma,separated,values'
56. }
57. var
58.   p, ErrorCode: Integer;
59.   NumberStr: String;
60. begin
61.   Result := False;
62.   typeHtml := '';
63.   Number := 0;
64.   if Length(Tag) < Length('<type=''''/>') then Exit;
65.   if (Copy(Tag, 1,5) <> '<for ') and (Tag[Length(Tag)] <> '>') and (Tag[Length(Tag)-1] <> '/') then Exit;
66.
67.   Delete(Tag,1,5);
68.   p := pos('=''', Tag); //also find the first single quote atfer the equals sign
69.   if p = 0 then Exit;
70.   Inc(p, Length('='''));
71.   Delete(Tag, 1, p-1);
72.   p := Pos('''', Tag);
73.   if p = 0 then Exit;
74.   NumberStr := Copy(Tag, 1, p-1);
75.   Val(NumberStr, Number, ErrorCode);
76.   if ErrorCode <> 0 then Exit;
77.
78.   p := Pos('type=''', Tag);  //also find the first single quote atfer the equals sign
79.   if p = 0 then Exit;
80.   Inc(p, Length('type='''));
81.   typeHtml := Copy(Tag, p, MaxInt);
82.   p := Pos('''', typeHtml);
83.   if p = 0 then Exit;
84.   Delete(typeHtml, p, MaxInt);
85.   Result := True;
86. end;
87.
88. function TryExtractNumberAndTypeHtmlFromTagSimple(Tag: String; var Number: Integer; var typeHtml: String): Boolean;
89. {
90. Precondition:
91.   Tag must be in the form of "<for it='SomeIntegerValue' type='comma,separated,values'/>"
92.   None of the comma-separated values itself can contain a single quote as part of the value
93.   There will be no space before or after any of the equals signs
94. PostCondition:
95.   The function returns True if both a number and a value for typeHtml are foud, otherwise it returns False
96.   The Number is set to 'SomeValue'
97.   The typeHtml is set to 'comma,separated,values'
98. }
99. var
100.   p, ErrorCode: Integer;
101.   NumberStr: String;
102. const
103.   ForLen =  9; //<for it='
104.   TypeLen = 6; //type='
105. begin
106.   Result := False;
107.   typeHtml := '';
108.   Number := 0;
109.   p := Pos('<for it=''', Tag);
110.   if p = 0 then Exit;
111.   Delete(Tag,1,p + ForLen - 1);
112.   p := Pos('''', Tag);
113.   if p = 0 then Exit;
114.   NumberStr := Copy(Tag, 1, p-1);
115.   Val(NumberStr, Number, ErrorCode);
116.   if ErrorCode <> 0 then Exit;
117.
118.
119.   p := Pos('type=''', Tag);  //also find the first single quote atfer the equals sign
120.   if p = 0 then Exit;
121.   typeHtml := Copy(Tag, p + TypeLen, MaxInt);
122.   p := Pos('''', typeHtml);
123.   if p = 0 then Exit;
124.   Delete(typeHtml, p, MaxInt);
125.   Result := True;
126. end;
127.
128. procedure AskTags(var Tags: Tab; var NrOfTags: Integer);
129. var
130.   Tag: String;
131. begin
132.   ClearTab(Tags); //Clear the array
133.   NrOfTags := 0;
134.   repeat
135.     write('Tag # ',NrOfTags+1,': (empty line quits input): ');
137.     if Tag <> '' then
138.     begin
139.       Inc(NrOfTags);
140.       Tags[NrOfTags] := Tag;
141.     end;
142.   until (Tag = '') or (NrOfTags = Max);
143. end;
144.
145. var
146.   typeHtml: String;
147.   Tag: string = '<for it=''123'' type=''password,button,text'' hidden=''true''/>';
148.   tType, Tags: tab;
149.   i, j, NrOfTags, Count, Number: Integer;
150. begin
152.   for i := 1 to NrOfTags do
153.   begin
154.     if not TryExtractNumberAndTypeHtmlFromTagSimple(Tags[i], Number, typeHtml) then
155.     begin
156.       writeln('Cannot extract Number and typeHtml from "',Tags[i],'"');
157.       Continue; //skip rest of the loop code, go to the next value of i
158.     end;
159.     writeln('Number   = "',Number,'"');
160.     writeln('typeHtml = "',typeHtml,'"');
161.     Split(typeHtml, tType, Count);
162.     writeln('Count    = ',Count);
163.     //write the seperate strings we found for this typeHtml
164.     for j := 1 to Count do writeln('  "',tType[j],'"');
165.   end;
166. end.

Code: [Select]
`C:\Users\Bart\LazarusProjecten\bugs\forum\htmltag>testTag # 1: (empty line quits input): <for it='123' type='foo,bar,foobar'/>Tag # 2: (empty line quits input): <for it='-32767' type='jan,klaas'/>Tag # 3: (empty line quits input): >invalid input type=' ,,,,'Tag # 4: (empty line quits input):Number   = "123"typeHtml = "foo,bar,foobar"Count    = 3  "foo"  "bar"  "foobar"Number   = "-32767"typeHtml = "jan,klaas"Count    = 2  "jan"  "klaas"Cannot extract Number and typeHtml from ">invalid input type=' ,,,,'"`
Bart

#### Bart

• Hero Member
• Posts: 3516
##### Re: String manipulation problem
« Reply #19 on: October 09, 2019, 05:34:05 pm »
The procedure cleanTag simply crashes:
Code: Pascal  [Select]
1.   for j:=1 to NumberVergul do
2.   begin
3.     for i:=1 to length(tag) do
4.     if tag[i] <> ',' then
5.       tTag[j]:=tag[i] <<-- crash: Runtime error 201 at \$00401A5B

In cleanTag what the beginning of the code essentially does is find the position of the first comma and set NumberVergul to that value minus 1.
Then in the next loop you seem to assume that NumberVergul is the amount of strings inside the 'text,password' part of the tag.
This is obviously wrong (and it will crash the program if NumberVergul is > 20).

Assuming that procedure remplire gives you an array of strings, where presumably only one line has an appropriate tag in it, then with little modifications procedure getNumber will be OK.
Note however that "<for it='QWERTY" will set numberFor to 'Q'.
(This may not be relevant if the assignment states that tags are always correctly formed as your example tag).

For the cleanTag() procedure you need to write down the algorithm (no code) on how you would solve this problem e.g. if you had only paper and pencil.
What steps are involved?
E.g.
• Part 1: Extract the 'password,text' part
• Part 2: Split the found 'password,text' into an array so that array[1]='password' and array[2]='text'

Step 1 for step 1 surely is: find the position of "type='"
What would you do logically next?

Once you have written this down tranlate the algorithm to pseudo-code
E.g.
Code: [Select]
`  Find p: position of "type='" in the Tag  if not found: exit with some indication of error  etc.`
This will make implementiong in code easy:
Code: Pascal  [Select]
1.   p := Pos('type=''', Tag);
2.   if p = 0 then ...

B.t.w. using tag and tTag as variable names inside one procedure is really confusing.
Using understandable type names can also help. Consider:
Code: Pascal  [Select]
1. const
2.   Max = 20;  //need the max amount allowed in the procedure that dos the user input
3.
4. type
5.   TStringArray= array[1..Max] of string;
6.   // versus
7.   Tab= array[1..Max] of string;

Bart

#### whitehat

• Jr. Member
• Posts: 93
##### Re: String manipulation problem
« Reply #20 on: October 09, 2019, 08:55:43 pm »
bart you are the best i hope one day i will be like you i mean it <3