Convert a String into a Hashtable in Powershell - string

my request is a bit special but quite simple to understand.
I get a string that I would like to exploit as a hashtable but I can't, my format is quite special and I haven't been successful with methods like ConvertFrom-StringData for example.
format preview:
#{id = 04cc943e-14a8-4bf6-8601-bd6fde3cc229; name = 7.10 10-16 - 6.0 * * * * | 1 0 0 0; privileges =}
I would therefore like to be able to access the content of the name attribute.
I spent a lot of time there but without success ...
Thank you for your help

So you may go with a string parsing and use combination of splitting and trimming.
$text = "#{id = 04cc943e-14a8-4bf6-8601-bd6fde3cc229; name = 7.10 10-16 - 6.0 * * * * | 1 0 0 0; privileges =}"
#this is to get rid of #{}
$preparedText = $text.Substring(2,$text.Length-3)
$obj = #{}
foreach ($kv in $preparedText.Split(";")) {
#Get key from string (which is "key = value")
$key = $kv.Trim().Split("=")[0].Trim()
Write-Host "Key = $key"
#Get value from string
$value = $kv.Trim().Split("=")[1].Trim()
Write-Host "Value = $value"
#Assign key-object pair to hashtable
$obj[$key] = $value
}
Write-Host "obj.name = $($obj.name);`nobj.id = $($obj.id);`nobj.privileges = $($obj.privileges)"
Which returns:
obj.name = 7.10 10-16 - 6.0 * * * * | 1 0 0 0;
obj.id = 04cc943e-14a8-4bf6-8601-bd6fde3cc229;
obj.privileges =

An alternative approach is to use the convertfrom-stringdata cmdlet, documented here:
Link to official MS documentation
The ConvertFrom-StringData cmdlet converts a string that contains one or more key and value pairs into a hash table. Because each key-value pair must be on a separate line, here-strings are often used as the input format. By default, the key must be separated from the value by an equals sign (=) character.
Using the OP's example as in #Karolina Ochlik's post:
$text = "#{id = 04cc943e-14a8-4bf6-8601-bd6fde3cc229; name = 7.10 10-16 - 6.0 * * * * | 1 0 0 0; privileges =}"
#this is to get rid of #{}
$preparedText = $text.Substring(2,$text.Length-3)
# replace semicolon with newline - `n needs double quotes
$preparedText.replace(';',"`n") | ConvertFrom-StringData
This creates the output:
Name Value
---- -----
name 7.10 10-16 - 6.0 * * * * | 1 0 0 0
id 04cc943e-14a8-4bf6-8601-bd6fde3cc229
privileges
the real work is done in the single line
$preparedText.replace(';',"`n") | ConvertFrom-StringData
I hope this helps someone.

Related

Taking strnig as input for calculator program in Perl

