#214090 - 2022-04-26 04:56 PM
Issue with Array not ignoring empty lines
|
Robdutoit
Hey THIS is FUN
Registered: 2012-03-27
Posts: 363
Loc: London, England
|
I have created a very simple udf to read an ini file and install the printers listed in that ini file. However, for some reason, the code is returning an extra return or item in the array. I have had to exclude empty lines to get rid of that extra result.
Here is the udf
Function PrintInstall($Section)
$Handle = Freefilehandle ()
If $handle > 0
If Open ($handle, $iniFile) = 0
$PrintServer = ReadProfileString($iniFile, "PrintServer", "Printer")
$printerKeys = split(ReadProfileString($iniFile, $Section, ""),chr(10))
FOR each $printerKey in $printerKeys
IF $printerKey <> ""
$printer = ReadProfileString($iniFile, $Section, $printerKey)
? "Install the $Printer"
ENDIF
NEXT
IF Close($handle)
Beep
? "Error closing file!"
ENDIF
ELSE
? "Unable to open" + $iniFile
ENDIF
ELSE
? "Unable to obtain a free system handle."
ENDIF
EndFunction
I have to put this line in - IF $printerKey <> "" - otherwise the code returns an extra entry. I think it is something to do with empty lines in the ini file.
This is the ini file sections that this coding is reading
[PrintServer]
Printer = ServerName
[Accounting Printers]
printer1 = Ricoh printer2 = Sharp printer3 = Brother printer4 = Xerox
|
Top
|
|
|
|
#214093 - 2022-04-27 01:00 AM
Re: Issue with Array not ignoring empty lines
[Re: ShaneEP]
|
Allen
KiX Supporter
Registered: 2003-04-19
Posts: 4549
Loc: USA
|
I can confirm that I have seen this problem many times, and never once thought to post about it. I too have a line in my scripts that checks for a blank data to avoid the issue.
$printers = Split(ReadProfileString(@ScriptDir + "\test.ini", "Accounting Printers", ""), chr(10))
For Each $printerKey in $printers
if $printerkey<>""
? "Install the " + ReadProfileString(@ScriptDir + "\test.ini", "Accounting Printers", $printerKey)
endif
Next
get $
I think it might be a bug.
|
Top
|
|
|
|
#214094 - 2022-04-27 01:06 AM
Re: Issue with Array not ignoring empty lines
[Re: Allen]
|
Allen
KiX Supporter
Registered: 2003-04-19
Posts: 4549
Loc: USA
|
$printers = Split(ReadProfileString(@ScriptDir + "\test.ini", "Accounting Printers", ""), chr(10))
? ubound($printers)
Returns 4 instead of 3 using the provided test.ini.
The 4th result is what is causing the odd output...
|
Top
|
|
|
|
#214096 - 2022-04-27 03:07 PM
Re: Issue with Array not ignoring empty lines
[Re: Allen]
|
ShaneEP
MM club member
Registered: 2002-11-29
Posts: 2125
Loc: Tulsa, OK
|
Found an example in an old forum post, where someone used Left($string, -1) to trim a space off the end, and it works (Trim() also seems to work). Seems that the readprofilesstring adds an extra line feed at the end, which causes the split to add the extra array item. Why it fills that blank item with the keys names, I can't say. Odd that I've used INI's so much in the past and don't recall ever running into this issue at all. Could just be an age issue, lol.
$printers = Split(Left(ReadProfileString(@ScriptDir + "\test.ini", "AccountingPrinters", ""),-1), chr(10))
For Each $printerKey in $printers
? "Install the " + ReadProfileString(@ScriptDir + "\test.ini", "AccountingPrinters", $printerKey)
Next
get $
|
Top
|
|
|
|
#214097 - 2022-04-27 03:17 PM
Re: Issue with Array not ignoring empty lines
[Re: ShaneEP]
|
ShaneEP
MM club member
Registered: 2002-11-29
Posts: 2125
Loc: Tulsa, OK
|
Back to the original post...Below is how I would handle the PrintInstall function. This seems to return the correct count of printer, regardless of blank lines in the INI file.
$iniFile = @ScriptDir+"\test.ini"
$Section = "Accounting Printers"
PrintInstall($iniFile, $Section)
Function PrintInstall($iniFile, $Section)
$PrintServer = ReadProfileString($iniFile, "PrintServer", "Printer")
$printerKeys = Split(Left(ReadProfileString($iniFile, $Section, ""), -1),chr(10))
FOR each $printerKey in $printerKeys
$printer = ReadProfileString($iniFile, $Section, $printerKey)
? "Install the $Printer"
NEXT
EndFunction
|
Top
|
|
|
|
#214098 - 2022-04-27 03:28 PM
Re: Issue with Array not ignoring empty lines
[Re: ShaneEP]
|
mole
Getting the hang of it
Registered: 2003-01-01
Posts: 77
Loc: Indian Head, Maryland, USA
|
Could just be an age issue, lol.
My thought as well, as in my case, OLD age. I used to use INI's too and recall stumbling over this issue a few times. Getting trim() in the proper location seemed to solve the issue if my memory is working. Never crossed my mind it could be a bug, but chalked up to being a chemist and not a programmer!
_________________________
mole
Who is John Galt?
|
Top
|
|
|
|
#214103 - 2022-04-28 03:51 PM
Re: (NA) Re: Issue with Array not ignoring empty lines
[Re: Ruud van Velsen]
|
ShaneEP
MM club member
Registered: 2002-11-29
Posts: 2125
Loc: Tulsa, OK
|
Rob...I debated on which way to go...Using the Left(string, -1) route, vs the Trim() route, and would recommend sticking with just the Left() method. In my mind, this altered the return values the least, and would avoid any potential issues if there were ever intentional spaces at the beginning or ending of values. That's just my 2 cents, even though Trim() may seem simpler.
Ruud, thanks for the feedback. In my opinion it seems odd to get the extra carriage return, but I can also understand why it was programmed that way. At this point it might be best to leave it as is, as it may break a lot of existing code that currently works around it. Some improved documentation on that function may be the better solution, but I'll let others chime in. I am curious however as to how all the section key names found their way into that last empty array item in my example above. I would have expected it to be blank.
Install the Ricoh Install the Sharp Install the Brother Install the Xerox Install the printer1 printer2 printer3 printer4
|
Top
|
|
|
|
#214104 - 2022-04-28 04:49 PM
Re: (NA) Re: Issue with Array not ignoring empty lines
[Re: ShaneEP]
|
Robdutoit
Hey THIS is FUN
Registered: 2012-03-27
Posts: 363
Loc: London, England
|
Shane - Sorry my post should have read Left, not Left Trim. I am using the Left option as it worked perfectly and got rid of the extra if,endif statement. So I didn't try the trim method. I will edit that post shortly.
Rudd - I would suggest changing the code for readprofilestring to remove the extra carriage return as technically it is a bug and it would seem that over the years many people have had to develop a workaround for it. I would expect anyone wanting to upgrade to the latest version of Kixtart to read the Release Notes to see what has changed before updating to the latest Kixtart variable. So I wouldn't expect it to break code as people should be reading the release notes before updating.
I don't know if you noticed, but I also created two more posts (in addition to the @Producttype bug which is being fixed in kix 4.69). That was for @domain variable and the syntax differences between AddPrinterConnection and DelPrinterConnection just in case you want to review whether it is worth making any changes there. Thanks.
The posts in question are:
http://kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=214011#Post214011 http://kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=214008#Post214008
|
Top
|
|
|
|
#214109 - 2022-04-28 11:12 PM
Re: (NA) Re: Issue with Array not ignoring empty lines
[Re: ShaneEP]
|
Glenn Barnas
KiX Supporter
Registered: 2003-01-28
Posts: 4396
Loc: New Jersey
|
If you think about this, it isn't a bug. If you enumerate the sections or keys, each object returned is terminated by a newline:You are using LINE TERMINATORS as DELIMITERS and have 3 delimiters, thus you have FOUR fields.
To properly parse the values, you need to translate LINE TERMINATION to DELIMITERS by removing the last (excess) line terminator. That's precisely what EnumIni() does, along with some error handling.
; Get an INI enumeration of keys
$Tmp = ReadProfileString(@ScriptDir + '\test.ini', 'Accounting Printers', '')
; If we got data, remove the last line terminator and split on the \n delimiters
If Trim($Tmp)
$aData = Split(Left($Tmp, Len($Tmp) - 1), Chr(10))
EndIf
; Enumerate the array of section keys and get the values
For Each $Key In $aData
'Key: ' $Key ?
'Val: ' ReadProfileString(@ScriptDir + '\test.ini', 'Accounting Printers', $Key) ?
Next
Quit 0 This outputs exactly what you would expect:Key: printer1
Val: Ricoh
Key: printer2
Val: Sharp
Key: printer3
Val: Brother
Key: printer4
Val: Xerox
If you don't remove the last line terminator, you have an extra delimiter, the last field after that is empty, so you get an additional enumeration string.
Basic coding - understand your data structure before you use it!
Glenn
_________________________
Actually I am a Rocket Scientist!
|
Top
|
|
|
|
#214116 - 2022-04-29 05:57 PM
Re: (NA) Re: Issue with Array not ignoring empty lines
[Re: Robdutoit]
|
Glenn Barnas
KiX Supporter
Registered: 2003-01-28
Posts: 4396
Loc: New Jersey
|
Allen - it's not broken so a "fix" would potentially affect all code that properly processes the results and is not a good solution IMO. If anything, the documentation should be updated to clarify that this is not a DELIMITED string but a String LIST of newline-terminated values. The data isn't coming from "nowhere", it's specifically requested because of poor data handling. The documentation, in fact, doesn't reference the returned format at all.
Ruud - the line terminator is Newline (Chr(10)) and not Carriage Return (Chr(13) as you noted.
Rob - Yes, it is returning the intended result.
This is what's happening, and why you aren't processing the data correctly.
Here's a simple example to help it make sense: If you had a delimited string "A,B,C", you see that it's delimited with commas. There are TWO delimiters that result in 3 fields. The Split() on this string returns a 3-element array. Change this to "A,B,C," and you have 3 delimiters and 4 fields (the last is blank). A split on this returns a 4-element array. This is what you're getting from your code.
The result of ReadProfileString($File, $Section, '') does NOT return a delimited string. It returns a string list - a set of lines terminated with a Newline: Line1 {NL} Line2 {NL} Line3 {NL}
If you look at this as a string, you get this: "Line1 {NL}Line2 {NL}Line3 {NL}" - 3 delimiters so 4 fields. The problem is that this isn't intended to be a delimiter because it isn't just a string, it's a string that contains a set of lines. When you split this using Chr(10) (newline) as a DELIMITER, it includes the last LINE TERMINATOR character and adds an "unseen" empty field. You cannot interchange the use of delimiters and line terminators.
To properly use the output of the ReadProfileString enumeration, you MUST remove the last Line Terminator (because it isn't a delimiter!). THEN and only then can you use the Line Terminator character as a delimiter, because the string now looks like "Line1 {NL}Line2 {NL}Line3" - 2 Delimiters, thus 3 fields.
If you don't eliminate the last LINE TERMINATOR, you wind up with an empty value. Consider what's happening:; Get the keys defined in the section
$aKeys = Split(ReadProfileString($File, $Section, '')
; Enumerate the keys
; just display the result of ReadProfileString for each key - assume there are 3 keys
; but we don't strip the final Line Terminator, so we have 4 elements in the array.
ReadProfileString($File, $Section, $aKey[0]) ? ; Displays data from "Line 1" key
ReadProfileString($File, $Section, $aKey[1]) ? ; Displays data from "Line 2" key
ReadProfileString($File, $Section, $aKey[2]) ? ; Displays data from "Line 3" key
; You have 4 elements, the last is blank
ReadProfileString($File, $Section, $aKey[3]) ?
; Displays the enumeration of File,Section because $aKey[3] is null! so outputs:
; Line1
; Line2
; Line3 Basically, either trim the last character from the output of the enumeration or use a pre-built function like EnumIni.
The Trim() in my example is not required - it's simply there to create a Boolean result to proceed when any data is returned or skip if a null / empty string was returned. I could have also used an If Not @ERROR instead. The most important thing is first getting the String List, then (if not empty), use Left() and Len() to trim the last line termination character. That logic essentially converts a String List to a Delimited String that can be split into the correct number of fields.
Shane's code is correct because he's removing the final delimiter, albeit through a "golf" mechanism that would not be obvious or intuitive. The left(String, -#) can trim a string by implicitly referencing the length from the end, so his and my examples provide the same result. I would not allow his example in production as it is not a clear/documented method, but something that was "discovered" and leveraged to reduce coding character counts for our Kix-golf games. When his code displays the excess list, it's when he didn't trim the final line terminator.
Finally, Trim() has no bearing whatsoever on processing INI files. You can have blank lines and comments (; or #) to your hearts content in an INI file and there will be zero impact on reading, writing, or enumerating it.
_________________________
Actually I am a Rocket Scientist!
|
Top
|
|
|
|
Moderator: Jochen, Allen, Radimus, Glenn Barnas, ShaneEP, Ruud van Velsen, Arend_, Mart
|
0 registered
and 920 anonymous users online.
|
|
|