Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
I need to make a comma separated string which would have multiple string values and each of these value should be in single quote.
When I am using QuotedStr, it is quoting with double-quote,instead of single.
I even tried using single-quote but still it is having same issue.
I tried googling but didn't get any resulting info.
Any other ways to make it ?
Regards.
Vishal
QuotedStr adds a single quotation mark at the beginning and at the end of the string, passed as argument.
What may be misleading is how the debugger shows strings.
For example, the following code
procedure TForm1.Button2Click(Sender: TObject);
var
a, b, c, r: string;
begin
a := 'A';
b := 'B';
c := 'C';
r := QuotedStr(a) + ',' +
QuotedStr(b) + ',' +
QuotedStr(c);
Label2.Caption := r; // breakpoint here
end;
with a break point on the last line shows this in the Local Variables pane:
but the actual label caption looks like this:
The debugger encloses strings in single quotes for display, and because your strings already contains single quotes, it looks like there would be double quotes.
The code that you posted in a comment ...
procedure TForm9.Button2Click(Sender: TObject);
var MyString : String;
begin
//#44
MyString := QuotedStr('String1');
MyString := MyString + ',' + QuotedStr('String2');
ShowMessage(MyString);
end;
... produces this result:
and if the message is expanded with showing the length, like this:
ShowMessage(MyString + ' Length: ' + IntToStr(Length(MyString)));
... it produces this:
Single quotes, length is as expected.
Related
This question already has an answer here:
Delphi check if character is in range 'A'..'Z' and '0'..'9'
(1 answer)
Closed 5 years ago.
for counter := 1 to lengthofpassword do
begin
currentletter:=password[counter];
currentascii:=Ord(currentletter);
if (96<currentascii<123) OR (64<currentascii<91) OR (47<currentascii<58) then
Writeln('valid')
else
asciicheck:=false;
end;
I know this code is wrong but I did it to explain what I want to ask. How can you specify ranges for an if statement? Before, I messed around with lots of if statements and my code wasn't working the way I wanted it to. Basically, I am making a procedure which checks the user input for anything other than uppercase and lowercase alphabet and numbers. This question is different because I was looking for how this problem could be solved using a Case Of statement.
for counter := 1 to lengthofpassword do
begin
currentletter:=password[counter];
currentascii:=Ord(currentletter);
if (currentascii<48) AND (currentascii>57) then
asciipoints:=asciipoints+1;
if (currentascii<65) AND (currentascii>90) then
asciipoints:=asciipoints+1;
if (currentascii<97) AND (currentascii>122) then
asciipoints:=asciipoints+1;
Writeln(asciipoints);
end;
I also tried to do it like this but then realised this wouldn't work because if one statement was satisfied, the others wouldn't be and the points based system wouldn't work either.
Glad you found the answer yourself.
Another way to make sure the password only contains upper and lower case characters and numbers is what I tried to point to: define a set of characters that are valid and check if each character in your password is in these valid characters.
So with a set defined like this:
const
ValidChars = ['A'..'Z', 'a'..'z', '0'..'9'];
you can use statements like
if password[I] in ValidChars then
This statement will however generate a compiler warning in Unicode Delphi, as the type in a set is limited to 256 possible values, and their ordinalities must fall between 0 and 255. This isn't the case for WideChar with 65.536 values. So the set of char defined is in fact a set of AnsiChar. For this task this is acceptable, as every character that needs to be checked is ASCII, so using the function CharInSet will not generate a compiler warning and have a defined behavior - returning False - if the password contains Unicode characters.
This is the resulting code:
const
ValidChars = ['A'..'Z', 'a'..'z', '0'..'9'];
var
I: Integer;
begin
for I := 1 to passwordlength do
begin
if CharInSet(password[I], ValidChars) then
Writeln('valid') // more likely to do nothing and invert the if statement
else
begin
asciicheck := False;
Break; // No need to look further, the check failed
end;
end;
end;
Multiple ranges is best expressed in a case statement:
begin
for counter := 1 to lengthofpassword do
begin
case Ord(password[counter]) of
48..57,
65..90,
97..122 :
Writeln('valid')
else
asciicheck:=false;
end;
end;
end;
Now, this works for characters < #128. If you are working in a unicode application and don't want the restriction of characters being the english alphabet, it is possible to use TCharHelper.IsLetterOrDigit.
if password[counter].IsLetterOrDigit then ...
Thanks to a comment up above, I have found a solution. I ended up using a Case Of statement like this:
for counter := 1 to lengthofpassword do
begin
currentletter:=password[counter];
currentascii:=Ord(currentletter);
case currentascii of
97..122 : asciicheck:=true;
65..90 : asciicheck:=true;
48..57 : asciicheck:=true;
else asciicheck:=false;
end;
end;
Thanks once again.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
procedure TMainWin.AgeEBKeyPress(Sender: TObject; var Key: Char);
procedure ProcessInput(Sender:Tobject);
var age : byte;
begin
Age := StrToInt(AgeEB.Text);
Age := Age+2;
AnsLbl.visible := True //it's here, where there is a mistake
AnsLbl.Caption := 'Bad luck, I"m' + IntTostr(Age) + '-Years old'
if not (Key in ['0'..'9' , chr(vk_return), chr(vk_back)]) then begin
key := #0;
exit
end
This is the code, there is some thing wrong with it, but
Statements need to be separated using the separator symbol, ;.
In the following code, you fail to do this:
AnsLbl.visible := True
AnsLbl.Caption := 'Bad luck, I"m' + IntTostr(Age) + '-Years old'
if ...
You must place separators between these three statements. Typically they are placed at the end of each line like so:
AnsLbl.visible := True;
AnsLbl.Caption := 'Bad luck, I"m' + IntTostr(Age) + '-Years old';
if ...
But they need not be. You could equally write this as:
AnsLbl.visible := True
; AnsLbl.Caption := 'Bad luck, I"m' + IntTostr(Age) + '-Years old'
; if ...
That would be very weird and I am not advocating that you do so. The documentation says this:
Tokens
On the simplest level, a program is a sequence of tokens delimited by
separators. A token is the smallest meaningful unit of text in a
program. A separator is either a blank or a comment. Strictly
speaking, it is not always necessary to place a separator between two
tokens; for example, the code fragment:
Size:=20;Price:=10;
is perfectly legal. Convention and readability, however, dictate that
we write this in two lines, as:
Size := 20;
Price := 10;
Tokens are categorized as special symbols, identifiers, reserved
words, directives, numerals, labels, and character strings. A
separator can be part of a token only if the token is a character
string. Adjacent identifiers, reserved words, numerals, and labels
must have one or more separators between them.
The documentation also has this to say about compound statements:
Compound Statements
A compound statement is a sequence of other (simple or structured)
statements to be executed in the order in which they are written. The
compound statement is bracketed by the reserved words begin and end,
and its constituent statements are separated by semicolons. For
example:
begin
Z := X;
X := Y;
X := Y;
end;
The last semicolon before end is optional. So this could have been
written as:
begin
Z := X;
X := Y;
Y := Z
end;
Compound statements are essential in contexts where Delphi syntax
requires a single statement. In addition to program, function, and
procedure blocks, they occur within other structured statements, such
as conditionals or loops. For example:
begin
I := SomeConstant;
while I > 0 do
begin
...
I := I - 1;
end;
end;
You can write a compound statement that contains only a single
constituent statement; like parentheses in a complex term, begin and
end sometimes serve to disambiguate and to improve readability. You
can also use an empty compound statement to create a block that does
nothing:
begin
end;
It has been a while since i did any Delphi coding but I think you need ; semicolon at the end of each line.
Like for instance this line:
AnsLbl.visible := True
should be:
AnsLbl.visible := True;
procedure TForm1.Button1Click(Sender: TObject);
var
xlap,xlbook,xlsheet:variant;
y:integer;
begin
xlap:=createoleobject('excel.application');
xlbook:=xlap.workbooks.add;
xlap.visible:=true;
xlsheet:=xlbook.worksheets.add;
for y:=1 to 50 do
begin
xlsheet.cells[y,2].value:= concat('=IF(A',y,'>6,"Good","Bad")')
inc(y);
end;
end;
That's my code to make an Excel through Delphi. I want to input the IF formula into column B of the Excel, but there is error when I use the concat('=IF(A',y,'>6,"Good","Bad")').
May be I need another way to include y between those strings.
Any suggestion? Thanks before
Delphi has a format statement bit like sprintf printf in c, well nearly
xlsheet.cells[y,2].value:= format('=IF(A%d>6,"Good", "Bad")',[y])
%d is a place holder for an int. Look it up for loads of other stuff.
NB the variables you want to interpolate are assed in an array.
In addition to Tony's answer about Format, here are a couple of other approaches. (Format is great if you have mixed types or many values, but it carries some overhead that might not be needed.)
You can concatenate strings with a simple + - in fact, the Concat documentation says it does the same thing but is faster:
Temp := 'ing';
Str := 'Test' + Temp; // Str = 'Testing'
As your y variable is an integer, you'll need to convert it to a string first (note you don't need to Inc(y);, as the loop will do that already - it's automatically incremented from the starting value to the ending value on each pass through the loop):
for y:=1 to 50 do
begin
xlsheet.cells[y,2].value:= '=IF(A' + IntToStr(y) + '>6,"Good","Bad")';
end;
I am trying to get a routine that will find a string that does not follow a parentheses. For instance if the file open in the RichEdit contains these lines of CNC code, I want it to find the first two and ignore the third. In the second line it should only find and highlight the first occurrence of the search string. The search string (mach.TOOL_CHANGE_CALL) in this example is 'T'.
N1T1M6
N1T1M6(1/4-20 TAP .5 DP.)
(1/4-20 TAP .5 DP.)
I have gotten this far, but am stumped.
procedure TMainForm.ToolButton3Click(Sender: TObject); // find tool number
var
row:integer;
sel_str:string;
par:integer;
tool:integer;
tool_flag:integer ;
line_counter:integer;
tool_pos:integer;
line_begin:integer;
RE:TRichEdit;
begin
RE:=(ActiveMDIChild as TMDIChild).RichEdit1;
line_counter:=0;
tool_flag:=0;
tool_pos:=0;
row:=SendMessage(RE.Handle,EM_LINEFROMCHAR,-1, RE.SelStart);
while tool_flag =0 do
begin
RE.Perform(EM_LINESCROLL,0,line_counter);
sel_str := RE.Lines[Line_counter];
tool:=pos(mach.TOOL_CHANGE_CALL,sel_str);
par:=pos('(',sel_str);
if par=0 then
par:=pos('[',sel_str);
tool_pos:=tool_pos+length(sel_str);
if (tool>0) and (par = 0) then
begin
RE.SetFocus;
tool_pos:=tool_pos + line_counter-1;
line_begin:=tool_pos-tool;
RE.SelStart := line_begin;
RE.SelLength := Length(sel_str);
tool_flag:=1;
end;
inc (line_counter);
end;
end;
The results I get is that it will ignore the third string, but will also ignore the second string as well. It also will not find subsequent occurrences of the string in the file, it just starts back at the beginning to the text and finds the first one again. How can I get it to find the second example and then find the next 'T' at the next click of the button? I also need it to highlight the entire line the search string was found on.
Given the samples you posted, you can use Delphi (XE and higher) regular expressions to match the text you've indicated. Here, I've put the three sample lines you've shown into a TMemo (Memo1 in the code below), evaluate the regular expression, and put the matches found into Memo2 - as long as your TRichEdit contains only plain text, you can use the same code by replacing Memo1 and Memo2 with RichEdit1 and RichEdit2 respectively.
I've updated the code in both snippets to show how to get the exact position (as an offset from the first character) and length of the match result; you can use this to highlight the match in the richedit using SelStart and SelLength.
uses
RegularExpressions;
procedure TForm1.Button1Click(Sender: TObject);
var
Regex: TRegEx;
MatchResult: TMatch;
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('N1T1M6');
Memo1.Lines.Add('N1T1M6(1/4-20 TAP .5 DP.)');
Memo1.Lines.Add('(1/4-20 TAP .5 DP.)');
Memo2.Clear;
// See the text below for an explanation of the regular expression
Regex := TRegEx.Create('^\w+T\w+', [roMultiLine]);
MatchResult := Regex.Match(Memo1.Lines.Text);
while MatchResult.Success do
begin
Memo2.Lines.Add(MatchResult.Value +
' Index: ' + IntToStr(MatchResult.Index) +
' Length: ' + IntToStr(MatchResult.Length));
MatchResult := MatchResult.NextMatch;
end;
end;
This produces the following results:
If you're using a version of Delphi that doesn't include regular expression support, you can use the free TPerlRegEx with some minor code changes to produce the same results:
uses
PerlRegEx;
procedure TForm1.Button1Click(Sender: TObject);
var
Regex: TPerlRegEx;
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('N1T1M6');
Memo1.Lines.Add('N1T1M6(1/4-20 TAP .5 DP.)');
Memo1.Lines.Add('(1/4-20 TAP .5 DP.)');
Memo2.Clear;
Regex := TPerlRegEx.Create;
try
Regex.RegEx := '^\w+T\w+';
Regex.Options := [preMultiLine];
Regex.Subject := Memo1.Lines.Text;
if Regex.Match then
begin
repeat
Memo2.Lines.Add(Regex.MatchedText +
' Offset: ' + IntToStr(Regex.MatchedOffset) +
' Length: ' + IntToStr(Regex.MatchedLength));
until not Regex.MatchAgain;
end;
finally
Regex.Free;
end;
end;
The regular expression above (^\w+T\w+) means:
Options: ^ and $ match at line breaks
Assert position at the beginning of a line (at beginning
of the string or after a line break character) «^»
Match a single character that is a “word character” (letters,
digits, and underscores) «\w+»
Between one and unlimited times, as many times as possible,
giving back as needed (greedy) «+»
Match the character “T” literally «T»
Match a single character that is a “word character” (letters,
digits, and underscores) «\w+»
Between one and unlimited times, as many times as possible,
giving back as needed (greedy) «+»
Created with RegexBuddy
You can find a decent tutorial regarding regular expressions here. The tool I used for working out the regular expression (and actually producing much of the Delphi code for both examples) was RegexBuddy - I'm not affiliated with the company that produces it, but just a user of that product.
I'm doing some work with code generation, and one of the things I need to do is create a function call where one of the parameters is a function call, like so:
result := Func1(x, y, Func2(a, b, c));
TStringList.CommaText is very useful for generating the parameter lists, but when I traverse the tree to build the outer function call, what I end up with looks like this:
result := Func1(x, y, "Func2(a, b, c)");
It's quoting the third argument because it contains commas, and that produced invalid code. But I can't do something simplistic like StringReplace all double quotes with empty strings, because it's quite possible that a function argument could be a string with double quotes inside. Is there any way to make it just not escape the lines that contain commas?
You could set QuoteChar to be a space, and you'd merely get some extra spaces in the output, which is generally OK since generated code isn't usually expected to look pretty. String literals would be affected, though; they would have extra spaces inserted, changing the value of the string.
Free Pascal's TStrings class uses StrictDelimiter to control whether quoting occurs when reading the DelimitedText property. When it's true, quoting does not occur at all. Perhaps Delphi treats that property the same way.
Build an array of "unlikely characters" : non-keyable like †, ‡ or even non-printable like #129, #141, #143, #144.
Verify you don't have the 1st unlikely anywhere in your StringList.CommaText. Or move to the next unlikely until you get one not used in your StringList.CommaText. (Assert that you find one)
Use this unlikely char as the QuoteChar for your StringList
Get StringList.DelimitedText. You'll get the QuoteChar around the function parameters like: result := Func1(x, y, †Func2(a, b, c)†);
Replace the unlikely QuoteChar (here †) by empty strings...
What about using the Unicode version of AnsiExtractQuotedStr to remove the quotes?
Write your own method to export the contents of your TStringList to a string.
function MyStringListToString(const AStrings: TStrings): string;
var
i: Integer;
begin
Result := '';
if AStrings.Count = 0 then
Exit;
Result := AStrings[0];
for i := 1 to AStrings.Count - 1 do
Result := Result + ',' + AStrings[i];
end;
Too obvious? :-)
Alternatively, what would happen if you set StringList.QuoteChar to #0 and then called StringList.DelimitedText?
We have written a descendant class of TStringList in which reimplemented the DelimitedText property. You can copy most of the code from the original implementation.
var
LList: TStringList;
s, LOutput: string;
begin
LList := TStringList.Create;
try
LList.Add('x');
LList.Add('y');
LList.Add('Func2(a, b, c)');
for s in LList do
LOutput := LOutput + s + ', ';
SetLength(LOutput, Length(LOutput) - 2);
m1.AddLine('result := Func1(' + LOutput + ')');
finally
LList.Free;
end;
end;
Had the same problem, here's how I fixed it:
s := Trim(StringList.Text)
that's all ;-)