I wonder, why this code does not catch a 'Disk Full' error like it should?
This is important because the user may lose their data if they do not notice that the saving failed.
I don't get this...
procedure TForm2.Button1Click(Sender: TObject);
var
Writer: TStreamWriter;
n : integer;
begin
Writer := TStreamWriter.Create('MyUTF8Text.txt', false, TEncoding.UTF8);
Try //Finally
Try //Except
for n := 1 to 1000 do
begin
Writer.WriteLine('Testing text writing to the UTF-8 file.');
end;
Except
on E: Exception do
begin
ShowMessage('Exception Class name: ' + E.ClassName);
ShowMessage('Exception Message: ' + E.Message);
end;
end; // except
Finally
Writer.Free();
End; //finally
end;
"BTW. 'Writer might not be initialized' warning, is it serious really?"
Edit: There was this warning because TStreamWriter.Create was after TRY.
Thanks for your advice I corrected that line of code to the correct location before(!) the TRY.
Try This:
procedure WriteStream;
var
Writer: TStreamWriter;
fs: TFileStream;
s: String;
n, bytesWritten : integer;
begin
bytesWritten := 0;
fs := TFileStream.Create('MyUTF8Text.txt', fmCreate);
try
//avoid warning by initiaizing before try
Writer := TStreamWriter.Create(fs);
try //Finally
try //Except
for n := 1 to 10 do
begin
s := 'Testing text writing to the UTF-8 file.' + '#13#10';
//keep count of bytes written
bytesWritten := bytesWritten + TEncoding.UTF8.GetByteCount(s);
Writer.Write(s);
end;
Writer.Flush;
Writer.Close;
//Check stream size to make sure all bytes written
if bytesWritten <> Writer.BaseStream.Size then
raise Exception.Create(String.Format('Expected %d bytes, wrote %d', [Writer.BaseStream.Size, bytesWritten]));
except
on E: Exception do
begin
Showmessage('Exception Class name: ' + E.ClassName);
Showmessage('Exception Message: ' + E.Message);
end;
end; // except
finally
Writer.Free; // Will only free if it has been constructed
end; //finally
finally
fs.Free;
end;
end;
Related
What is wrong in this code ? I don't understend, if I remove the "Try" my app dont open, and if don't remove always appear "need login" ...
procedure TF_login.FormActivate(Sender: TObject);
var
Result: Integer;
TextFile: TStringList;
VarArquivo: string;
text: string;
dataI, dataF : string;
begin
TextFile := TStringList.Create;
VarArquivo := System.IOUtils.TPath.GetDocumentsPath + PathDelim + 'Limit.txt';
try
TextFile.LoadFromFile(VarArquivo);
text := TextFile.Text;
// ShowMessage(TextFile.Text); // there is the text
// ShowMessage(text); // there is the text
dataI := FormatDateTime('dd/mm/yyyy', Now);
dataF := FormatDateTime('dd/mm/yyyy', StrToDate(text));
Result := CompareDate(StrToDate(dataI), StrToDate(dataF));
ShowMessage(dataF +' data f');
ShowMessage(dataI +' data I');
if ( Result = LessThanValue ) then
begin
ShowMessage('data F low');
end
else
begin
ShowMessage('data F high');
F_inicio.Show;
end;
FreeAndNil(TextFile);
except on E:
Exception do ShowMessage('An error happened!' + sLineBreak + '[' +
E.ClassName + '] ' + E.Message);
end;
end;
The error : [EConvertError] '09/11/2019' is not a valid date
to create the file, i do:
procedure TF_login.btn_entrarClick(Sender: TObject);
var
data : tdatetime;
Resposta, data_s: string;
begin
PathFile := System.IOUtils.TPath.GetDocumentsPath;
NameFile := 'Limit.txt';
data := Now; //data actual
data := IncMonth(data, 2);
data_s := FormatDateTime('dd/mm/yyyy', data);
TFile.WriteAllText(TPath.Combine(PathFile, NameFile), data_s );
F_inicio.Show;
end;
The file exists, because the first (and second) ShowMessage (what is commented) show me the "09/11/19" but the third and fourth not appear to me...
OBS: Delphi 10.3 (RIO), Plataform: Android
There are a couple of things that you should change in your code:
procedure TF_login.FormActivate(Sender: TObject);
var
TextFile: TStringList;
VarArquivo: string;
text: string;
dataI, dataF : string;
begin
// If an exception (unlikely, but on principle) happens in your VarArquivo
// assignment, then the original version will leak the allocated TStringList.
// Always place the TRY right after allocation of a memory block. That way
// you ensure that the FINALLY block will always release the allocated
// memory. Also, always include a FINALLY block to release the memory. Don't
// count on your code to reach the FreeAndNIL code (it doesn't in this
// instance, as you can see) to make sure that you actually release the
// memory.
VarArquivo := System.IOUtils.TPath.GetDocumentsPath + PathDelim + 'Limit.txt';
TextFile := TStringList.Create;
try // - except
try // - finally
TextFile.LoadFromFile(VarArquivo);
text := TextFile.Text;
// ShowMessage(TextFile.Text); // there is the text
// ShowMessage(text); // there is the text
dataI := FormatDateTime('yyyy/mm/dd', Date);
dataF := FormatDateTime('yyyy/mm/dd', StrToDate(text));
ShowMessage(dataF +' data f');
ShowMessage(dataI +' data I');
if ( dataF < dataI ) then
begin
ShowMessage('data F low');
end
else
begin
ShowMessage('data F high');
F_inicio.Show;
end;
finally
FreeAndNil(TextFile);
end
except
// NEVER just "eat" an exception. Especially not while developing the
// application.
// Always either log the exception or show it to the user.
on E:Exception do ShowMessage('Exception '+E.ClassName+': '+E.Message+#13#10+
'need login');
end;
end;
Now - if you do this, what exception and error message is shown. This is needed in order to properly diagnose the error. Perhaps you can even figure it out for yourself when you see what exactly goes wrong...
I have an exception logger that logs all exceptions to a log file:
class function TLogger.LogException (ACaller: String; E: Exception): Boolean;
var
LogFilename, tmp: string;
LogFile: TextFile;
appsettings: TApplicationSettings;
begin
// prepare log file
appsettings:=TApplicationSettings.Create;
try
tmp:=appsettings.ErrorLogsLocation;
finally
FreeAndNil(appsettings);
end;
if NOT (DirectoryExists(tmp)) then
CreateDir(tmp);
//We create a new log file for every day to help with file size issues
LogFilename:=IncludeTrailingPathDelimiter (tmp) + 'LJErrors_' + FormatDateTime('yyyy-mm-dd', Now) +'.log';
try
AssignFile (LogFile, LogFilename);
if FileExists (LogFilename) then
Append (LogFile) // open existing file
else
Rewrite (LogFile); // create a new one
// write to the file and show error
Writeln(LogFile, CRLF+CRLF);
Writeln (LogFile, 'Application Path: ' + ExtractFilePath(ParamStr (0)));
Writeln (LogFile, 'Application Version: ' + TUtility.GetAppVersionString);
Writeln (LogFile, 'Operating System: ' + TUtility.GetOSInfo);
Writeln (LogFile, 'Error occurred at: ' + FormatDateTime ('dd-mmm-yyyy hh:nn:ss AM/PM', Now));
Writeln (LogFile, 'Logged By: ' + ACaller);
Writeln (LogFile, 'Unit Name: ' + E.UnitName);
Writeln (LogFile, 'Error Message: ' + E.Message);
Writeln (LogFile, 'Error Class: ' + E.ClassName);
Writeln (LogFile, 'Base Exception Error: ' + E.BaseException.Message);
Writeln (LogFile, 'Base Exception Class: ' + E.BaseException.ClassName);
Writeln (LogFile, 'Stack Trace: ' + E.StackTrace);
Result:=True;
finally
// close the file
CloseFile (LogFile);
end;
end;
To enable the Exception.StackTrace, I am using JCLDebug as outlined in: https://blog.gurock.com/working-with-delphis-new-exception-stacktrace.
unit StackTrace;
interface
uses
SysUtils, Classes, JclDebug;
implementation
function GetExceptionStackInfoProc(P: PExceptionRecord): Pointer;
var
LLines: TStringList;
LText: String;
LResult: PChar;
begin
LLines := TStringList.Create;
try
JclLastExceptStackListToStrings(LLines, True, True, True, True);
LText := LLines.Text;
LResult := StrAlloc(Length(LText));
StrCopy(LResult, PChar(LText));
Result := LResult;
finally
LLines.Free;
end;
end;
function GetStackInfoStringProc(Info: Pointer): string;
begin
Result := string(PChar(Info));
end;
procedure CleanUpStackInfoProc(Info: Pointer);
begin
StrDispose(PChar(Info));
end;
initialization
// Start the Jcl exception tracking and register our Exception
// stack trace provider.
if JclStartExceptionTracking then
begin
Exception.GetExceptionStackInfoProc := GetExceptionStackInfoProc;
Exception.GetStackInfoStringProc := GetStackInfoStringProc;
Exception.CleanUpStackInfoProc := CleanUpStackInfoProc;
end;
finalization
// Stop Jcl exception tracking and unregister our provider.
if JclExceptionTrackingActive then
begin
Exception.GetExceptionStackInfoProc := nil;
Exception.GetStackInfoStringProc := nil;
Exception.CleanUpStackInfoProc := nil;
JclStopExceptionTracking;
end;
end.
I have enabled the following options in Project Options:
Compiling: Debug Information, Local Symbols, Symbol Reference Info, Use debug .dcus, Use imported data references
Linking: Debug Information
However, when I trigger an exception, even though the GetExceptionStackInfoProc gets triggered, the Exception.StackInfo is always an empty string. Any ideas as to what I may be missing?
UPDATE 20170504: Thanks to Stefan Glienke for the solution. For completeness, I am including the code here for the changed GetExceptionStackInfoProc procedure that incorporates his solution:
function GetExceptionStackInfoProc(P: PExceptionRecord): Pointer;
var
LLines: TStringList;
LText: String;
LResult: PChar;
jcl_sil: TJclStackInfoList;
begin
LLines := TStringList.Create;
try
jcl_sil:=TJclStackInfoList.Create(True, 7, p.ExceptAddr, False, nil, nil);
try
jcl_sil.AddToStrings(LLines, true, true, true, true);
finally
FreeAndNil(jcl_sil);
end;
LText := LLines.Text;
LResult := StrAlloc(Length(LText));
StrCopy(LResult, PChar(LText));
Result := LResult;
finally
LLines.Free;
end;
end;
You need to create it yourself (and free it):
TJclStackInfoList.Create(True, 7, p.ExceptAddr, False, nil, nil);
On that instance you can call AddToStrings.
For more info take a look at JclDebug.GetExceptionStackInfo. The value for AIgnoreLevels is taken from there but in my tests I always had one entry too much so I increased it by one.
The following it a snippet from what I get from a Button1 application calling RaiseLastOSError;
[0042BFE5] System.SysUtils.Sysutils.RaiseLastOSError$qqrix20System.UnicodeString (Line 24937, "System.SysUtils.pas")
[0042BF5B] System.SysUtils.Sysutils.RaiseLastOSError$qqrv (Line 24919, "System.SysUtils.pas")
[005CD004] Unit85.TForm85.Button1Click$qqrp14System.TObject (Line 28, "Unit85.pas")
[0051D567] Vcl.Controls.TControl.Click$qqrv (Line 7429, "Vcl.Controls.pas")
[00534CDA] Vcl.StdCtrls.Stdctrls.TCustomButton.Click$qqrv (Line 5434, "Vcl.StdCtrls.pas")
If you actually read the JclDebug unit, you will see that initialization and finalization sections have registration and deregistration code, so simply placing the unit into the uses section is enough. (See SetupExceptionProcs)
(edit This question has received few downvotes. I don't know a reason and still cannot see what's wrong with it. I can edit this if downvoters could comment about what they wish to see better written or lack of valuable info I have not given).
I have Intermec PM4i label printer and Generic text print driver. I am able to print text file script from Notepad or Delphi calls ShellExecute('printto',..) shellapi function.
I found few raw printing examples but none work. How can Delphi app print to Generic_text_driver without shellapi function? This is not GDI printer.canvas capable driver.
I have tried "everything" even legacy PASSTHROUGH printing examples. Only working method is Notepad.exe or shellexecute('printto', 'tempfile.txt',...) which I guess is using Notepad internally. I can see notepad printing dialog flashing on screen. I would like to control this directly from Delphi application.
This printFileToGeneric did not work.
// https://groups.google.com/forum/#!topic/borland.public.delphi.winapi/viHjsTf5EqA
Procedure PrintFileToGeneric(Const sFileName, printerName, docName: string; ejectPage: boolean);
Const
BufSize = 16384;
Var
Count : Integer;
BytesWritten: DWORD;
hPrinter: THandle;
DocInfo: TDocInfo1;
f: file;
Buffer: Pointer;
ch: Char;
Begin
If not WinSpool.OpenPrinter(PChar(printerName), hPrinter, nil) Then
raise Exception.Create('Printer not found');
Try
DocInfo.pDocName := PChar(docName);
DocInfo.pOutputFile := nil;
DocInfo.pDatatype := 'RAW';
If StartDocPrinter(hPrinter, 1, #DocInfo) = 0 Then
Raise Exception.Create('StartDocPrinter failed');
Try
If not StartPagePrinter(hPrinter) Then
Raise Exception.Create('StartPagePrinter failed');
System.Assign(f, sFileName);
Try
Reset(f, 1);
GetMem(Buffer, BufSize);
Try
While not eof(f) Do Begin
Blockread(f, Buffer^, BufSize, Count);
If Count > 0 Then Begin
If not WritePrinter(hPrinter, Buffer, Count, BytesWritten) Then
Raise Exception.Create('WritePrinter failed');
End;
End;
Finally
If ejectPage Then Begin
ch:= #12;
WritePrinter( hPrinter, #ch, 1, BytesWritten );
End;
FreeMem(Buffer, BufSize);
End;
Finally
EndPagePrinter(hPrinter);
System.Closefile( f );
End;
Finally
EndDocPrinter(hPrinter);
End;
Finally
WinSpool.ClosePrinter(hPrinter);
End;
End;
The following prtRaw helper util example did not work.
prtRaw.StartRawPrintJob/StartRawPrintPage/PrintRawData/EndRawPrintPage/EndRawPrintJob
http://www.swissdelphicenter.ch/torry/showcode.php?id=940
The following AssignPrn method did not work.
function testPrintText(params: TStrings): Integer;
var
stemp:String;
idx: Integer;
pOutput: TextFile;
begin
Result:=0;
idx := getPrinterIndexByName( params.Values['printer'] );
if (idx<0) then Raise Exception.Create('Printer not found');
WriteLn('Use printer(text) ' + IntToStr(idx) + ' ' + Printer.Printers[idx] );
Printer.PrinterIndex := idx;
stemp := params.Values['jobname'];
if (stemp='') then stemp:='rawtextprint';
Printer.Title:=stemp;
AssignPrn(pOutput);
ReWrite(pOutput);
stemp := 'INPUT ON'+#10;
stemp := stemp + 'NASC 1252'+#10;
stemp := stemp + 'BF OFF'+#10;
stemp := stemp + 'PP 30,480:FT "Swiss 721 BT",8,0,100'+#10;
stemp := stemp + 'PT "Test text 30,480 position ABC'+#10;
Write(pOutput, stemp);
//Write(pOutput, 'INPUT ON:');
//Write(pOutput, 'NASC 1252:');
//Write(pOutput, 'BF OFF:');
//Write(pOutput, 'PP 30,480:FT "Swiss 721 BT",8,0,100:');
//Write(pOutput, 'PT "Test text 30,480 position ABC":');
//Write(pOutput, 'Text line 3 goes here#13#10');
//Write(pOutput, 'Text line 4 goes here#13#10');
CloseFile(pOutput);
end;
This Printer.Canvas did not print anything, it should have had print something because Notepad is internally using GDI printout. Something is missing here.
function testPrintGDI(params: TStrings): Integer;
var
filename, docName:String;
idx: Integer;
lines: TStrings;
begin
Result:=0;
idx := getPrinterIndexByName( params.Values['printer'] );
if (idx<0) then Raise Exception.Create('Printer not found');
WriteLn('Use printer(gdi) ' + IntToStr(idx) + ' ' + Printer.Printers[idx] );
docName := params.Values['jobname'];
if (docName='') then docName:='rawtextprint';
filename := params.values['input'];
If Not FileExists(filename) then Raise Exception.Create('input file not found');
Printer.PrinterIndex := idx;
Printer.Title := docName;
Printer.BeginDoc;
lines := readTextLines(filename);
try
for idx := 0 to lines.Count-1 do begin
Printer.Canvas.TextOut(10, 10*idx, lines[idx]);
end;
finally
FreeAndNil(lines);
Printer.EndDoc;
end;
end;
Only three methods working are printing from Notepad.exe, Delphi ShellExecute call or open a raw TCP socket to IP:PORT address and write text lines to a socket outputstream.
function testPrintPrintTo(params: TStrings): Integer;
var
filename, printerName:String;
idx: Integer;
exInfo: TShellExecuteInfo;
device,driver,port: array[0..255] of Char;
hDeviceMode: THandle;
timeout:Integer;
//iRet: Cardinal;
begin
Result:=0;
idx := getPrinterIndexByName( params.Values['printer'] );
if (idx<0) then Raise Exception.Create('Printer not found');
WriteLn('Use printer(printto) ' + IntToStr(idx) + ' ' + Printer.Printers[idx] );
filename := params.values['input'];
If Not FileExists(filename) then Raise Exception.Create('input file not found');
filename := uCommon.absoluteFilePath(filename);
Printer.PrinterIndex := idx;
Printer.GetPrinter(device, driver, port, hDeviceMode);
printerName := Format('"%s" "%s" "%s"', [device, driver, port]);
FillChar(exInfo, SizeOf(exInfo), 0);
with exInfo do begin
cbSize := SizeOf(exInfo);
fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
Wnd := 0;
exInfo.lpVerb := 'printto';
exInfo.lpParameters := PChar(printerName);
lpFile := PChar(filename);
lpDirectory := nil;
nShow := SW_HIDE;
end;
WriteLn('printto ' + printerName);
if Not ShellExecuteEx(#exInfo) then begin
Raise Exception.Create('ShellExecuteEx failed');
exit;
end;
try
timeout := 30000;
while WaitForSingleObject(exInfo.hProcess, 50) <> WAIT_OBJECT_0 do begin
Writeln('wait timeout ' + IntToStr(timeout));
dec(timeout, 50);
if (timeout<1) then break;
end;
finally
CloseHandle(exInfo.hProcess);
end;
{iRet:=ShellExecute(0, 'printto',
PChar(filename),
PChar(printerName), //Printer.Printers[idx]),
nil, //PChar(filepath),
SW_HIDE // SW_SHOWNORMAL
);
Writeln('printto return code ' + IntToStr(iRet)); // >32 OK }
end;
you can use this procedure:
where the LabelFile is the full path of the label file,we are using this code and works with generic text driver printer and the printer is set as the default printer.
it works with zebra printer and windows xp operating system.
i hope this will help you.
function PrintLabel(LabelName: String): Boolean;
var
EfsLabel,dummy: TextFile;
ch : Char;
begin
try
try
Result:= False;
AssignFile(EfsLabel,LabelName);
Reset(EfsLabel);
Assignprn(dummy);
ReWrite(Dummy);
While not Eof(EfsLabel) do
begin
Read(EfsLabel, Ch);
Write(dummy, Ch);
end;
Result:= True;
except
Result:=False;
LogMessage('Error Printing Label',[],LOG_ERROR);
end;
finally
CloseFile(EfsLabel);
CloseFile(dummy);
end;
end;
You are able to print to this printer from Notepad. Notepad prints using the standard GDI mechanism. If Notepad can do this then so can you. Use Printer.BeginDoc, Printer.Canvas etc. to print to this printer.
I have a TCP "Server", that is sending data every 20 seconds. I am trying to receive this data and display the result. Currently, the data is displayed 3 times instead of once. It then waits another 20 seconds, and again displays the data 3 times.
with Client do
begin
Connect;
while True do
begin
try
if IOHandler.CheckForDataOnSource(1000) then
begin
WriteLn('Bytes available: ' IntToStr(IOHandler.InputBuffer.Size));
IOHandler.ReadBytes(buffer, IOHandler.InputBuffer.Size);
end;
except
on e : exception do
writeln(e.message);
end;
if(buffer <> nil) then
begin
SetString(msg, PAnsiChar(#buffer[0], length(buffer));
buffer := nil;
IOHandler.InputBuffer.Clear;
end;
write(msg);
end;
end;
You are calling write(msg) on every loop iteration regardless of whether you actually read any new data or not. You are resetting your buffer to nil after assigning it to msg, but you are not resetting msg to '' after writing it. So each loop iteration is writing whatever msg already holds until you assign a new value to it.
Try this code instead:
with Client do
begin
Connect;
buffer := nil;
repeat
try
if IOHandler.CheckForDataOnSource(1000) then
begin
WriteLn('Bytes available: ' + IntToStr(IOHandler.InputBuffer.Size));
IOHandler.ReadBytes(buffer, IOHandler.InputBuffer.Size);
SetString(msg, PAnsiChar(buffer), Length(buffer));
Write(msg);
buffer := nil;
end;
except
on E: Exception do
WriteLn(E.Message);
end;
until not Connected;
end;
Which can be simplified to this:
with Client do
begin
Connect;
buffer := nil;
repeat
try
IOHandler.ReadBytes(buffer, -1);
WriteLn('Bytes read: ' + Length(buffer));
SetString(msg, PAnsiChar(buffer), Length(buffer));
Write(msg);
buffer := nil;
except
on E: Exception do
WriteLn(E.Message);
end;
until not Connected;
end;
Or even:
with Client do
begin
Connect;
repeat
try
if IOHandler.CheckForDataOnSource(IdTimeoutDefault) then
begin
WriteLn('Bytes available: ' + IntToStr(IOHandler.InputBuffer.Size));
Write(IOHandler.InputBufferAsString(en8bit));
end;
except
on E: Exception do
WriteLn(E.Message);
end;
until not Connected;
end;
I fixed the problem I was having, by moving the write(msg) into the if statement checking if the buffer is not nil.
with Client do
begin
Connect;
while True do
begin
try
if IOHandler.CheckForDataOnSource(1000) then
begin
WriteLn('Bytes available: ' IntToStr(IOHandler.InputBuffer.Size));
IOHandler.ReadBytes(buffer, IOHandler.InputBuffer.Size);
end;
except
on e : exception do
writeln(e.message);
end;
if(buffer <> nil) then
begin
SetString(msg, PAnsiChar(#buffer[0], length(buffer));
buffer := nil;
IOHandler.InputBuffer.Clear;
write(msg); {only write out the message when the buffer is not nil}
end;
end;
end;
This program raises an I/O 104 error on EoF when first entering the while loop.
The purpose of the program is to look up if a username is already taken. The existing usernames are stored in a text file.
procedure TForm1.btnRegisterClick(Sender: TObject);
begin
sCUser := edtUserName.Text;
AssignFile(tNames, 'Names.txt');
begin
try
Reset(tNames);
except
ShowMessage('File not found');
Exit;
end;
end;
rewrite(tNames);
while not EoF(tNames) do // I get a I/O 104 Error here `
begin
Readln(tNames, sLine);
iPosComme := Pos(',', sLine);
sUser := Copy(sLine, 1, iPosComme - 1);
Delete(sLine, 1, iPosComme - 1);
if sCUser = sUser then begin
ShowMessage('Username taken');
end
else
begin
rewrite(tNames);
Writeln(tNames, sCUser + ',' + '0');
CloseFile(tNames);
end;
end;
end;
Remove the call to Rewrite()before Eof(). Even if you were not getting an IO error, your code would still fail because Rewrite() closes the file you opened with Reset() and then it creates a new bank file, so Eof() would always be True.
Update: error 104 is file not open for input, which means Reset() is not opening the file but is not raising an exception (which sounds like an RTL bug if Eof() is raising an exception, indicating that {I+} is active).
In any case, using AssignFile() and related routines is the old way to do file I/O. You should use newer techniques, like FileOpen() with FileRead(), TFileStream with TStreamReader, TStringList, etc...
Update: your loop logic is wrong. You are comparing only the first line. If it does not match the user, you are wiping out the file, writing the user to a new file, closing the file, and then continuing the loop. EoF() will then fail at that point. You need to rewrite your loop to the following:
procedure TForm1.btnRegisterClick(Sender: TObject
var
SCUser, sUser: String;
tNames: TextFile;
iPosComme: Integer;
Found: Boolean;
begin
sCUser := edtUserName.Text;
AssignFile(tNames,'Names.txt');
try
Reset(tNames);
except
ShowMessage('File not found');
Exit;
end;
try
Found := False;
while not EoF(tNames) do
begin
Readln(tNames,sLine);
iPosComme := Pos(',', sLine);
sUser := Copy(sLine ,1,iPosComme -1);
if sCUser = sUser then
begin
ShowMessage('Username taken') ;
Found := True;
Break;
end;
end;
if not Found then
Writeln(tNames,sCUser + ',0');
finally
CloseFile(tNames);
end;
end;
For the sake of completeness, this Version works for me, but it is hard to guess what the code is intended to do. Especially the while loop seems a bit displaced, since the file will contain exactly one line after the rewrite-case has ben hit once.
program wtf;
{$APPTYPE CONSOLE}
{$I+}
uses
SysUtils;
procedure Sample( sCUser : string);
var sUser, sLine : string;
iPosComme : Integer;
tnames : textfile;
begin
AssignFile(tNames,'Names.txt');
try
Reset(tNames);
except
Writeln('File not found');
Exit;
end;
while not EoF(tNames) do
begin
Readln(tNames,sLine);
iPosComme := Pos(',', sLine);
sUser := Copy(sLine ,1,iPosComme -1);
Delete( sLine,1, iPosComme -1);
if sCuser = sUser then begin
Writeln('Username taken') ;
end
else begin
Rewrite(tNames);
Writeln(tNames,sCUser + ',' + '0');
CloseFile(tNames);
Break; // file has been overwritten and closed
end;
end;
end;
begin
try
Sample('foobar');
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;
end.
I wrote a version of this method that uses the newer TStreamReader and TStreamWriter classes.
This won't work with Delphi 7 of course, it's just to show how this could be done in newer versions of Delphi.
The code was heavily inspired by Remys answer.
procedure TForm1.btnRegisterClick(Sender: TObject);
var
Stream: TStream;
Reader: TStreamReader;
Writer: TStreamWriter;
Columns: TStringList;
UserName: string;
Found: Boolean;
FileName: string;
Encoding: TEncoding;
begin
FileName := ExpandFileName('Names.txt'); // An absolute path would be even better
UserName := edtUsername.Text;
Found := False;
Encoding := TEncoding.Default; // or another encoding, e.g. TEncoding.Unicode for Unicode
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Reader := TStreamReader.Create(Stream, Encoding);
try
Columns := TStringList.Create;
try
Columns.Delimiter := ',';
Columns.StrictDelimiter := True; // or False, depending on the file format
while not Reader.EndOfStream do
begin
Columns.DelimitedText := Reader.ReadLine;
if Columns.Count > 0 then
begin
if AnsiSameStr(Columns[0], UserName) then // or AnsiSameText if UserName is not case-sensitive
begin
ShowMessage('Username taken') ;
Found := True;
Break;
end;
end;
end;
finally
Columns.Free;
end;
finally
Reader.Free;
end;
finally
Stream.Free;
end;
if not Found then
begin
Writer := TStreamWriter.Create(FileName, True, Encoding);
try
// Warning: This will cause problems when the file does not end with a new line
Writer.WriteLine(UserName + ',0');
finally
Writer.Free;
end;
end;
end;
If performance and memory usage are not a concern:
procedure TForm1.btnRegisterClick(Sender: TObject);
var
Rows: TStringList;
Columns: TStringList;
UserName: string;
Found: Boolean;
FileName: string;
Encoding: TEncoding;
Row: string;
begin
FileName := ExpandFileName('Names.txt'); // An absolute path would be even better
UserName := edtUsername.Text;
Found := False;
Encoding := TEncoding.Default; // or another encoding, e.g. TEncoding.Unicode for Unicode
Rows := TStringList.Create;
try
Rows.LoadFromFile(FileName, Encoding);
Columns := TStringList.Create;
try
Columns.Delimiter := ',';
Columns.StrictDelimiter := True; // or False, depending on the file format
for Row in Rows do
begin
Columns.DelimitedText := Row;
if Columns.Count > 0 then
begin
if AnsiSameStr(Columns[0], UserName) then // or AnsiSameText if UserName is not case-sensitive
begin
ShowMessage('Username taken') ;
Found := True;
Break;
end;
end;
end;
finally
Columns.Free;
end;
if not Found then
begin
Rows.Add(UserName + ',0');
Rows.SaveToFile(FileName, Encoding);
end;
finally
Rows.Free;
end;
end;
This solution can be adapted to Delphi 7 by removing the Encoding variable.
If it's part of a bigger database it should be stored in a real database management system rather than a text file.