Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Literal comparison oddity

Tags:

batch-file

So - here's the code

@echo off
setlocal

for %%a in (a A j J z Z) do for %%c in (d D) do if "%%c" geq "%%a" (echo "%%c" geq "%%a") else (echo "%%c" lss "%%a")

for %%a in (Blue blue BLUE Red red RED) do for %%c in (Pink pink PINK) do if "%%c" geq "%%a" (echo "%%c" geq "%%a") else (echo "%%c" lss "%%a")

Here's the result:

Microsoft Windows [Version 10.0.19042.804]
(c) 2020 Microsoft Corporation. All rights reserved.

"d" geq "a"
"D" geq "a"
"d" geq "A"
"D" geq "A"
"d" lss "j"
"D" lss "j"
"d" lss "J"
"D" lss "J"
"d" lss "z"
"D" lss "z"
"d" lss "Z"
"D" lss "Z"
"Pink" geq "Blue"
"pink" geq "Blue"
"PINK" geq "Blue"
"Pink" geq "blue"
"pink" geq "blue"
"PINK" geq "blue"
"Pink" geq "BLUE"
"pink" geq "BLUE"
"PINK" geq "BLUE"
"Pink" lss "Red"
"pink" lss "Red"
"PINK" lss "Red"
"Pink" lss "red"
"pink" lss "red"
"PINK" lss "red"
"Pink" lss "RED"
"pink" lss "RED"
"PINK" lss "RED"

(I just cut/paste/censored the screen to show the Windows version)

So - I'm about to go to do my grocery shopping (since its ~midnight) which will allow me to clear my head.

Am I seeing things? Doesn't batch do an ASCII comparison any more? I recall it used to.

AFAICS, if has suddenly decided to automatically do a case-insensitive comparison. That'll break many a SO solution.


So - bulk responses. I'm still trying to process it all.

The base problem:

I have a label printer which has 5 inbuilt dot-matrix fonts and allows a multiplier to be assigned to both the X and Y dimension. Obviously, applying 1 for the Xmultiplier and 6 for Y produces ugly tall-and-thin characters. Reversing these produces ugly squat characters. Also, not all multipliers (1-9) are available in each direction.

I therefore have a table of "acceptable" X-Y multiplier-pairs which do not produce obviously-distorted characters. Since I'm dealing with monospaced fonts, each font & multiplier-pair yields a coverage per character, which I want to maximise. Applying each possible combination to resolve the maximum coverage is a simple process, allowing the selection of the font and multiplier for each element of the label.

The fly in the ointment is that one font is upper-case only, so I wanted to apply an exclusion for that font if the element contains a lower-case letter.

The code I used for that exclusion was to apply an if test to each character %%c

 if "%%c" geq "a" if "%%c" leq "z" set "islower=Y"

BUT this doesn't work as advertised. It will set islower regardless of case. So the caps-only font was always excluded where the text contained alphas and therefore I never observed its being used. Oops.

Hence this question.

I've been doing many experiments as well as catching up on beauty-sleep (which I need) for the past few days, frankly dreading the volume of responses and comments.

Conclusions:

/i is redacted from the if if it's the first token.
not is redacted from the if if it's then the first token.

  • hence beware if string1==string2 if string1 is resolved to not

We're then left with string1 operator string2 and a complex relationship between the operator used and the precise format of the strings.

== is the simplest. There is no requirement for == to be preceded or succeeded by separators. The strings are compared alphabetically, hence you need the /i switch to perform a case-insensitive comparison.

equ and its family is where things get more complicated. Each must be preceded and succeeded by separators. The characteristics of the comparison made depends on the structure of the operands.

  • the operands may be strings, "quoted strings", pure-decimal (digits 0-9) or pure-octal (leading 0, digits 0-7)
  • In the case of pure-numeric arguments, the arguments are converted to binary, and the results are compared.
    • Hence IF 066 equ 54 evaluates as TRUE because 066-octal equals 54 decimal. == predictably evaluates this as FALSE
  • In the case of quoted-decimal-strings IF "102" gtr "94" evaluates as FALSE because 1 is not greater than 9
  • With the gtr geq lss leq operators on strings, operation becomes truly bizarre. CAT gtr cat (quoted or no) evaluates as TRUE, as do dog gtr cat and "dog" gtr "cat", regardless of case of either operand.
    • Even more outlandish as everyone knows that cat is greater than dog.

Sorry - It's really all too complicated for me. I'm off for a rest.

Unfortunately, this problem really doesn't seem to fit SO's Q&A format.

like image 352
Magoo Avatar asked Nov 06 '22 02:11

Magoo


1 Answers

As already described via comments, Strings are compared on a character by character basis, with the comparison returning it's value after the first non-matching character or the last character in the shortest string is encountered.

The value of each character is converted into a non-locale specific binary value as an unsigned char. From IBM's knowledge Centre:

The relation between the strings is determined by subtracting: string1[i] - string2[i], as i increases from 0 to strlen of the smaller string. The sign of a nonzero return value is determined by the sign of the difference between the values of the first pair of bytes (both interpreted as type unsigned char)

References:
docs.microsoft
IBM knowledge Centre
C++

A simple set of tests that demonstrates the returns that should be expected for the manner in which strcmp assesses strings:

IF "Pink" equ "pink" (Echo true) else Echo false
false
IF "Pink" LSS "pink" (Echo true) else Echo false
false
IF "Pink" GTR "pink" (Echo true) else Echo false
true
IF "Pink" GTR "pinky" (Echo true) else Echo false
false
IF "Pink" lss "pinky" (Echo true) else Echo false
true
IF "Pink" GTR "Pinky" (Echo true) else Echo false
false
IF "Pink" GTR "pinky" (Echo true) else Echo false
false

The confusion arises due to the assesment disregarding characters beyond the first non matching character:

IF "Pink" lss "aaaaa" (Echo true) else Echo false
false
IF "Pink" GTR "Z" (Echo true) else Echo false
false

false is returned as soon as nonmatching character is encountered, rendering string length irrelevent in the comparison. In the first of the above two examples, false is returned by strcmp as soon as P is evaluated as being GTR than a. The result would be the same if the comparison was: IF "Pink" lss "a" (Echo true) else Echo false

like image 179
T3RR0R Avatar answered Jan 30 '23 20:01

T3RR0R