Removing char from string in pascal cause question marks in console pascal - encoding

I am trying write simple program that will remove all 'o' letters from the string.
Example :
I love cats
Output:
I lve cats
I wrote following code :
var
x:integer;
text:string;
text_no_o:string;
begin
text:='I love cats';
for x := 0 to Length(text) do
//writeln(Ord(text[6]));
if(Ord(text[x])=111) then
else
text_no_o[x]:=text[x];
write(text_no_o);
end.
begin
end;
end.
When text is in English program works fine .
But if i change it to Russian . It returns we question marks in console.
Code with small modifications for Russian language.
var
x:integer;
text:string;
text_no_o:string;
begin
text:='Русский язык мой родной';
for x := 0 to Length(text) do
//writeln(Ord(text[6]));
if(Ord(text[x])=190) then
else
text_no_o[x]:=text[x];
write(text_no_o);
end.
begin
end;
end.
And result in console that i receive is :
Русский язык м�й р�дн�й
I expect receive
Русский язык мй рднй
As I got the problem can be caused incorrect encoding settings in console, so i should force pascal to use CP1252 instead ANSI .
I am using Free Pascal Compiler version 3.2.0+dfsg-12 for Linux .
P.S I am not allowed to use StringReplace or Pos

Simple solution:
function Simple_StripO (Text : String) : String;
var
i : integer;
Text2 : string;
begin
Text2 := '';
for i := 1 to Length(Text) do
if Text[i] <> 'o' then
Text2 := Text2 + Text[i];
Result := Text2; // Or Simple_StripO := Text2;
end;

