As it's said in the title Im having trouble finding a solution on how to check if a string PW contains a number or not. How can i check in TP if the string PW contains a digit?
repeat
writeln;
writeln('Ok, please enter your future password.');
writeln('Attention: The Text can only be decoded with the same PW');
readln(PW);
pwLength:= Length(PW);
error:=0;
for i:= 1 to Length(PW) do begin
if Input[i] in ['0'..'9'] then begin
error:=1;
end;
end;
if Length(PW)=0 then
begin
error:=1;
end;
if Length(PW)>25 then
begin
error:=1;
end;
if error=1 then
begin
writeln('ERROR: Your PW has to contain at least 1character, no numbers and has to be under 25characters long.');
readln;
clrscr;
end;
until error=0;
This is how I would write your code:
var
PW : String;
Error : Integer;
const
PWIsOk = 0;
PWIsBlank = 1;
PWTooLong = 2;
PWContainsDigit = 3;
procedure CheckPassword;
var
i : Integer;
begin
writeln;
writeln('Ok, please enter your future password.');
writeln('Attention: The Text can only be decoded with the same PW');
writeln('Your password must be between 1 and 25 characters long and contain no digits.');
repeat
error := PWIsOk;
readln(PW);
if Length(PW) = 0 then
Error := PWIsBlank;
if Error = PWIsOk then begin
if Length(PW) > 25 then
Error := PWTooLong;
if Error = 0 then begin
for i := 1 to Length(PW) do begin
if (PW[i] in ['0'..'9']) then begin
Error := PWContainsDigit;
Break;
end;
end;
end;
end;
case Error of
PWIsOK : writeln('Password is ok.');
PWIsBlank : writeln('Password cannot be blank.');
PWTooLong : writeln('Password is too long.');
PWContainsDigit : writeln('Password should not contain a digit');
end; { case}
until Error = PWIsOk;
writeln('Done');
end;
These are some of the things to notice:
Don't use the same error code value to represent different types of error. Using the same value for different errors just makes it more difficult for you to debug your code, because you can't tell which test gave Error the value 1.
Define constants to represent the different types of error. That way, readers don't have to wonder "What does 3 mean" in if error = 3 ...
Once you've detected a digit character in the password, there is no point examining the characters after it, hence the Break in my for loop.
If I were a user I would be annoyed not to be told what the rules are until after the program tells me I've done something wrong. Tell the use beforehand what the rules are.
Actually, it would be better to include an additional constant Unclassified with a value of say, -1, and start each iteration of the loop by assigning Error to it and, in the subsequent steps, test for Error = Unclassified rather than PWIsOk.
A case statement is a tidy and easily-maintainable way of selecting one of a number of mutually exclusive execution paths based on an ordinal value.
Related
I really don't know why Pos keep returning 0 instead of the char ";" position in string
I have to get a response of a php page which outputs a Content-Type: text/plain
So one example output is
2;fulano;fulano;0
3;ciclano;ciclano;0
4;beltrano;beltrano;0
5;foo;foo;0
8;jose;jose;0
9;maria;maria;0
and the code is
var
linha,uid,login,senha,email,tipo : WideString;
resposta : TStringList;
I : Integer;
begin
try
resposta := TStringList.Create;
resposta.Text := frmMain.IdHTTP1.Get(frmMain.cdsConfig.FieldByName('WebService').AsString+'listdest.php');
for I := 0 to resposta.Count-1 do
begin
linha := resposta.Strings[i];
if i = 0 then
Delete(linha,1,1); // the first line have one wierd $FEFF
if length(linha) > 5 then
begin
uid := Copy(linha,1,Pos(linha,';')-1);
Delete(linha,1,Pos(linha,';'));
login:=Copy(linha,1,Pos(linha,';')-1);
Delete(linha,1,Pos(linha,';'));
senha:=Copy(linha,1,Pos(linha,';')-1);
Delete(linha,1,Pos(linha,';'));
email:=Copy(linha,1,Pos(linha,';')-1);
Delete(linha,1,Pos(linha,';'));
tipo:=Copy(linha,1,Pos(linha,';')-1);
Delete(linha,1,Pos(linha,';'));
end;
end;
//dlgWait.Close;
except on E :Exception do
begin
MessageBox(Self.Handle,PWideChar(E.Message),'Erro',MB_OK+MB_ICONERROR+MB_APPLMODAL);
dlgWait.Close;
FreeAndNil(resposta);
end;
end;
Your call to Pos is backwards. The parameters are:
function Pos(const SubStr, Str: _ShortStr; Offset: Integer): Integer;
But your code assumes they are:
function Pos(const Str, SubStr: _ShortStr; Offset: Integer): Integer;
So actually what it's trying to do is look for the value of linha within ';', which of course unless linha = ';', it will return 0.
Another way to put it, as Rudy said, instead of looking for a needle in a haystack, your code is looking for a haystack in a needle.
Swap around the first and second parameters to these calls.
On a side note, just a tip for performance. Rather than calling Pos twice for each, keep a cached copy of the value...
P := Pos(';', linha);
uid := Copy(linha,1,P-1);
Delete(linha,1,P);
So I have 2 strings and I want to be able to say if the 2 strings are the same or not. The only problem is im filling the string 1 character by 1 using a while so if I use length/ord it doesn't work properly. I guess if you see the code im working with you will have an easier tas helping me out, so I'll just paste it here.
var
cad1, cad2: string;
car: char;
icad1, icad2: integer;
begin
car := 'o';
icad1 := 1;
icad2 := 1;
write('Write the cad1: ');
while (car<>'.') do begin
car := readkey;
cad1 := car;
write(car);
inc(icad1);
end;
car := 'o';
writeln;
write('Write thecad2: ');
while (car <> '.') do begin
car := readkey;
cad2 := car;
write(car);
inc(icad2);
end;
writeln;
end.
You have just to do :
CompareText(cad1, cad2)
it will return 0 if the two string are the same.
http://www.freepascal.org/docs-html/rtl/sysutils/comparetext.html
There are several problems in your code. For example: the line cad1:=car; assigns the character to a string. That means that the resulting string contains only one character equal to car. All the previous inputs are lost.
The simpliest way to input the strings and compare them is the following:
write('Write the cad1: ');
readln(cad1);
write('Write thecad2: ');
readln(cad2);
write(cad1=cad2);
readln;
I have a memo lines like this:
Mahogany
Unpolished
In Stock : Yes
Total Stock : 102
Redwood
Polished
In Stock : Yes
Total Stock : 80
Pine
Polished
In Stock : Yes
Total Stock : 22
And i want to have only the line of Redwood's Total Stock.
Since there are many same string of
Total Stock
I can not use this string as my keyword. So, i use "Redwood" , but i don't know how to get the line of "Total Stock" after "Redwood".
var
i: Integer;
s: string;
begin
for i := 0 to mem0.lines.Count - 1 do
begin
if (AnsiContainsStr(mem0.lines[i], 'Redwood')) then
begin
// s:= Redwood's total stock, how to do this ?
end
end;
end;
The missing code is:
s := mem0.Lines[i+3];
This makes the assumption that the format of the data is always exactly as seen in the question. If that assumption is valid then this simple code is the best solution.
It would probably make more sense to use one of the standard human readable structured data formats like JSON or YAML that have good parsers and emitters. Sadly the support for YAML on Delphi is essentially non-existant so that leaves JSON.
You could try this, but like your original code it's a bit 'fragile' in that it makes assumptions about the layout of the text you're searching (in particular that the text you're searching through is packaged in a TStrings object):
function TotalForItem(const ItemName : String; Strings : TStrings) : String;
var
i,
j,
p : Integer;
s : string;
TotalLineIntro : String;
begin
Result := '';
TotalLineIntro := 'Total Stock : ';
for i := 0 to Strings.Count - 1 do
begin
if (Pos(ItemName, Strings[i]) > 0) then
begin
for j:= i + 1 to Strings.Count - 1 do begin
p := Pos(TotalLineIntro, Strings[j]);
if p > 0 then
begin
Result := Copy(Strings[j], p + Length(TotalLineIntro), Length(Strings[j]));
exit;
end;
end;
end
end;
end;
procedure TForm3.Button1Click(Sender: TObject);
begin
ShowMessage(TotalForItem('Redwood', Memo1.Lines));
end;
Instead of AnsiContainsStr, you can use StartsWith, since the rest of the lines of the Redwood part are indentend.
var
I: Integer;
TotalStockLine: string;
MyArray: TArray<string>
begin
for I := 0 to Memo1.Lines.Count - 1 do
if Memo.Lines[I].StartsWith('Redwood') then
begin
TotalStockLine := Trim(Memo.Lines[I + 3];
Break;
end;
if TotalStockLine <> '' then
begin
MyArray := TotalStockLine.Split([':']);
TotalStock := StrToInt(Trim(MyArray[1]));
end;
etc... This can probably be simplified a little, but that shows how you could do this.
FWIW, this assumes you are using XE3 or later. Otherwise you must use the standalone StartsWith.
The program has several "encryption" algorithms. This one should blockwise reverse the input. "He|ll|o " becomes "o |ll|He" (block length of 2).
I add two strings, in this case appending the result string to the current "block" string and making that the result. When I add the result first and then the block it works fine and gives me back the original string. But when i try to reverse the order it just gives me the the last "block".
Several other functions that are used for "rotation" are above.
//amount of blocks
function amBl(i1:integer;i2:integer):integer;
begin
if (i1 mod i2) <> 0 then result := (i1 div i2) else result := (i1 div i2) - 1;
end;
//calculation of block length
function calcBl(keyStr:string):integer;
var i:integer;
begin
result := 0;
for i := 1 to Length(keyStr) do
begin
result := (result + ord(keyStr[i])) mod 5;
result := result + 2;
end;
end;
//desperate try to add strings
function append(s1,s2:string):string;
begin
insert(s2,s1,Length(s1)+1);
result := s1;
end;
function rotation(inStr,keyStr:string):string;
var //array of chars -> string
block,temp:string;
//position in block variable
posB:integer;
//block length and block count variable
bl, bc:integer;
//null character as placeholder
n : ansiChar;
begin
//calculating block length 2..6
bl := calcBl(keyStr);
setLength(block,bl);
result := '';
temp := '';
{n := #00;}
for bc := 0 to amBl(Length(inStr),bl) do
begin
//filling block with chars starting from back of virtual block (in inStr)
for posB := 1 to bl do
begin
block[posB] := inStr[bc * bl + posB];
{if inStr[bc * bl + posB] = ' ' then block[posB] := n;}
end;
//adding the block in front of the existing result string
temp := result;
result := block + temp;
//result := append(block,temp);
//result := concat(block,temp);
end;
end;
(full code http://pastebin.com/6Uarerhk)
After all the loops "result" has the right value, but in the last step (between "result := block + temp" and the "end;" of the function) "block" replaces the content of "result" with itself completely, it doesn't add result at the end anymore.
And as you can see I even used a temp variable to try to work around that.. doesnt change anything though.
I am 99.99% certain that your problem is due to a subtle bug in your code. However, your deliberate efforts to hide the relevant code mean that we're really shooting in the dark. You haven't even been clear about where you're seeing the shortened Result: GUI Control/Debugger/Writeln
The irony is that you have all the information at your fingertips to provide a small concise demonstration of your problem - including sample input and expected output.
So without the relevant information, I can only guess; I do think I have a good hunch though.
Try the following code and see if you have a similar experience with S3:
S1 := 'a'#0;
S2 := 'bc';
S3 := S1 + S2;
The reason for my hunch is that #0 is a valid character in a string: but whenever that string needs to be processed as PChar, #0 will be interpreted as a string terminator. This could very well cause the "strange behaviour" you're seeing.
So it's quite probable that you have at least one of the following 2 bugs in your code:
You are always processing 1 too many characters; with the extra character being #0.
When your input string has an odd number of characters: your algorithm (which relies on pairs of characters) adds an extra character with value #0.
Edit
With the additional source code, my hunch is confirmed:
Suppose you have a 5 character string, and key that produces block length 2.
Your inner loop (for posB := 1 to bl do) will read beyond the length of inStr on the last iteration of the outer loop.
So if the next character in memory happens to be #0, you will be doing exactly as described above.
Additional problem. You have the following code:
//calculating block length 2..6
bl := calcBl(keyStr);
Your assumption in the comment is wrong. From the implementation of calcBl, if keyStr is empty, your result will be 0.
I'm expanding a class of mine for storing generic size strings to allow more flexible values for user input. For example, my prior version of this class was strict and allowed only the format of 2x3 or 9x12. But now I'm making it so it can support values such as 2 x 3 or 9 X 12 and automatically maintain the original user's formatting if the values get changed.
The real question I'm trying to figure out is just how to detect if one character from a string is either upper or lower case? Because I have to detect case sensitivity. If the deliminator is 'x' (lowercase) and the user inputs 'X' (uppercase) inside the value, and case sensitivity is turned off, I need to be able to find the opposite-case as well.
I mean, the Pos() function is case sensitive...
Delphi 7 has UpperCase() and LowerCase() functions for strings. There's also UpCase() for characters.
If I want to search for a substring within another string case insensitively, I do this:
if Pos('needle', LowerCase(hayStack)) > 0 then
You simply use lower case string literals (or constants) and apply the lowercase function on the string before the search. If you'll be doing a lot of searches, it makes sense to convert just once into a temp variable.
Here's your case:
a := '2 x 3'; // Lowercase x
b := '9 X 12'; // Upper case X
x := Pos('x', LowerCase(a)); // x = 3
x := Pos('x', LowerCase(b)); // x = 3
To see if a character is upper or lower, simply compare it against the UpCase version of it:
a := 'A';
b := 'b';
upper := a = UpCase(a); // True
upper := b = UpCase(b); // False
try using these functions (which are part of the Character unit)
Character.TCharacter.IsUpper
Character.TCharacter.IsLower
IsLower
IsUpper
UPDATE
For ansi versions of delphi you can use the GetStringTypeEx functions to fill a list with each ansi character type information. and thne compare the result of each element against the $0001(Upper Case) or $0002(Lower Case) values.
uses
Windows,
SysUtils;
Var
LAnsiChars: array [AnsiChar] of Word;
procedure FillCharList;
var
lpSrcStr: AnsiChar;
lpCharType: Word;
begin
for lpSrcStr := Low(AnsiChar) to High(AnsiChar) do
begin
lpCharType := 0;
GetStringTypeExA(LOCALE_USER_DEFAULT, CT_CTYPE1, #lpSrcStr, SizeOf(lpSrcStr), lpCharType);
LAnsiChars[lpSrcStr] := lpCharType;
end;
end;
function CharIsLower(const C: AnsiChar): Boolean;
const
C1_LOWER = $0002;
begin
Result := (LAnsiChars[C] and C1_LOWER) <> 0;
end;
function CharIsUpper(const C: AnsiChar): Boolean;
const
C1_UPPER = $0001;
begin
Result := (LAnsiChars[C] and C1_UPPER) <> 0;
end;
begin
try
FillCharList;
Writeln(CharIsUpper('a'));
Writeln(CharIsUpper('A'));
Writeln(CharIsLower('a'));
Writeln(CharIsLower('A'));
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
Readln;
end.
if myChar in ['A'..'Z'] then
begin
// uppercase
end
else
if myChar in ['a'..'z'] then
begin
// lowercase
end
else
begin
// not an alpha char
end;
..or D2009 on..
if charInSet(myChar,['A'..'Z']) then
begin
// uppercase
end
else
if charInSet(myChar,['a'..'z']) then
begin
// lowercase
end
else
begin
// not an alpha char
end;
The JCL has routines for this in the JclStrings unit, eg CharIsUpper and CharIsLower. SHould work in Delphi 7.
AnsiPos() is not case-sensitive. You can also force upper or lower case, irrespective of what the user enters using UpperCase() and LowerCase().
Just throwing this out there since you may find it far more simple than the other (very good) answers.