How to manually specify the column names using DBD::CSV? - perl

I am using DBD::CSV to show csv data. Sometimes the file doesn't contain column names, so we have to manually define it. But after I followed the documentation, I got stuck with how to make the attribute skip_first_row work. The code I have is:
#! perl
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect("dbi:CSV:", undef, undef, {
f_dir => ".",
f_ext => ".txt/r",
f_lock => 2,
csv_eol => "\n",
csv_sep_char => "|",
csv_quote_char => '"',
csv_escape_char => '"',
csv_class => "Text::CSV_XS",
csv_null => 1,
csv_tables => {
info => {
file => "countries.txt"
}
},
FetchHashKeyName => "NAME_lc",
}) or die $DBI::errstr;
$dbh->{csv_tables}->{countries} = {
skip_first_row => 0,
col_names => ["a","b","c","d"],
};
my $sth = $dbh->prepare ("select * from countries limit 1");
$sth->execute;
while (my #row = $sth->fetchrow_array) {
print join " ", #row;
print "\n"
}
print join " ", #{$sth->{NAME}};
The countries.txt file is like this:
AF|Afghanistan|A|Asia
AX|"Aland Islands"|E|Europe
AL|Albania|E|Europe
But when I ran this script, it returns
AX Aland Islands E Europe
AF AFGHANISTAN A ASIA
I expected it to either return:
AF AFGHANISTAN A ASIA
a b c d
or
a b c d
a b c d
Does any know what's going on here?

For some reason, contrary to the documentation, it doesn't see the per-table settings unless you pass them to connect.
my $dbh = DBI->connect("dbi:CSV:", undef, undef, {
f_dir => ".",
f_ext => ".txt/r",
f_lock => 2,
csv_eol => "\n",
csv_sep_char => "|",
csv_quote_char => '"',
csv_escape_char => '"',
csv_class => "Text::CSV_XS",
csv_null => 1,
csv_tables => {
countries => {
col_names => [qw( a b c d )],
}
},
FetchHashKeyName => "NAME_lc",
}) or die $DBI::errstr;
Then it works fine:
my $sth = $dbh->prepare ("select * from countries limit 1");
$sth->execute;
print "#{ $sth->{NAME} }\n"; # a b c d
while (my $row = $sth->fetch) {
print "#$row\n"; # AF Afghanistan A Asia
}

Related

The correct way to play with data references in Perl

