Hello !
What is the easiest way to perform a logical or script operation on a number represented by a string variable? For example, there is a line “01001”. Should it be converted to “10110”?
Hello !
What is the easiest way to perform a logical or script operation on a number represented by a string variable? For example, there is a line “01001”. Should it be converted to “10110”?
I answer myself:
{
:local a "1101"
:local c
:for i from=0 to=[:len $a] do={:set c ("$c"."$[:pick $a $i (1+$i)]".".")}
:set $c [:pick $c 0 ([:len $c]-2)]
:put $c
:set c ($c^1.1.1.1)
:put $c
}
but this only works with 4 digits.
This can be done in blocks of 4 digits to the end. In the example for 5 digits:
# inversion for 5 digits
{
:local a "10011"; :local b; :local d
:local f do={:local c; :for i from=0 to=[:len $1] do={:set c ("$c"."$[:pick $1 $i (1+$i)]".".")}; return [:pick $c 0 ([:len $c]-2)]}
:set b ([$f [:pick $a 0 4]]^1.1.1.1); :set d ([$f [:pick $a 1 5]]^1.1.1.1); :set d ("$b"."$[:pick $d 5 7]"); set b ""
:for i from=0 to=[:len $d] do={:set a [:pick $d $i (1+$i)]; if ($a=".") do={} else={:set b ("$b"."$a")}}
:put $b
}
Maybe Rex will laugh and it can be much simpler and shorter? More versatile ?
The built-in bitwise XOR:
https://wiki.mikrotik.com/wiki/Manual:Scripting#Bitwise_Operators
should work also for IPv6 and num data types:
Bitwise Operators
Bitwise operators are working on number, IP and IPv6 address data types.
So, using your code but with IPv6 (and : instead of . as separator) you should be able to get at 8 digits.
Otherwise you would probably need to convert the binary to num, and then the result back to binary, rextended has posted:
http://forum.mikrotik.com/t/rextended-fragments-of-snippets/151033/18
http://forum.mikrotik.com/t/rextended-fragments-of-snippets/151033/18
The proposed options are not shorter than mine… We need to write a function that XORs a binary number of any length.
Where did our Cat go?
The topic is wrong at the start.
Then it’s not even written, but I suppose they are binary numbers…
OR and XOR are operation between two bit/number.
Then in the second post an IP number pops up… (1.1.1.1)
This topic starts badly and its meaning is not clear…
Anyway:
(000)01001 XOR (000)11111 = (000)10110
Even negation must be handled according to significant figures…
NOT 01001 is wanted = to 10110 ???, so NOT 9 = 22 ???
but…
NOT (000)01001 is = to (111)10110 so NOT 9 = -9 ???
Since RouterOS do not work with binary data, and you want work with strings,
simply search and replace 1 on 0 and 1 with 0 on string…
I get @Sertik like a clean simple generic solution. But… once you go beyond 7-bits, all conversions from binary to number need to know a lot more on how the binary is structured. If you really are starting with some LONGER string with binary 1/0’s like “01000100101111010101010” — you have to also know the original byte ordering (and endian-ness etc) to get that to a number (and then routeros has a limit on it’s int size too).
In 7.16, there are new :convert operators, including a “bit-array-xxx”, that help do the reverse:
:put [:convert from=hex to=bit-array-lsb "EF"]
# 1;1;1;1;0;1;1;1
:put [:convert from=hex to=bit-array-msb "EF"]
# 1;1;1;0;1;1;1;1
:put [:convert from=byte-array to=bit-array-lsb {239}]
# 1;1;1;1;0;1;1;1
:put [:convert from=byte-array to=bit-array-msb {239}]
# 1;1;1;0;1;1;1;1
But the it’s only a one-way to bits, as there is no from=bit-array-XXXX which make sense since again once past 8-bit, the conversion needs to know more beyond LSB and MSB . That, and, the new commands were designed around the data coming from IoT things like Bluetooth - where you’d want to parse the binary payload - but IoT starts with a hexstring so all the :convert operators are built around the “hexstring” (“FF00EE”) as the starting point for these byte/bit conversions.
Just one example on the string, but obviously my previous post it wasn’t that rich in explanations, but it highlights the lack in what was being written…
{
:local invert01onstr do={
:local result “”
:for i from=0 to=([:len $1] -1) do={
:if ([:pick $1 $i (1 + $i)] = “1”) do={:set result ($result . “0”)} else={:set result ($result . “1”)}
}
:return $result
}
:put [$invert01onstr “10011”]
:put [$invert01onstr “1010110011”]
:put [$invert01onstr “010”]
}
01100
0101001100
101
@Amm0 It would be easier to know where that 01001 came from and why it needs to be transformed into 10110.
But @Sertik suckers us in every time. I guess I view his questions a RouterOS version of LeetCode questions (i.e. theoretical CS problems).
Anyway, for fun, here is my take:
:global invBinaryString do={
:local bstr $1
:local asString false
:if ($2 = “as-string”) do={:set asString true}
:local barr [:toarray “”]
:for i from=0 to=([:len $bstr]-1) do={:set ($barr->$i) (![:tobool [:tonum [:pick $bstr $i ($i+1)]]])}
:if $asString do={
:local xorstr “”
:foreach b in=$barr do={
# bool->num not possible - :set xorstr “$xorstr$[:tonum $b]”
:if $b do={ :set xorstr ($xorstr . “1”) } else={:set xorstr ($xorstr . “0”) }
}
:return $xorstr
} else={
:return $barr
}
}
:put [$invBinaryString “1010101111101”]
:put [$invBinaryString “1010101111101” as-string]
Do not do the same error, is not a OR or XOR, the number is only one, is just INVERT or at least NOT (can not really be NOT since the base is unknow…)
I wrote it that way and named it wrong. As you point out, it hard to know what he’s looking for. I was just trying to point out getting to array make using the bits-in-string easier (and that allow the :convert stuff if desired). i.e. once converted, you’d still likely want to access the bits inside & array makes that easy:
:global invbits [$invBinaryString "1010101111101"]
:put ($invbits->0)
#false
And correct if a real OR and/or XOR is needed, more info be needed.
Also, there be another approach using ":convert to=byte-array"to get an array… but that get you the ASCII codes for 1 and 0
:put [:convert to=byte-array from=raw "10101010"]
# 49;48;49;48;49;48;49;48
but since that’s at least an array, and not a string, you can use a :foreach on it.
Anyway…the underlying complexity stems from that strings are not arrays in RouterOS. So the stupid :for and [:pick], [:len], etc. to deal “read a char” from a string are needed. e.g. (“mystring”->0) does NOT work nor get you “m”.
So the sooner you get some bit/num/byte things to an array, the better… all the conversion like OR or whatever become easier.
Firsto obstacle on OP is arbitrary lenght. Can happen. A byte can contain various information in specific points. For example:
0 0 0 = 0
0 0 1 = 1
0 1 0 = 2
0 1 1 = 3
1 0 0 = -4
1 0 1 = -3
1 1 0 = -2
1 1 1 = -1
Or simply none are negative, is from 0 to 7 (or from 1 to 8..)
Probably you want write another thing…
:put [:convert to=byte-array from=raw "\01\00\01\00\01\00\01\00"]
# 1;0;1;0;1;0;1;0
And we come back to the original problem… How did those bits get represented in a RouterOS string – those had to come from somewhere. So if it was already “\01\00” form, not a string with actual ascii… then everything is easier.
Without any concrete needs/use, are all useless.
{
:local invert01onstr do={
:local result ""
:for i from=0 to=([:len $1] -1) do={
:if ([:pick $1 $i (1 + $i)] = "1") do={:set result ($result . "0")} else={:set result ($result . "1")}
}
:return $result
}
This is what we need. Very simple and great! Thanks Rex!
God ! Why am I so dumb?
:global invBinaryString do={
:local bstr $1
:local asString false
:if ($2 = "as-string") do={:set asString true}
:local barr [:toarray ""]
:for i from=0 to=([:len $bstr]-1) do={:set ($barr->$i) (![:tobool [:tonum [:pick $bstr $i ($i+1)]]])}
:if $asString do={
:local xorstr ""
:foreach b in=$barr do={
# bool->num not possible - :set xorstr "$xorstr$[:tonum $b]"
:if $b do={ :set xorstr ($xorstr . "1") } else={:set xorstr ($xorstr . "0") }
}
:return $xorstr
} else={
:return $barr
}
}
:put [$invBinaryString "1010101111101"]
# false;true;false;true;false;true;false;false;false;false;false;true;false
:put [$invBinaryString "1010101111101" as-string]
# 0101010000010
Thank you very much for this too, Amm0!
But @Sertik suckers us in every time.
Well, dear friend! No way…