The string is likely to be UTF8 encoded. So the cyrillic o is encoded as two chars $d0 $be. Here you replace one $be (=190). You need to replace both chars, though you cannot just test for the value of the char, because their meaning depends of surrounding chars.
Here is a way, remembering the current state (outside of letter or after first byte)
var
c: char;
text: string;
state: (sOutside, sAfterD0);
begin
text:= 'Русский язык мой родной';
state:= sOutside;
for c in text do
begin
if state = sOutside then
begin
if c = #$D0 then // may be the start of the letter
state := sAfterD0
else
write(c); // output this char because not part of letter
end
else if state = sAfterD0 then
begin
if c = #$BE then state := sOutside // finished skipping
else
begin
// chars do not form letter so output skipped char
write(#$D0, c);
state := sOutside;
end;
end
end;
writeln;
end.

Related

Delphi RTTI Object Inspector

I'm trying to build a simplified object inspector for a drawing app I'm writing.
I'm trying to dynamically get RTTI for the selected object and its child objects. If a given property is a class (tkClass), I want to call GetRTTIObject recursively, handing that property as the object to get the "subproperties" for it (i.e. BaseObj.Brush.Color or BaseObj.Pen.Width etc.). I suspect I want to pass the instance of that object and that it will be painfully obvious when someone points out what that is. How do I get an instance to pass to my function? Or should I be looking at TRttiInstance for properties that are classes....?
I know it works at "level 0", because I can pass in BaseObject.Brush to my first call of GetRTTIObject and I get a list of TBrush properties. How can I drill down recursively?
I seem to get a pointer of some kind with Value := GetPropValue(AObj, Prop.Name);
Do I dereference that somehow to get my instance...?
Regards,
Rob
The simplified test class is defined:
TBaseClass = class(TObject)
private
FFont: TFont;
FBrush: TBrush;
FPen: TPen;
FCaption: String;
FFloat1: Real;
FInt1: Integer;
published
property Font: TFont Read FFont Write FFont;
property Brush: TBrush Read FBrush Write FBrush;
property Pen: TPen Read FPen Write FPen;
property Caption: String Read FCaption Write FCaption;
property Float1: Real Read FFloat1 Write FFloat1;
property Int1: Integer Read FInt1 Write FInt1;
end;
My RTTI procedure is:
procedure TfrmMain.GetRTTIClass(AClass: TClass; Items: TStrings; Indent: Integer);
var
LContext: TRttiContext;
LType: TRttiType;
Prop: TRttiProperty;
PropString: String;
PropInfo: PPropInfo;
Tabs: String;
I: Integer;
Value: Variant;
begin
LContext := TRttiContext.Create();
try
for I := 0 to Indent do
Tabs := Tabs + ' '; //chr(9)
Log(Format('Get RTTI (Class) for "%s"', [AClass.ClassName]));
LType := LContext.GetType(AClass.ClassInfo);
Items.Add(Tabs + 'RTTI for: ' + Ltype.Name);
Items.Add(Tabs + 'Package Name: ' + LType.Package.Name);
Items.Add(Tabs + '-- Properties --');
for Prop in LType.GetProperties do
begin
PropString := 'property: ' + Prop.Name;
PropInfo := GetPropInfo(AClass, Prop.Name);
PropString := PropString + ': ' + GetEnumName(TypeInfo(TTypeKind), Ord(Prop.PropertyType.TypeKind));
if propInfo <> nil then begin
PropString := PropString + ': ' + PropInfo^.PropType^.Name;
case propInfo.PropType^.Kind of
tkClass: begin
PropString := PropString + ' (Class)' ; // ' GetProp Value: ' + IntToHex(PropInfo.GetProc, 8); // Items.Add('--- Get RTTI ---');(Class)';
Log(Format('GetRTTI: %s (%s)', [Prop.Name, PropInfo^.PropType^.Name]));
// TODO: Get a reference to the object and call GetRTTI
// TODO: Or change function to work from classtype rather than object
// GetRTTIObject(### WHAT GOES HERE?!?!?, Items, Indent + 1);// := PropString + ' Class';
end;
end;
end;
Items.Add(Tabs + PropString);
end;
finally
LContext.Free;
end;
end;
Oops!!
I see I put the wrong function in.....the one in question takes a TObject and the assignment is:
LType := LContext.GetType((AObject.ClassInfo); (AObject.ClassType also seems to work...)....
Not at my dev station just now, but think everything else is the same after that....
Problem in your example that TBrash have property TBitMap, TBitMap have TCanvas, TCanvas have TBrash. Call of function GetRTTIClass will be infinite recursive. But if make condition for getting RTTI only one time for each class it is possible to fix your function.
uses System.Generics.Collections;
var ListClasses:TList<TClass>;
LContext : TRttiContext;
implementation
procedure TfrmMain.FormCreate(Sender: TObject);
begin
LContext := TRttiContext.Create();
ListClasses:=TList<TClass>.Create;
end;
procedure TfrmMain.GetRTTIClass(AClass: TClass; Items: TStrings; Indent: Integer);
var
LType: TRttiType;
Prop: TRttiProperty;
PropString: String;
Tabs: String;
I: Integer;
begin
if ListPrinted.Contains(AClass) then Exit
else ListPrinted.Add(AClass);
for I := 0 to Indent do Tabs := Tabs + ' ';
LType := LContext.GetType(AClass.ClassInfo);
Items.Add(Tabs + 'RTTI for: ' + Ltype.Name);
Items.Add(Tabs + 'Package Name: ' + LType.Package.Name);
Items.Add(Tabs + '-- Properties --');
for Prop in LType.GetProperties do begin
PropString := 'property: ' + Prop.Name;
PropString := PropString + ': ' + GetEnumName(TypeInfo(TTypeKind), Ord(Prop.PropertyType.TypeKind))+' '+Prop.PropertyType.Name;
Items.Add(Tabs + PropString);
case Prop.PropertyType.Handle^.Kind of
tkClass: begin
GetRTTIClass(Prop.PropertyType.Handle^.TypeData^.ClassType, Items,Indent+2);
end;
end;
end;
procedure TfrmMain.btn1Click(Sender: TObject);
begin
GetRTTIClass(TBaseClass, Items,0);
end;
Ok, I have made some modifications to the procedure. Parsing the class isn't quite sufficient. I need handles to instances.
To recursively call my procedure (the one that takes an object, not a class as the first parameter), I need the instance of the sub-object (AObj.Font, for example). I can acquire that with:
case Prop.PropertyType.TypeKind of
tkClass: begin
SubObj := GetObjectProp(AObj, Prop.Name);
GetRTTIObject2(SubObj, Tree, ChildNode, Indent + 2);
end;
end;
So simple, really, once I wrapped my head around it.
Still going to vote the other answer as the solution, since it gave good pointers on avoiding another pitfall. :)

Numbering with TRichEdit in Delphi

I am trying to implement numbering in TRichEdit component, Delphi. Ideally I want to get the same behavior as in these 3rd party component:
As you can see Numbering button works similar way as Bullet button. I mean it sets FirstIdent or LeftIdent (I am not sure) and put the numbers 1,2,3,... instead of bullets. When you move cursor to the left close to number it does not allow to move onto number but jumps one line up.
This is what I got so far:
procedure TMainForm.NumberingButtonClick(Sender: TObject);
var
i: Integer;
s: String;
begin
if NumberingButton.Down then
begin
Editor.Paragraph.Numbering := nsNone;
i := Editor.ActiveLineNo;
s := Editor.Lines[i];
insert(inttostr(i)+'. ', s, 1);
//Editor.Paragraph.LeftIndent := 10;
Editor.Paragraph.FirstIndent := 10;
Editor.Lines[i] := s;
end;
end;
But it does not work as I want. Anybody have any ideas?
This code works exactly how I expected:
procedure TMainForm.NumberingButtonClick(Sender: TObject);
var
i: Integer;
s: String;
fmt: TParaFormat2;
begin
FillChar(fmt, SizeOf(fmt), 0);
fmt.cbSize := SizeOf(fmt);
fmt.dwMask := PFM_NUMBERING or PFM_NUMBERINGSTART or
PFM_NUMBERINGSTYLE or PFM_NUMBERINGTAB;
if NumberingButton.Down then
fmt.wNumbering := 2
else
fmt.wNumbering := 0;
// wNumbering:
// 0 - no numbering
// 1 - bullet list (·, ·, ·, ...).
// 2 - Arabic numbers (1, 2, 3, ...).
// 3 - small letters (a, b, c, ...).
// 4 - capital letters (A, B, C, ...).
// 5 - small Roman numbers (i, ii, iii, ...).
// 6 - capital Roman numbers (I, II, III, ...).
// 7 - Unicode character sequence
fmt.wNumberingStart := 1;
// wNumberingStart:
// The number at which the numbering starts.
fmt.wNumberingStyle := $200;
// wNumberingStyle:
// Numbering Style
// 0 : 1)
// $100 : (1)
// $200 : 1.
// $300 : 1
// $400 : remove list
// $8000 : continues to number the list without changing the style
fmt.wNumberingTab := 1440 div 4;
// wNumberingTab:
// the space between number and paragraph text
Editor.Perform( EM_SETPARAFORMAT, 0, lParam( #fmt ) );
if BulletsButton.Down then
BulletsButton.Down := False;
end;
Thanks to www.decoding.dax.ru

Delphi Xe10 Datasnap : Image function sending not working

I have a problem with the send function. My program worked correcly in Xe8 but with the upgrade to Xe10, I have a problem : encryption ?
I have create a simple project datasnap with juste the bugging function like that
procedure TServerMethods1.test3;
VAR
a : string;
begin
a := LoadFileToStr('C:\demo\Bitmaps\Mazak_Matrix.JPG');
GetInvocationMetadata.ResponseContentType := 'image/jpeg';
GetInvocationMetadata.ResponseCode := 200;
GetInvocationMetadata().ResponseContent := a;
GetInvocationMetaData.CloseSession := True;
end;
And the function LoadFileToStr
function LoadFileToStr(const FileName: string): AnsiString;
var
FileStream : TFileStream;
begin
FileStream:= TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
if FileStream.Size>0 then
begin
SetLength(Result, FileStream.Size);
FileStream.Read(Pointer(Result)^, FileStream.Size);
end;
finally
FileStream.Free;
end;
end;
Actually, the data was correct until
GetInvocationMetadata().ResponseContent := a;
The responseContent destroy the data, like you can see on the hex editor. An example. The entire file is not corrupt, just a few characters.
Original Picture
After sending by the server
Someone has encountered the same bug? If going directly a stream that works, but it doesn't interest me.
Thanks !
Had same problem. Try something like this.
procedure TAPI.GetFile(UID: string);
var
LFS: TFileStream;
I: Integer;
LByte: Byte;
begin
try
LFS := TFileStream.Create('e:\image.jpg', fmOpenRead);
for I := 0 to Pred(LFS.Size) do
begin
LFS.Read(LByte, 1);
GetInvocationmetaData.ResponseContent := GetInvocationmetaData.ResponseContent + Char(LByte);
end;
GetInvocationmetaData.ResponseContentType := 'image/jpeg';
finally
LFS.Free;
end;
end;

Form resource not found after on-the-fly String-Resource translation

I have a problem which occurs only in a very small customer range and I would like to ask if you might give me a hint where the problem might be. The program works for 98% of the customers. Alas, it is not possible that I work with the customers to debug the issue, because their knowledge of Windows and computers is very basic. It is also not possible that I send multiple versions of the product to them, since they don't even know how to install software (the admins do all the stuff).
First of all, I translate all RT_STRING resources on-the-fly, so that the language-switching in the program also affects hardcoded stuff like "Yes", "No", "Cancel" etc., which would only be possible by compiling 2 EXE files.
The code (I have tried to left away as much unnecessary stuff as possible, but since I don't know where the problem is, I provided as much details for the bug as possible):
The ony-the-fly resource translation
procedure TranslateResources;
var
i: integer;
s: string;
{$IF NOT Declared(FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)}
const
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = $2000;
{$IFEND}
begin
// I copy all resources in a dummy DLL (without code), because
// 1) The resources are the only thing we need when changing the resource module
// 2) If the EXE code/debug sections are too long, BeginUpdateResource() will ruin the performance heavily
FTempFile := IncludeTrailingPathDelimiter(GetTempDirectory) + GetRandomString(8)+'.dll';
// Transfers all resources from ParamStr(0) into the dummy DLL at FTempFile
ReGenerateResourceFile(FTempFile);
// if necessary, remove readonly flag
SetFileAttributes(PChar(FTempFile), FILE_ATTRIBUTE_OFFLINE or
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED or
FILE_ATTRIBUTE_TEMPORARY );
for i := 0 to Length(RTLResStringTranslationArray)-1 do
begin
s := Translate(RTLResStringTranslationArray[i].TranslationID);
if s <> '' then
begin
// Translate the string
UpdateResString(RTLResStringTranslationArray[i].ResStrDescriptor.Identifier, s);
end;
end;
LoadNewResourceModule(FTempFile):
end;
procedure ReGenerateResourceFile(OutputFile: string);
var
hUpd: Cardinal;
rs: TResourceStream;
fs: TFileStream;
begin
// As template we use a dummy DLL which contains no code.
// We will implement all resources from ParamStr(0) into it, before we translate the strings.
rs := TResourceStream.Create(HInstance, 'DUMMYDLL', 'DLL');
fs := TFileStream.Create(OutputFile, fmCreate or fmOpenWrite);
try
fs.CopyFrom(rs, rs.Size)
finally
rs.Free;
fs.Free;
end;
// Transfer resources from our EXE into the dummy DLL file
hUpd := BeginUpdateResource(PChar(OutputFile), true);
try
EnumResourceTypes(hInstance, #_enumResTypesProc, hUpd);
finally
EndUpdateResource(hUpd, false)
end;
end;
// This is based on reinit.pas from Borland's RichEdit example; slightly modified
function LoadNewResourceModule(PatchedFile: string): LongInt;
var
NewInst: Longint;
CurModule: PLibModule;
begin
Result := 0;
// Win95: "Initialization routine failed"
// NewInst := LoadLibrary(PChar(PatchedFile));
NewInst := LoadLibraryEx(PChar(PatchedFile), 0, LOAD_LIBRARY_AS_DATAFILE);
CurModule := LibModuleList;
Result := 0;
while CurModule <> nil do
begin
if CurModule.Instance = HInstance then
begin
if CurModule.ResInstance <> CurModule.Instance then
FreeLibrary(CurModule.ResInstance);
// Win95: ERangeError
CurModule^.ResInstance := NewInst;
Result := NewInst;
Exit;
end;
CurModule := CurModule.Next;
end;
end;
// Based on http://stackoverflow.com/questions/1498658/modifying-a-string-in-resource-of-an-exe
// Modified
procedure UpdateResString(const AStringIdent: Integer; const ANewString: WideString);
var
ResData, TempData: TWordArray;
iSection, iIndexInSection: Integer;
i, iLen, iSkip, iPos: Integer;
begin
// Calculate the resource string area and the string index in that area
iSection := AStringIdent div 16 + 1;
iIndexInSection := AStringIdent mod 16;
ResData := ReadSectionCached(iSection);
// Calculate the position of the string
iLen := Length(ANewString);
iPos := 0;
for i := 0 to iIndexInSection do
begin
if iPos > High(ResData) then
begin
SetLength(ResData, iPos + 1);
ResData[iPos] := 0;
end;
if i <> iIndexInSection then
begin
iSkip := ResData[iPos] + 1;
Inc(iPos, iSkip);
end;
end;
// Put data behind strings into TempData
iSkip := 1{size} + ResData[iPos];
SetLength(TempData, Length(ResData) - (iPos + iSkip));
if Length(TempData) > 0 then
begin
CopyMemory(#TempData[0], #ResData[iPos + iSkip], Length(TempData)*SizeOf(TempData[0]));
end;
SetLength(ResData, iPos + (iLen + 1{size}) + Length(TempData));
// Overwrite string
ResData[iPos] := iLen;
Inc(iPos);
if iLen > 0 then
begin
CopyMemory(#ResData[iPos], #ANewString[1], iLen*SizeOf(ANewString[1]));
Inc(iPos, iLen);
end;
// Append TempData after our new string
if Length(TempData) > 0 then
begin
CopyMemory(#ResData[iPos], #TempData[0], Length(TempData)*SizeOf(TempData[0]));
end;
CacheSet(iSection, ResData);
end;
type
TGlobalData = record
GlobalPtr: Pointer;
Length: integer;
end;
function LoadResourcePtr(hModule: HMODULE; restype, resname: PChar; wIDLanguage: WORD): TGlobalData;
var
hFind, hRes: THandle;
begin
result.GlobalPtr := nil;
result.Length := -1;
hFind := Windows.FindResourceEx(hModule, restype, resname, wIDLanguage);
if hFind = 0 then RaiseLastOSError;
hres := Windows.LoadResource(hModule, hFind);
if hres = 0 then RaiseLastOSError;
result.GlobalPtr := Windows.LockResource(hres);
result.Length := Windows.SizeofResource(hModule, hFind);
end;
function _enumResLangsProc(hmodule: HMODULE; restype, resname: PChar; wIDLanguage: WORD;
lParam: LongInt): BOOL; stdcall;
var
rs: TGlobalData;
begin
rs := LoadResourcePtr(hmodule, restype, resname, wIDLanguage);
UpdateResource(lParam, restype, resname, wIDLanguage, rs.GlobalPtr, rs.Length);
result := true;
end;
function _enumResNamesProc(hmodule: HMODULE; restype, resname: PChar;
lParam: LongInt): BOOL; stdcall;
begin
EnumResourceLanguages(hmodule, restype, resname, #_enumResLangsProc, lParam);
result := true;
end;
function _enumResTypesProc(hmodule: HMODULE; restype: PChar;
lParam: LongInt): BOOL; stdcall;
begin
EnumResourceNames(hmodule, restype, #_enumResNamesProc, lParam);
result := true;
end;
{$R '..\dummydll\dummydll.RES'}
Then I use a wait form:
unit Wait;
interface
uses
...
type
TWaitForm = class(TForm)
...
end;
var
WaitForm: TWaitForm;
implementation
{$R *.dfm}
...
end;
The wait form will be called by dynamically showing the form:
procedure ShowWaitForm;
begin
...
{ I use my own _CreateForm function because it solves many workarounds for
juicy stuff like half-modal windows (which can be hidden without user action),
miscellaneous deadlocks etc. and to allow the form to be shown in a shared PAS file
without the requirement to add it to every DPR file where the WaitForm API is used. }
WaitForm := _CreateForm(TWaitForm, {Application.MainForm}AParent) as TWaitForm;
WaitForm.Show;
...
end;
function _CreateForm(InstanceClass: TCustomFormClass; AParent: TCustomForm): TCustomForm;
var
LOwner: TComponent;
begin
if Assigned(AParent) then
begin
LOwner := AParent;
end
else if Assigned(Application) then
begin
LOwner := Application;
end
else
begin
LOwner := nil;
end;
result := InstanceClass.Create(LOwner);
end;
The error message at 2% of the customers:
Resource TWaitForm was not found
However, other forms are working.
There are 2 theories I can think of:
1) Did the resource translation corrupt the DLL file / part of the RCData section? (Maybe a bug in the WinAPI's UpdateResource ?)
2) Is there a problem with the dynamic showing of the wait form (since other "static" forms are shown?)

How to get/find the variable that caused Division By Zero error in delphi?

I know how to do basic exception handling. So i can raise a message on divide by zero using the 'try except' method.
What i would like to do is, find the variable that causes this error and then change its value on run time.
For Ex:
procedure Calculate();
var
a, b, c : Double;
begin
try
a := 4; //suppose i take this value from user and he enters 4
b := 0; //suppose i take this value from user and he enters 0
c := a/b;
ShowMessage(FloatToStr(c));
except
on E : EZeroDivide do
begin
ShowMessage('Exception message = '+E.Message);
//i am not sure how to identify that its variable 'b' that is causing the error and has to be changed by a default value
get(E....errorVaraiable);
E....errorVaraiable := 0.00001;
c := a/E....errorVariable;
ShowMessage(FloatToStr(c));
end;
end;
Please, can anyone help me with this?
Here's a modified version of your example that does what you want.
procedure Calculate();
var
a, b, c : Double;
begin
a := 4; //suppose i take this value from user and he enters 4
b := 0; //suppose i take this value from user and he enters 0
if IsZero(b) then
begin
ShowMessage('b cannot be 0')
end
else
begin
c := a/b;
ShowMessage(FloatToStr(c));
end;
end;