How do I get the currently-running MLM name without the user name at the beginning? The special keyword THIS_MLM_NAME returns the name of the MLM in the format USERNAME-302364198::MLM_NAME_HERE, but I just want the MLM's name by itself.
I tried using SUBSTRING:
SUBSTRING 200 CHARACTERS
STARTING AT ((FIND "::" IN THIS_MLM_NAME) + 2)
FROM THIS_MLM_NAME;
But this just returns null. What am I doing wrong?
The problem is that THIS_MLM_NAME is not actually an Arden string. If you test THIS_MLM_NAME IS STRING you will get false. To fix that, convert it to a string with THIS_MLM_NAME AS STRING:
ThisMLMName := SUBSTRING 200 CHARACTERS
STARTING AT ((FIND "::" IN STRING (THIS_MLM_NAME AS String)) + 2)
FROM (THIS_MLM_NAME AS String);
Since there is no debugger in Sunrise Acute Care's implementation of Arden, I wrote the following MLM to help show information about variables (name the module MOD_VARIABLE_INFO or change the code to match the actual name):
// data slot
(Variable, Padding) := ARGUMENT;
Result := "";
IF NOT EXIST Padding THEN
Padding := "";
ENDIF;
CR := (13 FORMATTED WITH "%c") || Padding;
Delimiter := "";
MOD_VARIABLE_INFO := MLM 'MOD_VARIABLE_INFO';
IF Variable IS LIST THEN
Result := Result || "List(" || (COUNT Variable) || ") [" || CR || " ";
FOR Item IN Variable DO
Result := Result || Delimiter;
TempResult := CALL MOD_VARIABLE_INFO WITH Item, Padding || " ";
Result := Result || TempResult;
Delimiter := "," || CR || " ";
ENDDO;
Result := Result || CR || "]";
ELSEIF Variable IS STRING THEN
Result := Result || "String";
ELSEIF Variable IS NUMBER THEN
Result := Result || "Number";
ELSEIF Variable IS BOOLEAN THEN
Result := Result || "Boolean";
ELSEIF Variable IS NET_OBJECT THEN
Result := Result || ".Net Object";
ELSEIF Variable IS NULL THEN
Result := Result || "Null";
ELSEIF Variable IS OBJECT THEN
Result := Result || "Object {" || CR || " ";
FOR Attr IN (EXTRACT ATTRIBUTE NAMES Variable) DO
Result := Result || Delimiter || Attr || ": ";
Item := ATTRIBUTE Attr FROM Variable;
TempResult := CALL MOD_VARIABLE_INFO WITH Item, Padding || " ";
Result := Result || TempResult;
Delimiter := "," || CR || " ";
ENDDO;
Result := Result || CR || "}";
ELSE
Result := Result || "Unknown (" || Variable || ")";
ENDIF;
// logic slot
CONCLUDE True;
// action slot
RETURN Result;
While this MLM returns "Unknown" for THIS_MLM_NAME, it at least shows that it is not any of the native Arden data types nor is it a .Net data type.
In the Sunrise MLM Editor, you can see what is going on in the underlying Lisp by syntax checking the MLM, then clicking on the "Syntax Check MLM" tab, selecting "Function Definition" then looking at the code in the lower right pane. Search for THIS_MLM_NAME and you will find (SETQ THIS_MLM_NAME 'USERNAME-302364198::MLM_NAME). From this you can see that the variable has been set to a plain quoted/unevaluated lisp expression rather than a string, which would look like (SETQ THIS_MLM_NAME "USERNAME-302364198::MLM_NAME").
Related
I have to make a program that reads some "random" strings of letters and numbers from a text file and checking if they meet some conditions that makes them a valid password.
The conditions are: -Have exactly 4 digits
-Have exactly 8 characters
-Have at least one uppercase letter and at least one lowercase letter
The program reads the file and it outputs the number of valid passwords.
This is the format of the text file:
"eR68G12a 91jY643ebjp eRty74kLh 24fG92 aj85gt32 dGb9357jKoup2 " (on a single line)
The code:
ยดยดยด
Program Ej23_version3;
var
char1,char2:char;
mayus,minus:boolean; // mayus and minus would be uppercase and lowercase respectively
cantDigitos,cantCaracteres,contrasenasValidas:integer;
datos:text;
Begin
assign(datos,'Datos_guia3_ej23.txt'); reset(datos);
contrasenasValidas := 0;
char1 := ' ';
Read(datos,char2);
while not eof(datos) do
Begin
mayus := false; minus := false; cantDigitos := 0; cantCaracteres := 0;
if (char1 = ' ') and (char2 <> ' ') then //check if its the beggining of the word
Begin
while not eof(datos) and (char2 <> ' ') do
Begin
cantCaracteres := cantCaracteres + 1;
if char2 = UPCASE(char2) then // if the character2 is equal to the uppercase version of the character2, character2 is uppercase
mayus := true
else
if (char2 in ['0'..'9']) then
cantDigitos := cantDigitos + 1
else
minus := true;
if eof(datos) then // when it reaches the end of the file, it also reads and checks the last character
if char2 = UPCASE(char2) then
mayus := true
else
if (char2 in ['0'..'9']) then
cantDigitos := cantDigitos + 1
else
minus := true;
End;
if minus and mayus and (cantDigitos = 4) and (cantCaracteres = 8) then //if all conditions are met, the password is valid and its added to the counter
contrasenasValidas := contrasenasValidas + 1;
char1 := char2; Read(datos, char2); //char2 should be an empty character by this point, so it passes that value to char1 and reads the next character
End
End;
WriteLn(contrasenasValidas);
End.
But when i run it, it just gets stuck there with only the prompt ticking
The problem is in the way you read the file (character by character).
It would be better to read it all at once, and examine all the sequences of eight characters one by one.
uses
SysUtils;
var
LFile: TextFile;
LStr, LSubStr: string;
LStartIndex: integer;
LExit: boolean;
begin
AssignFile(LFile, 'Datos_guia3_ej23.txt');
Reset(LFile);
ReadLn(LFile, LStr); // Get the whole line
LStartIndex := 1; // Search all 8 characters sequences, starting from the first character
LExit := FALSE;
repeat
LSubStr := Copy(LStr, LStartIndex, 8);
if Length(LSubStr) = 8 then
begin
// Here check other conditions
// ...
Inc(LStartIndex);
end else
LExit := TRUE;
until LExit;
CloseFile(LFile);
end.
I have two files - one contains a number of short strings (that I call Items) and the other contains the IDs for these short strings. I also have a number of other files, each of which contains a very long string that is broken up into thousands of smaller strings by means of line breaks. I need to find the number of times each short string ("Item") occurs in each of the large string files, as well as the starting and ending position for each hit.
My first task is to remove the line breaks in the large files so that the hits do not straddle across two or more smaller strings, and then find the number of occurrences and start and end positions of each short string in the large strings, and report these results using the ID numbers for each short string.
I am getting a EInOutError (Access Denied) error apparently at the ReWrite(ResultFile) line (in the bottom half of the second procedure in the code below). Not sure why. I can see the file created for the results of the first long string, but it is blank. Here is the code; pretty simple and straightforward, actually. Thanks.
procedure TForm1.OpenStrCmdBtnClick(Sender: TObject);
begin
if OpenDialog1.execute then OpenStrEditBx.Text := OpenDialog1.FileName
else ShowMessage('Cannot open file!');
end;
procedure TForm1.FindItemfCmdBtnClick(Sender: TObject);
var
I, LenItem, NumTimes, offset: integer;
Extension, FileName, Dir, Str, Item, ID: string;
TempList, IDList, ItemList: TStringList;
searchResult: TSearchRec;
ResultFile: TextFile;
begin
IDList := TStringList.Create;
ItemList := TStringList.Create;
IDList.LoadFromFile('D:\...\IDs.txt');
ItemList.LoadFromFile('D:\...\Items.txt');
try
Dir := ExtractFilePath(OpenStrEditBx.Text);
//concatenate each line in each str sequence to get full string
if FindFirst(Dir + '*.txt', faAnyFile, searchResult) = 0 then
repeat
Extension := ExtractFileExt(OpenStrEditBx.Text);
FileName := StringReplace(searchResult.Name, Extension, '', [rFReplaceAll, rFIgnoreCase]);//remove file extension to get only file name
TempList := TStringList.Create;
TempList.LoadFromFile(Dir + searchResult.Name);
Str := '';
for I := 1 to TempList.Count-1 do // I <> 0 to ignore ID in first line
begin
Str := Str + TempList[I];
ProgressBar1.Position := (ProgressBar1.Position + 10) mod ProgressBar1.Max;
end;
TempList.Free;
//Find number and location of occurrences of each Item
for I := 0 to ItemList.Count-1 do
begin
Item := ItemList[I];
LenItem := Length(Item);
ID := IDList[I];
NumTimes := 0;
Offset := PosEx(Item, Str, 1);
AssignFile(ResultFile, 'D:\...\' + FileName + '.txt');
ReWrite(ResultFile);
while Offset <> 0 do
begin
inc(NumTimes);
if NumTimes > 0 then
WriteLn(ResultFile, 'The ' + IntToStr(NumTimes) + 'th occurrence of ' + 'ID# ' + ID + ' is from Position# ' + IntToStr(Offset) + ' to Position# ' + IntToStr(Offset + LenItem- 1) + ' in ' + FileName);
Offset := PosEx(Item, Str, Offset + LenItem);
end;
WriteLn(ResultFile, 'ID# ' + ID + ' occurs ' + IntToStr(NumTimes) + ' number of times in ' + FileName);
end;
CloseFile(ResultFile);
ShowMessage(FileName + ' done!');
until FindNext(searchResult) <> 0;
FindClose(searchResult);
finally
IDList.Free;
ItemList.Free;
end;
end;
I have a problem. I'm learning Pascal for only a couple of weeks and I don't know much. I have to write a program that has to calculate something out of 3 entered numbers. The problem is all 3 of them need to be entered in one edit with spaces in between. So basically I have a string 'number number number'. How do I separate these numbers as 3 separate strings so I can convert them into Integer.
In pascal there are built-in procedures to retrieve the input from the console.
The easiest way to get numeric inputs is to use Read()/ReadLn(), which also can make the conversion from string to a numeric value:
procedure GetNumbers(var x,y,z: Integer);
begin
WriteLn('Enter three numbers separated with space and then press enter.');
ReadLn(x,y,z);
end;
Here, the ReadLn() detects three inputs separated with a space, waits for the [Enter] key and assigns the integer values to the x,y,z variables.
Using the copy function is one way. Sorry about the formatting, I can't understand how to paste code snippets properly in these answer sections.
function TMyForm.Add( anEdit : TEdit ) : integer;
var
Idx : integer;
TempString : string;
function GetNext : integer;
begin
result := result + StrToInt( copy( TempString, 1, Idx - 1 ) );
TempString := copy( TempString, Idx + 1, MAXINT );
end;
begin
result := 0;
TempString := anEdit.Text;
repeat
Idx := pos( ' ', TempString );
if Idx > 0 then
result := GetNext;
until Idx = 0;
if trim( TempString ) <> '' then
//this is the last piece of it then
result := result + StrToInt( trim( TempString ) );
end;
You need to also take care that the values entered are numbers and not letters, usually done with try..except blocks.
I have been trying to make use of setfilter but it seems not working
FOR i := 1 TO ARRAYLEN(ItemGroupFilter) DO BEGIN
IF i <> ARRAYLEN(ItemGroupFilter) THEN BEGIN
IF ItemGroupFilter[i] <> '' THEN
IF ItemGroupString = '' THEN
ItemGroupString := '<>' + ('%' + FORMAT(i))
ELSE
ItemGroupString := ItemGroupString + ('|%' + FORMAT(i))
ELSE
ItemGroupString := ItemGroupString;
END ELSE BEGIN
IF ItemGroupFilter[i] <> '' THEN
IF ItemGroupString = '' THEN
ItemGroupString := '<>' + ('%' + FORMAT(i))
ELSE
ItemGroupString := ItemGroupString + ('|%' + FORMAT(i))
ELSE
ItemGroupString := ItemGroupString;
END;
END;
Item.RESET;
Item.SETFILTER("Item Group",ItemGroupString,ItemGroupFilter[1],ItemGroupFilter[2],ItemGroupFilter[3],ItemGroupFilter[4]);
As a result, only the first ItemGroupFilter[1] is doing the job. as from the OR (|) afterwards, it doesnt work
Message Values:
ItemGroupString = '<>%1|%2|%3|%4'
ItemGroupFilter[1] = '1'
ItemGroupFilter[2] = '2'
ItemGroupFilter[3] = '3'
ItemGroupFilter[4] = '4'
Found the solution. SETFILTER for <> should be as follows:
SETFILTER('<>%1&<>%2&<>%3&<>%4','1','2','3','4')
So my above ItemGroupString Variable consist of
ItemGroupString = '<>%1&<>%2&<>%3&<>%4'
Happy programming! :)
I have a Question about Delphi StringLists and sorting them. I am sorting a list of attributes (with duplicate entries) so I need to retain their original index before the sort. Here is a sample of what I am trying
procedure TestFind;
var
i, iIndex :integer;
slStrings : TStringlist;
begin
slStrings := TStringList.Create;
slStrings.Sorted := False;
slStrings.Add('Zebra');
slStrings.AddObject('Zebra',TObject(1));
slStrings.Add('Bat');
slStrings.AddObject('Bat',TObject(2));
slStrings.Add('Cat');
slStrings.AddObject('Cat',TObject(3));
slStrings.Add('Hat');
slStrings.AddObject('Hat',TObject(4));
slStrings.Add('Aat');
slStrings.AddObject('Aat',TObject(5));
slStrings.sorted := True;
if slStrings.Find('Zebra',iIndex) then
begin
while slStrings.Strings[iIndex] = slStrings.Strings[iIndex + 1] do
begin
i := ObjectToInt(slStrings.Objects[iIndex]) ;
AddMemoData ('Stringlist Found at Position: ' + inttoStr(i) + ' Current index position is: ' + inttoStr(iIndex), false);
inc(iIndex);
end;
i := ObjectToInt(slStrings.Objects[iIndex]) ;
AddMemoData ('Stringlist Found at Position: ' + inttoStr(i) + ' Current index position is: ' + inttoStr(iIndex), false);
end;
end;
When I run this I get a Value of 0,8 for Zebra, this makes no sense to me, I would expect a message of 1,4
I really can't work out what your code is trying to achieve, but it is accessing beyond the end of the list. To avoid that your while test can be modified like so:
while (iIndex<slStrings.Count-1)
and (slStrings.Strings[iIndex] = slStrings.Strings[iIndex + 1]) do
Your use of Objects[] will work. Values placed there are kept with their matching Strings[] values when the list is sorted.
However, if I were you I would not use a string list for this task. I would declare a record like this:
TMyRec = record
Name: string;
Index: Integer;
end;
I would hold them in a TList<TMyRec> and then sort them using a custom comparer.
I note that you add each object twice, once with an associated index, and once without. Those latter instances will get a default index value of 0. I also observe that the code you present will not execute because of the out of bounds error that I identified. Further, even when you fix that it does not give output of the form that you claim.
In other words, it appears that the code you posted is very different from the code that you are running. I've answered based on the code that you included in the question. I hope that you can accept an answer on that basis and don't expect help with the code that you have, that we cannot see. Perhaps I should just have voted to close.
Anyway, perhaps the main problem is here:
slStrings.Add('Zebra');
slStrings.AddObject('Zebra',TObject(1));
slStrings.Add('Bat');
slStrings.AddObject('Bat',TObject(2));
slStrings.Add('Cat');
slStrings.AddObject('Cat',TObject(3));
slStrings.Add('Hat');
slStrings.AddObject('Hat',TObject(4));
slStrings.Add('Aat');
slStrings.AddObject('Aat',TObject(5));
This is equivalent to:
slStrings.AddObject('Zebra',TObject(0));
slStrings.AddObject('Zebra',TObject(1));
slStrings.AddObject('Bat',TObject(0));
slStrings.AddObject('Bat',TObject(2));
slStrings.AddObject('Cat',TObject(0));
slStrings.AddObject('Cat',TObject(3));
slStrings.AddObject('Hat',TObject(0));
slStrings.AddObject('Hat',TObject(4));
slStrings.AddObject('Aat',TObject(0));
slStrings.AddObject('Aat',TObject(5));
Did you actually mean to write this:
slStrings.AddObject('Zebra',TObject(1));
slStrings.AddObject('Bat',TObject(2));
slStrings.AddObject('Cat',TObject(3));
slStrings.AddObject('Hat',TObject(4));
slStrings.AddObject('Aat',TObject(5));
the Solution was this:
procedure TestFind;
var
i, iIndex, iStringSize :integer;
slStrings : TStringlist;
begin
slStrings := TStringList.Create;
slStrings.Sorted := False;
slStrings.AddObject('Zebra',TObject(1));
slStrings.AddObject('Bat',TObject(2));
slStrings.AddObject('Cat',TObject(3));
slStrings.AddObject('Hat',TObject(4));
slStrings.AddObject('Zebra',TObject(6));
slStrings.AddObject('Aat',TObject(5));
slStrings.AddObject('Zebra',TObject(7));
slStrings.sorted := True;
if slStrings.Find('Bat',iIndex) then
begin
//find lowest position of string matching found string
while iIndex > 0 do
begin
if (g_slVials.Strings[iIndex] = g_slVials.Strings[iIndex-1]) then
iIndex := iIndex - 1
else
break;
end;
iStringSize := slStrings.Count;
while iIndex < iStringSize -1 do //check for more matching strings in higher range
begin
if (g_slVials.Strings[iIndex] = g_slVials.Strings[iIndex+1]) then
begin
i := ObjectToInt(slStrings.Objects[iIndex]) ;
AddMemoData ('Stringlist Found at Position: ' + inttoStr(i) + ' Current index position is: ' + inttoStr(iIndex), false);
inc(iIndex);
end else
break;
end;
i := ObjectToInt(slStrings.Objects[iIndex]) ;
AddMemoData ('Stringlist Found at Position: ' + inttoStr(i) + ' Current index position is: ' + inttoStr(iIndex), false);
end;
end;
this allows me to find all matching strings and return their index position