I have this $value= 24153;
I have a field that can hold one or more values like this {"id":"2","value":["3"]} or like this {"id":"2","value":["3","4"]} or {"id":"2","value":["3","4","2"]}
I have this regex that works fine, but returns if the ONE value exists.
I need to improve this regex to the case there are more than one value in field.
REGEXP BINARY \'(.*{"id":"2","value":\["[^\"]*['.$value.'][^\"]*",?)+\]}.*\'
here is a regexp that will do what you want - but i have to agree with Your Common Sense - storing json and then using regex to extract data from it is sickening
REGEXP BINARY \'(.*{"id":"2","value":\[("[^\"]*",)*"'.$value.'"(,"[^\"]*")*)+\]}.*\'
$value = 4;
$subject_a = '{"id":"2","value":["4"]}';
$subject_b = '{"id":"2","value":["1","4","1","1"]}';
$subject_c = '{"id":"2","value":["1","1","1","1","1","44","1","1"]}';
$pattern = '/{"id":"2","value":\[("|\d|,)*"' . $value . '"("|\d|,)*\]/';
$matches[0] = preg_match($pattern,$subject_a);
$matches[1] = preg_match($pattern,$subject_b);
$matches[2] = preg_match($pattern,$subject_c);
echo '<pre>';
var_dump($matches);
echo '</pre>';
The result will be:
array(3) {
[0]=> int(1)
[1]=> int(1)
[2]=> int(0)
}
Related
I have a function i cannot control which returns a string which is acutally a hash. It looks something like below:
{"offset":0,"limit":500,"count":0,"virtual_machines":[]}
I need to check if the count is greater than 0. Because the output is a string and not a hash, i am trying to split the string and get the output from it.
The snippet for the same is below:
my $output = '{"offset":0,"limit":500,"count":0,"virtual_machines":[]}';
$output =~ s/ +/ /g;
my #words = split /[:,"\s\/]+/, $output;
print Dumper(#words);
The output for this is:
$VAR1 = '{';
$VAR2 = 'offset';
$VAR3 = '0';
$VAR4 = 'limit';
$VAR5 = '500';
$VAR6 = 'count';
$VAR7 = '0';
$VAR8 = 'virtual_machines';
$VAR9 = '[]}';
Now, i can get the value $VAR7 and get the count.
Is there a way to convert a string to hash and then use the keys to get the values instead of using regex and split. Can someone help me out here!
That string is in JSON format. I'd simply do
use strict;
use warnings;
use JSON::PP qw(decode_json);
my $output = '{"offset":0,"limit":500,"count":0,"virtual_machines":[]}';
my $data = decode_json $output;
print $data->{count}, "\n";
If all colons are just separators, then you can replace them with '=>'s and eval the string.
That's probably unrealistic, though. So you can use JSON ... looks like the string is in JSON format. Try the following (worked for me :-):
#!/usr/bin/perl
use JSON::Parse 'parse_json';
# the string is JSON
my $jstr = '{"offset":0,"limit":500,"count":0,"virtual_machines":[]}';
# oversimplified (not using json ... o.k. if no colons anywhere but as separators
my $sstr = $jstr;
$sstr =~ s/:/=>/g;
my $href = eval "$sstr";
printf("From oversimplified eval, limit == %d\n", $href->{limit});
# using JSON (looks like string is in JSON format).
# get JSON::Parse from CPAN (sudo cpan JSON::Parse)
my $jref = parse_json($jstr);
printf("From JSON::Parse, limit == %d\n", $jref->{limit});
1;
Output:
From oversimplified eval, limit == 500
From JSON::Parse, limit == 500
Hope some Perl gurus out there can help me out here. Basically my issue is when a JSON string starts with a "[" instead of a "{", Perl doesn't treat the variable as a hash after I use decode_json.
Here's a sample code.
#!/usr/bin/perl
use JSON;
use Data::Dumper;
$string1 = '{"Peti Bar":{"Literature":88,"Mathematics":82,"Art":99},"Foo Bar":{"Literature":67,"Mathematics":97}}';
$string = '[{"ActionID":5,"ActionName":"TEST- 051017"},{"ActionID":10,"ActionName":"Something here"},{"ActionID":13,"ActionName":"Some action"},{"ActionID":141,"ActionName":"Email Reminder"}]';
print "First string that starts with \"{\" below:\n$string1\n\n";
my $w = decode_json $string1;
my $count = keys %$w;
print "printing \$count's value -> $count\n\n";
print "Second string starts with \"[\" below:\n$string\n\n";
my $x = decode_json $string;
my $count2 = keys %$x;
print "printing \$count2's value -> $count2\n\n";
Below is the script output.
Both $w and $x works though. It's just I have to use keys $x instead of keys %$x on the other json string.
Now the issue with using that is I get a keys on reference is experimental at tests/jsontest.pl error. It won't stop the script but I'm worried about future compatibility issues.
What's the best way to approach this?
Use the ref function to determine what type the reference is. See perldoc -f ref.
my $w = decode_json $string1;
my $count = 1;
if( my $ref = ref( $w ) ){
if( $ref eq 'HASH' ){
$count = keys %$w;
}elsif( $ref eq 'ARRAY' ){
$count = scalar #$w;
}else{
die "invalid reference '$ref'\n";
}
}
I'm trying to write a program to fetch a big MySQL table, rename some fields and write it to JSON. Here is what I have for now:
use strict;
use JSON;
use DBI;
# here goes some statement preparations and db initialization
my $rowcache;
my $max_rows = 1000;
my $LIMIT_PER_FILE = 100000;
while ( my $res = shift( #$rowcache )
|| shift( #{ $rowcache = $sth->fetchall_arrayref( undef, $max_rows ) } ) ) {
if ( $cnt % $LIMIT_PER_FILE == 0 ) {
if ( $f ) {
print "CLOSE $fname\n";
close $f;
}
$filenum++;
$fname = "$BASEDIR/export-$filenum.json";
print "OPEN $fname\n";
open $f, ">$fname";
}
$res->{some_field} = $res->{another_field}
delete $res->{another_field}
print $f $json->encode( $res ) . "\n";
$cnt++;
}
I used the database row caching technique from
Speeding up the DBI
and everything seems good.
The only problem I have for now is that on $res->{some_field} = $res->{another_field}, the row interpreter complains and says that $res is Not a HASH reference.
Please could anybody point me to my mistakes?
If you want fetchall_arrayref to return an array of hashrefs, the first parameter should be a hashref. Otherwise, an array of arrayrefs is returned resulting in the "Not a HASH reference" error. So in order to return full rows as hashref, simply pass an empty hash:
$rowcache = $sth->fetchall_arrayref({}, $max_rows)
I'm writing a regex pattern to split MySQL CREATE statements into column definition arrays. So far, it works great for everything aside from SET columns. Here's the process:
$lines = explode("\n", $create_sql);
foreach ($lines as $line) {
$line = str_replace('NOT NULL', 'NOT_NULL', $line);
$pattern = '/`(.*)`[\s]*([^\s]*)[\s]*(NOT_NULL|NULL)[\s]*(.*),/';
$search = preg_match($pattern, $line, $matches);
if ($search !== false && count($matches) === 5) {
$columns[$matches[1]] = array(
'type' => $matches[2],
'null' => $matches[3] === 'NULL',
'extra' => $matches[4],
);
}
}
This process works great on a column with a definition like this:
`id` INT(11) NOT NULL AUTO_INCREMENT,
...but fails on SET columns. How can I best accommodate for the extra quotes and parentheses in this line?
`platform` SET('iOS', 'Android') NULL DEFAULT NULL,
To summarize, I need to integrate a pattern to match SET('one', 'two', n) in a string.
First just let slightly modify your pattern expression, first for readability (\s* instead of [\s]*), then to ensure working even with lowercase statements :
$pattern = '/`(.*)`\s*([^\s]*)\s*(NOT_NULL|NULL)\s*(.*),/i';
Then, to answer your question, prepare the pattern depending on a SET is present or not in the line:
$sub_pattern = stripos($line, ' SET(') ?
'SET\([^\)]*\)\s*[^\s]*'
: '[^\s]*';
$pattern = '/`(.*)`\s*(' . $sub_pattern . ')\s*(NOT_NULL|NULL)\s*(.*),/i';
Note that, however, this code is not secured against syntax errors in the source lines.
// Input: $sql
$match = '(
([^(),]+?| # no parens, or contains ():
[^(),]+?
\( [^)]+? \) # (123), (enum...), etc
[^(),]*?
) \s*(,\s*|$) # separator or terminator
)';
preg_match('/(CREATE\s+TABLE.*?\()(.*)(\)[^()]*(;|$))/si', $sql, $m);
list($x, $pre, $list, $post, $z) = $m;
$list = trim($list);
$list = preg_replace('/\s+/', ' ', $list);
$list = preg_replace("/$match/sx", "$1
", $list);
$list = trim($list);
// Output:
echo "<pre>
$pre
$list
$post\n</pre>\n";
Notes:
Certain quoted strings will fail to parse correctly.
I think all cases of parentheses work ok.
You will still need to parse each element in $list[], which should be one per column.
Way oversimplified example:
# Get Some data
$query = $db->prepare(qq{
select * from my_table where id = "Some Value"
});
$query->execute;
# Iterate through the results
if ( *THE QUERY HAS RETURNED A RESULT* ) {
print "Here is list of IDs ";
while ($query_data = $query->fetchrow_hashref) {
print "$query_data->{id}";
}
};
Looking for the code for "THE QUERY HAS RETURNED A RESULT" up there. I'd like to avoid using count(*) in my SQL if possible, since that will require a "group by".
my $sth = $dbh->prepare($stmt);
$sth->execute();
my $header = 0;
while (my $row = $sth->fetchrow_hashref) {
print "Here is list of IDs:\n" if !$header++;
print "$row->{id}\n";
}
Alternative:
my $sth = $dbh->prepare($stmt);
$sth->execute();
my $row = $sth->fetchrow_hashref;
print "Here is list of IDs:\n" if $row;
while ($row) {
print "$row->{id}\n";
$row = $sth->fetchrow_hashref;
}
Simpler code at the expense of memory:
my $ids = $dbh->selectcol_arrayref($stmt);
if (#$ids) {
print "Here is list of IDs:\n";
print "$_\n" for #$ids;
}
Looks to me like your check for the query result is redundant. Your while loop will evaluate 'false' if there is no row to fetch.
old/wrong answer
If you are using DBI with DBD::mysql then $query->rows; will return you the number of rows, selected (or affected on a writing statement) by your statement.
EDIT
Please don't use that and have a look at the comment to this answer by #Luke The Obscure