I have a set of data that I would like to process.
In order to simplify my code, It would be nice to access to some subsets of my data through array of references that point to the original data.
Better than an explanation, I wrote down this example (which is not working yet). At the end, I would like to update the original data without having to also update all the subsets.
Is it possible to do something like this with Perl ?
#!/usr/bin/env perl
use strict;
use warnings;
# A set of data
my $design = {
box => {
ne => {data => 'north-east'},
nw => {data => 'north-west'},
n => {data => 'north'},
s => {data => 'south'},
e => {data => 'east'},
w => {data => 'west'},
se => {data => 'south-east'},
sw => {data => 'south-west'}
}
};
# Select a design
my $selected = 'box';
# Build some arrays
my $d = $design->{$selected};
my #eastside = (\$d->{e}, \$d->{ne}, \$d->{se});
my #westside = (\$d->{w}, \$d->{nw}, \$d->{sw});
my #northside = (\$d->{n}, \$d->{ne}, \$d->{nw});
# Update one data
$d->{ne}->{data} .= " updated!";
# Display
print join '', "Composed of:\n", map("\t".$_->{data}."\n", #eastside);
The script should output:
Composed of:
east
north-east updated!
south-east
All that is wrong is that you are taking a reference of values that are already hash references in the lines like
my #eastside = (\$d->{e}, \$d->{ne}, \$d->{se})
You should simple omit the backslashes and everything will work.
(By the way, you may want to know that the reference \ operator is distributive, so you can write the same thing with
my #eastside = \( $d->{e}, $d->{ne}, $d->{se} )
but it's no more correct that way!)
Some other points
You should make use of hash slices in situations like this where you need to extract a list of hash elements using multiple keys. In this case #eastside is just #{$d}{qw/ ne e se /}
Perl allows the indirection operator -> between pairs of closing and opening braces and brackets to be omitted, so $d->{ne}->{data} can be written $d->{ne}{data}
You are printing the result of a join with a null between the elements. You'll get the same result by just listing the items to be printed. You can also interpolate hash elements into a double-quoted string, so "\t".$_->{data}."\n" is the same as "\t$_->{data}\n"
Making those changes results in this working program
use strict;
use warnings;
# A set of data
my $design = {
box => {
ne => {data => 'north-east'},
nw => {data => 'north-west'},
n => {data => 'north'},
s => {data => 'south'},
e => {data => 'east'},
w => {data => 'west'},
se => {data => 'south-east'},
sw => {data => 'south-west'},
}
};
# Select a design
my $selected = 'box';
# Build some arrays
my $d = $design->{$selected};
my #eastside = #{$d}{qw/ ne e se /};
my #westside = #{$d}{qw/ nw w sw /};
my #northside = #{$d}{qw/ nw n ne /};
# Update one item
$d->{ne}{data} .= " updated!";
# Display
print "Composed of:\n";
print " $_->{data}\n" for #eastside;
output
Composed of:
north-east updated!
east
south-east
You have scalar references in #eastside array, so in order to dereference scalar put extra $ in front of $_->{data}, or use ${$_}->{data}.
print join '', "Composed of:\n", map("\t".$$_->{data}."\n", #eastside);
output
Composed of:
east
north-east updated!
south-east
You can check the data structure with Data::Dumper module.
Also check the next two #east2 and #east3 examples, especially how is built the #east3.
#!/usr/bin/env perl
use 5.010;
use warnings;
use Data::Dumper;
# A set of data
my $design = {
box => {
ne => {data => 'north-east'}, nw => {data => 'north-west'},
n => {data => 'north'}, s => {data => 'south'},
e => {data => 'east'}, w => {data => 'west'},
se => {data => 'south-east'}, sw => {data => 'south-west'}
}
};
my $selected = 'box';
my $d = $design->{$selected};
#your example
my #east1 = (\$d->{e}, \$d->{ne}, \$d->{se});
say Dumper \#east1;
my #east2 = ($d->{e}, $d->{ne}, $d->{se});
say Dumper \#east2;
my #east3 = #$d{qw(e ne se)};
say Dumper \#east3;
$d->{ne}->{data} .= " updated!";
print join '', "Composed of:\n", map("\t".$$_->{data}."\n", #east1);
print join '', "Composed of:\n", map("\t".$_->{data}."\n", #east2);
print join '', "Composed of:\n", map("\t".$_->{data}."\n", #east3);
prints:
$VAR1 = [
\{
'data' => 'east'
},
\{
'data' => 'north-east'
},
\{
'data' => 'south-east'
}
];
$VAR1 = [
{
'data' => 'east'
},
{
'data' => 'north-east'
},
{
'data' => 'south-east'
}
];
$VAR1 = [
{
'data' => 'east'
},
{
'data' => 'north-east'
},
{
'data' => 'south-east'
}
];
Composed of:
east
north-east updated!
south-east
Composed of:
east
north-east updated!
south-east
Composed of:
east
north-east updated!
south-east

Perl adding Lines into a Multi-Dimensional Hash

Hello I want to split a Line and add the Values in to a multi dimensional Hash. This is how the Lines look like:
__DATA__
49839382;Test1;bgsae;npvxs
49839384;Test2;bgsae;npvxs
49839387;Test3;bgsae;npvxs
So what I am doing now is:
my %prefix = map { chomp; split ';' } <DATA>;
But now I can only access Test1 with:
print $prefix{"49839382"}
But how can I also add the bgsae to the Hash so I can access is with
$prefix{"49839382"}{"Test1"}
Thank you for your help.
What structure are you trying to build?
use Data::Dumper;
my %prefix = map { chomp (my #fields = split /;/); $fields[0] => { #fields[1 .. $#fields] } } <DATA>;
print Dumper \%prefix;
Output:
$VAR1 = {
'49839384' => {
'Test2' => 'bgsae',
'npvxs' => undef
},
'49839382' => {
'Test1' => 'bgsae',
'npvxs' => undef
},
'49839387' => {
'npvxs' => undef,
'Test3' => 'bgsae'
}
};
Or do you need a deeper hash?
my %prefix;
for (<DATA>) {
chomp;
my $ref = \%prefix;
for (split /;/) {
warn "[$_]";
$ref->{$_} = {};
$ref = $ref->{$_};
}
}
Returns:
$VAR1 = {
'49839384' => {
'Test2' => {
'bgsae' => {
'npvxs' => {}
}
}
},
'49839382' => {
'Test1' => {
'bgsae' => {
'npvxs' => {}
}
}
},
'49839387' => {
'Test3' => {
'bgsae' => {
'npvxs' => {}
}
}
}
};
I don't know what you need the data for, but at a guess you want something more like this.
It builds a hash of arrays, using the first field as the key for the data, and the remaining three in an array for the value. So you can access the test number as $data{'49839382'}[0] etc.
use strict;
use warnings;
my %data = map {
chomp;
my #fields = split /;/;
shift #fields => \#fields;
} <DATA>;
use Data::Dumper;
print Data::Dumper->Dump([\%data], ['*data']);
__DATA__
49839382;Test1;bgsae;npvxs
49839384;Test2;bgsae;npvxs
49839387;Test3;bgsae;npvxs
output
%data = (
'49839384' => [
'Test2',
'bgsae',
'npvxs'
],
'49839382' => [
'Test1',
'bgsae',
'npvxs'
],
'49839387' => [
'Test3',
'bgsae',
'npvxs'
]
);

How to use DBD::CSV to get column names row?

I am using DBD::CSV to show csv data. The code I have is:
#! perl
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect("dbi:CSV:", undef, undef, {
f_dir => ".",
f_ext => ".txt/r",
f_lock => 2,
csv_eol => "\n",
csv_sep_char => "|",
csv_quote_char => '"',
csv_escape_char => '"',
csv_class => "Text::CSV_XS",
csv_null => 1,
csv_tables => {
info => {
file => "countries.txt"
}
},
FetchHashKeyName => "NAME_lc",
}) or die $DBI::errstr;
$dbh->{csv_tables}->{countries} = {
skip_first_row => 0,
col_names => ["a","b","c","d"],
raw_header => 1,
};
my $sth = $dbh->prepare ("select * from countries limit 1");
$sth->execute;
while (my #row = $sth->fetchrow_array) {
print join " ", #row;
print "\n"
}
The countries.txt file is like this:
ISO_COUNTRY|COUNTRY_NAME|REGION_CODE|REGION_NAME
AF|Afghanistan|A|Asia
AX|"Aland Islands"|E|Europe
AL|Albania|E|Europe
But when I ran this script, it returns
AF Afghanistan A Asia
I wanted it to return:
ISO_COUNTRY COUNTRY_NAME REGION_CODE REGION_NAME
Does any one know how to achieve this using DBD::CSV module?
Another question is why the col_names attribute setting didn't take effect?
How to make it return the following?
a b c d
$sth->{NAME}, $sth->{NAME_lc} and $sth->{NAME_uc} return a reference to an array containing the names.
my $sth = $dbh->prepare("select * from countries limit 1");
$sth->execute;
print "$_\n" for #{ $sth->{NAME} };

Perl - Class::Struct Deferencing array

use Class::Struct;
struct (TimingStruct => {
_timingSense => '$',
_timingType => '$',
_relatedPin => '$',
_whenCond => '$'
});
struct (OutPinStruct => {
_outPinName => '$',
_outFunction => '$',
_timingarray => '#', #_timingarc => 'TimingStruct'
});
my #tarray = ();
my $t;
$t = TimingStruct->new(_timingSense => 'Unate',
_timingType => 'Wave',
_relatedPin => 'CO',
_whenCond => 'A ^ B'
);
push(#tarray, $t);
$t = TimingStruct->new(_timingSense => 'Combinational',
_timingType => 'Rising',
_relatedPin => 'ICO',
_whenCond => 'A ^ B ^ CI'
);
push(#tarray, $t);
my $op = OutPinStruct->new(_outPinName => "CO",
_outFunction => "A ^ B ^ CI",
_timingarray => \#tarray);
print $op->_outPinName . "\n";
print $op->_outFunction . "\n";
print $op->_timingarray . "\n";
my $t = ${${$op->_timingarray}[0]}[0];
print "\$t = \$op->_timingarray = $t->_timingSense() \n";
my #t = {$op->_timingarray};
print "\#t = \#{\$op->_timingarray} = $$t[1] \n";
Every output pin can have many timing-arcs and the OutPinStruct has a array to hold the timing-arcs. I'm not sure about de-referencing arrays(_timingarray) could someone tell me what is it that I'm doing wrongly?
Thanks.
$op->_timingarray is a "list of hashes". In general the keys to a hash are unordered and you cannot lookup the hash values through a numbered index. The elements you can access are
$op->_timingarray->[0]{'TimingStruct::_whenCond'}
$op->_timingarray->[0]{'TimingStruct::_timingSense'}
$op->_timingarray->[0]{'TimingStruct::_relatedPin'}
$op->_timingarray->[0]{'TimingStruct::_timingType'}
$op->_timingarray->[1]{'TimingStruct::_whenCond'}
$op->_timingarray->[1]{'TimingStruct::_timingSense'}
$op->_timingarray->[1]{'TimingStruct::_relatedPin'}
$op->_timingarray->[1]{'TimingStruct::_timingType'}

RegSvr32 registering yet nothing actually registered

A rather odd experience. Using the latest PDK (v7.3) from ActiveState, I used perlctrl to build a COM DLL. Perlctrl ran without a hitch. OLEView read the typelib okay. RegSvr32 registered it okay. However ... there's no sign of it in registry, and anything that tries to use it fails. I hunted for the various UIDs using RegEdit and they're just not there.
The code is below. It's a wrapping of Lingua::ZH::WordSegmenter, but with the encoding changed to utf8 rather than gbk.
It's probably something obvious ...
package ZHWordSeg;
use strict;
use warnings;
use utf8;
use ws;
use Encode;
use constant STX => chr( 2 ); #[
use constant ETX => chr( 3 ); #]
use constant FS => chr( 28 ); #^
use constant RS => chr( 30 ); #~
use constant TAB_SEPARATOR => 0;
use constant CARET_SEPARATOR => 1;
use constant FS_SEPARATOR => 2;
use constant SPACE_SEPARATOR => 3;
use constant AS_ARRAY => 4;
use feature 'switch';
our $segmenter;
sub ZHWordSeg_Setup {
my $dic = shift;
my $dic_encoding = shift;
my $separator = shift;
my $verbose = shift;
$dic_encoding = 'utf8' unless defined( $dic_encoding );
$separator = " " unless defined( $separator );
$verbose = 0 unless defined( $verbose );
if ( defined( $dic ) ) {
$segmenter = ws->new( dic => $dic, dic_encoding => $dic_encoding, seperator => $separator, verbose => $verbose );
} else {
$segmenter = ws->new( dic_encoding => $dic_encoding, seperator => $separator, verbose => $verbose );
}
}
sub ZHWordSeg {
my $source = shift;
print STDERR $source;
my $sepcode = shift;
$source = encode("utf8",$source);
my $stringres = $segmenter->seg($source);
my #arrayres;
given ($sepcode) {
when (TAB_SEPARATOR) {
$stringres =~ tr/ /\t/;
return $stringres;
}
when (CARET_SEPARATOR) {
$stringres =~ tr/ /^/;
$stringres .= "^";
return $stringres;
}
when (FS_SEPARATOR) {
$stringres =~ s/ /FS/eg;
$stringres .= FS;
return $stringres;
}
when (SPACE_SEPARATOR) {
return $stringres;
}
default {
#arrayres = split( / /, $stringres );
return \#arrayres;
}
}
}
sub SetDictionary {
my ($source) = shift;
my $res = set_dic($source);
return $res;
}
1;
=pod
=begin PerlCtrl
%TypeLib = (
PackageName => 'ZHWordSeg',
DocString => 'Chinese word segmentation',
HelpContext => 1,
TypeLibGUID => '{F6C9BD66-7CA1-4610-B77F-E219A7122C18}', # do NOT edit this line
ControlGUID => '{45D47C6A-2B9A-4D62-9CFD-F18C95DC00C5}', # do NOT edit this line either
DispInterfaceIID=> '{007E4E7A-3B75-4DC3-864C-7746860941B3}', # or this one
ControlName => 'BOCWS',
ControlVer => 2, # increment if new object with same ProgID
# create new GUIDs as well
ProgID => 'ZHWordSeg.BOCWS',
LCID => 0,
DefaultMethod => 'ChineseWordSegmenter',
Methods => {
'ChineseWordSegmenter' => {
RetType => VT_VARIANT,
TotalParams => 2,
NumOptionalParams => 1,
ParamList =>
[ 'source' => VT_BSTR,
'sepcode' => VT_I4
]
},
'ChineseWordSegmenter_Setup' => {
RetType => VT_VARIANT,
TotalParams => 4,
NumOptionalParams => 4,
ParamList =>
[ 'dic' => VT_BSTR,
'dic_encoding' => VT_BSTR,
'separator' => VT_BSTR,
'verbose' => VT_BSTR
]
}
}, # end of 'Methods'
Properties => {
TAB_SEPARATOR => {
DocString => "Separate items with TAB (0x0)",
Type => VT_I4,
DispID => 3,
ReadOnly => 1,
},
CARET_SEPARATOR => {
DocString => "Separate items with ^ (0x1)",
Type => VT_I4,
DispID => 4,
ReadOnly => 1,
},
FS_SEPARATOR => {
DocString => "Separate items with ascii 28 (0x2)",
Type => VT_I4,
DispID => 5,
ReadOnly => 1,
},
SPACE_SEPARATOR => {
DocString => "Separate items with space (0x3)",
Type => VT_I4,
DispID => 6,
ReadOnly => 1,
},
AS_ARRAY => {
DocString => "Separate items as array (0x4)",
Type => VT_I4,
DispID => 7,
ReadOnly => 1,
}
}, # end of 'Properties'
); # end of %TypeLib
=end PerlCtrl
=cut
This is the .perlctrl file, in case it matters:
#!C:\Program Files\ActiveState Perl Dev Kit 7.3 Deployment\bin\lib\pai.exe
PAP-Version: 1.0
Packer: C:\Program Files\ActiveState Perl Dev Kit 7.3 Deployment\bin\perlctrl.exe
Script: ZHWordSeg.ctrl
Cwd: P:\BOCWS
Byref: 0
Clean: 0
Date: 2008-10-24 18:05:42
Debug: 127.0.0.1:2000
Dependent: 0
Dyndll: 0
Exe: BOCWS.dll
Force: 1
Gui: 0
Hostname: xi
No-Compress: 0
No-Gui: 0
No-Logo: 0
Runlib:
Shared: none
Singleton: 0
Tmpdir:
Verbose: 0
Version-Comments:
Version-CompanyName:
Version-FileDescription: Wrapper of Lingua::ZH::WordSegmenter.pm
Version-FileVersion: 1.0
Version-InternalName: ZHWordSeg
Version-LegalCopyright:
Version-LegalTrademarks:
Version-OriginalFilename: ZHWordSeg.ctrl
Version-ProductName: BOChineseWordSegmenter
Version-ProductVersion: 1.0
Warnings: 0
Xclude: 1
The only "solution" that I've found was suggested over on news:comp.os.ms-windows.programmer.win32
i am not a PDK user but from experience i can tell you, that you should check the DllRegister exports code and what it internally does, since this is what the regsvr32 calls and this code is creating the registry keys for your com server/proxy, etc, ...
You can track the registry for changes with sysinternal tools like regmon or procmon, just to make sure!
Ultimately, I gave up and went back to a previous version that worked and tweaked it.