I am new to Perl and I'm trying to create a simple calculator program, but the rules are different from normal maths. All operations have the same power and the math problem must be solved from left to right.
Here is an example:
123 - 10 + 4 * 10 = ((123 - 10) + 4) * 10 = 1170
8 * 7 / 3 + 2 = ((8 * 7) / 3) + 2 = 20.666
So in the first case the user needs to enter one string: 123 - 10 + 4 * 10.
How do i approach this task?
I'm sorry if it's too much of a general question, but i'm not sure how to even begin. Do i need a counter? Like - every second character of the string is an operator, while the two on the sides are digits.
I'm afraid I'm lazy so I'll parse with a regex and process as I parse.
#!/usr/bin/env perl
#use Data::Dumper;
use Params::Validate (':all');
use 5.01800;
use warnings;
my $string=q{123 - 10 + 4 * 10};
my $result;
sub fee {
my ($a)=validate_pos(#_,{ type=>SCALAR });
#warn Data::Dumper->Dump([\$a],[qw(*a)]),' ';
$result=$a;
};
sub fi {
my ($op,$b)=validate_pos(#_,{ type=>SCALAR},{ type=>SCALAR });
#warn Data::Dumper->Dump([\$op,\$b],[qw(*op *b)]),' ';
$result = $op eq '+' ? $result+$b :
$op eq '-' ? $result-$b :
$op eq '*' ? $result*$b :
$op eq '/' ? $result/$b :
undef;
#warn Data::Dumper->Dump([\$result],[qw(*result)]),' ';
};
$string=~ m{^(\d+)(?{ fee($1) })(?:(?: *([-+/*]) *)(\d+)(?{ fi($2,$3) }))*$};
say $result;
Note the use of (?{...}) 1
To be clear, you are not looking for a regular calculator. You are looking for a calculator that bends the rules of math.
What you want is to extract the operands and operators, then handle them 3 at the time, with the first one being the rolling "sum", the second an operator and the third an operand.
A simple way to handle it is to just eval the strings. But since eval is a dangerous operation, we need to de-taint the input. We do this with a regex match: /\d+|[+\-*\/]+/g. This matches either 1 or more + digits \d or |, 1 or more + of either +-*/. And we do this match as many times as we can /g.
use strict;
use warnings;
use feature 'say';
while (<>) { # while we get input
my ($main, #ops) = /\d+|[+\-*\/]+/g; # extract the ops
while (#ops) { # while the list is not empty
$main = calc($main, splice #ops, 0, 2); # take 2 items off the list and process
}
say $main; # print result
}
sub calc {
eval "#_"; # simply eval a string of 3 ops, e.g. eval("1 + 2")
}
You may wish to add some input checking, to count the args and make sure they are the correct number.
A more sensible solution is to use a calling table, using the operator as the key from a hash of subs designed to handle each math operation:
sub calc {
my %proc = (
"+" => sub { $_[0] + $_[1] },
"-" => sub { $_[0] - $_[1] },
"/" => sub { $_[0] / $_[1] },
"*" => sub { $_[0] * $_[1] }
);
return $proc{$_[1]}($_[0], $_[2]);
}
As long as the middle argument is an operator, this will perform the required operation without the need for eval. This will also allow you to add other math operations that you might want for the future.
Just to read raw input from the user you would simply read the STDIN file handle.
$input = <STDIN>;
This will give you a string, say "123 + 234 - 345" which will have a end of line marker. You can remove this safely with the chomp command.
After that you will want to parse your string to get your appropriate variables. You can brute force this with a stream scanner that looks at each character as you read it and processes it accordingly. For example:
#input = split //, $input;
for $ch (#input) {
if ($ch > 0 and $ch <= 9) {
$tVal = ($tVal * 10) + $ch;
} elsif ($ch eq " ") {
$newVal = $oldVal
} elsif ($ch eq "+") {
# Do addition stuff
}...
}
Another approach would be to split it into words so you can just deal with whole terms.
#input = split /\s+/, $input;
Instead of a stream of characters, as you process the array values will be 123, +, 234, -, and 345...
Hope this points you in the right direction...

Lua script optimization

I am trying to connect redis via c# using using ServiceStack.Redis.
I have written below code to validate number based on the key specified.
argv[1] is key
argv[2] is number
string strScript = " local intCurrentVal = redis.call('GET', '' .. ARGV[1] .. ''); \n"
+ "if (tonumber(intCurrentVal) <= 0 ) then return 1 elseif ( (tonumber(intCurrentVal)) - (tonumber('' .. ARGV[2] .. '')) < 0 ) then return 0 end;"
+ "local intUpdatedVal = redis.call('SET', '' .. ARGV[1] .. '',( intCurrentVal - tonumber('' .. ARGV[2] .. '')));"
+ "local intCurr = redis.call('GET', '' .. ARGV[1] .. ''); return intCurr";
logical steps:
get the current value
check if current value should not be less then or equal to 0
check if current value - passed value should not be less then O
if current value - passed is not less then 0 then set the (current value - passed) as the current value
get the current value
Is it possible to optimize and tune the following lua script for performance. please help.
Original formatting is awful -- so is often performance.
local key = tostring(ARGV[1])
local number = tonumber(ARGV[2])
local current = tonumber(redis.call('GET', key))
if current <= 0 then
return 1
elseif current < number then
return 0
end
redis.call('SET', key, current - number)
return redis.call('GET', key)
Further optimization steps may include: localizing of global functions (like to string, tonumber, etc.), caching compiled chunk at LUA_REGISTRYINDEX table.

Powershell string to number functions?

I am looking for an easy way to convert numeric strings e.g. "1.78M" "1.47B" to an integer variable.
Any help is appreciated.
thanks
Are you looking for M == MB or M == 1E6? If it is the former, PowerShell understands KB, MB, GB and TB e.g.:
C:\PS> Invoke-Expression "2MB"
2097152
Big caveat here with Invoke-Expression, if you're getting the string from a user, file, i.e. an untrusted source. You have to be careful about executing it. Say the string is "2MB; Remove-Item C:\ -Recurse -Force -Whatif -EA 0", you'd have a bad day using Invoke-Expression on that string. BTW, I'm being nice here by adding the -Whatif. :-)
If it is the latter, you could do a regex -replace followed by a coercion e.g.:
C:\PS> [long]("3.34 B" -replace '(\d+)\s*(B)','$1E9')
3340000000
There is nothing built in that supports suffixes like M for million, B for billion, etc. There is only built-in support for "file size" suffixes, for example 32KB -> 32768
Here's my attempt at a basic script version to address your problem. This supports multi-character suffixes if needed, or no suffix at all. It will always return an [int], so be wary of overflow (e.g. 5.5B will cause an error since it won't fit in an int). You can modify the types a bit to support bigger numbers.
function ToNumber
{
param([string] $NumberString)
# add other multiplier suffixes to this table
$multipliers = #{ 'B' = 1000000000; 'M' = 1000000; 'K' = 1000; '' = 1 }
switch -regex ($numberString)
{
'^(?<base>[\d\.]+)(?<suffix>\w*)$'
{
$base = [double] $matches['base']
$multiplier = [int] $multipliers[$matches['suffix']]
if($multiplier)
{
[int]($base * $multiplier)
}
else
{
throw "$($matches['suffix']) is an unknown suffix"
}
}
default
{
throw 'Unable to parse input'
}
}
}
C:\> ToNumber '1.7B'
1700000000
C:\> ToNumber '1.7K'
1700

lpeg grammar to parse comma separated groups that may have internal groups

I need to parse comma separated groups(enclosed in brackets) that may have internal groups inside the groups. It should only separate the outside groups.
I have a function that does this:
function lpeg.commaSplit(arg)
local P,C,V,sep = lpeg.P, lpeg.C, lpeg.V, lpeg.P(",")
local p = P{
"S";
S = lpeg.T_WSpace * C(V"Element") * (lpeg.T_WSpace * sep * lpeg.T_WSpace * C(V"Element"))^0 * lpeg.T_WSpace,
Element = (V"Group")^0 * (1 - lpeg.T_Group - sep)^0 * (V"Group" * (1 - lpeg.T_Group - sep)^0)^0 * (1 - sep)^0,
Group = lpeg.T_LGroup * ((1 - lpeg.T_Group) + V"Group")^0 * lpeg.T_RGroup
}^-1
return lpeg.match(lpeg.Ct(p), arg)
end
But the problem is to remove the extra brackets that may enclose the group.
Here is a test string:
[[a,b,[c,d]],[e,[f,g]]]
should parse to
[a,b,[c,d] & [e,[f,g]]
Notice the internal groups are left alone. A simple removal of the extra brackets on the end does not work since you'll end up with a string like a,b,[c,d]],[e,[f,g].
Any ideas how to modify the lpeg grammar to allow for the outside groups?
As I am not expert in making grammars in LPeg, I found this exercise interesting to do...
I couldn't manage to use your grammar, so I went ahead and made my own, with smaller chunks easier to understand and where I could put the captures I needed.
I think I got a decent empirical result. It works on your test case, I don't know if groups can be more deeply nested, etc. The post-processing of the capture is a bit ad hoc...
require"lpeg"
-- Guesswork...
lpeg.T_WSpace = lpeg.P" "^0
lpeg.T_LGroup = lpeg.P"["
lpeg.T_RGroup = lpeg.P"]"
lpeg.T_Group = lpeg.S"[]"
function lpeg.commaSplit(arg)
local P, C, Ct, V, sep = lpeg.P, lpeg.C, lpeg.Ct, lpeg.V, lpeg.P","
local grammar =
{
"S";
S = lpeg.T_WSpace * V"Group" * lpeg.T_WSpace,
Group = Ct(lpeg.T_LGroup * C(V"Units") * lpeg.T_RGroup),
Units = V"Unit" *
(lpeg.T_WSpace * sep * lpeg.T_WSpace * V"Unit")^0,
Unit = V"Element" + V"Group",
Element = (1 - sep - lpeg.T_Group)^1,
}
return lpeg.match(Ct(P(grammar)^-1), arg)
end
local test = "[[a,b,[c,d]],[e,[f,g]]]"
local res = lpeg.commaSplit(test)
print(dumpObject(res))
print(res[1], res[1][1], res[1][2])
local groups = res[1]
local finalResult = {}
for n, v in ipairs(groups) do
if type(v) == 'table' then
finalResult[#finalResult+1] = "[" .. v[1] .. "]"
end
end
print(dumpObject(finalResult))
dumpObject is just a table dump of my own. The output of this code is as follows:
local T =
{
{
"[a,b,[c,d]],[e,[f,g]]",
{
"a,b,[c,d]",
{
"c,d"
}
},
{
"e,[f,g]",
{
"f,g"
}
}
}
}
table: 0037ED48 [a,b,[c,d]],[e,[f,g]] table: 0037ED70
local T =
{
"[a,b,[c,d]]",
"[e,[f,g]]"
}
Personally, I wouldn't pollute the lpeg table with my stuff, but I kept your style here.
I hope this will be useful (or will be a starting point to make you to advance).

php/mysql Pagination argument not valid

After db connection-
`$tbl_name="mytable";`
$adjacents = 3;
$query = "SELECT COUNT (*) as num FROM $mytable";
$total_pages = mysql_fetch_array(mysql_query($query)); this is line 32
$total_pages = $total_pages[num];
$targetpage = "pagination.php"; (the name of this file)
$limit = 20;
error i am getting is-
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in line 32.
can anyone help?
thanks
The problem should be in your query, it should be either:
$query = "SELECT COUNT (*) as num FROM mytable";
or
$query = "SELECT COUNT (*) as num FROM ".$tbl_name."";
You are referencing a variable $mytable that you haven't previously defined

Resources