Harvesting repository // "From=>" and "Until=>" with user input variable - date

I am using „from=>“ and “until=>” while harvesting a repository with OAI-PMH written in Perl. I tried to let the user type in start- and end-date with <STDIN> in format yyyy-mm-dd. But instead of giving me back the records/results the compiler seems to ignore my query / request, at least it does not give me any results. I have enclosed the relevant parts of the code below. Thanks for your help!
#! /usr/bin/perl
use warnings;
use strict;
use Net::OAI::Harvester;
use Time::Piece;
use Time::Seconds;
my $harvester = Net::OAI::Harvester->new(
baseURL => 'https://opus4.XXX/oai'
);
my $weekAgo = localtime() - ONE_WEEK;
$weekAgo = $weekAgo->ymd;
my $monthAgo = localtime() - ONE_MONTH;
$monthAgo = $monthAgo->ymd;
print "Please enter a number \n
1 for last weeks records \n
2 for last months records \n
3 for records from ... until ...\n";
my $input = <STDIN>;
[...]
elsif( $input == 3 ) {
print "enter start date (yyyy-mm-dd): ";
my $from = <STDIN>;
print "Enter final date (yyyy-mm-dd): ";
my $until = <STDIN>;
my $list = $harvester->listRecords(
metadataPrefix => 'oai_dc',
from=>$from,
until=>$until
);
search($list);
}
sub search {
my $list = $_[0];
while ( my $record = $list->next ) {
my $datestamp = $record->header->datestamp;
print "[ time stamp: ",$datestamp," ]","\n";
my $metadata = $record->metadata;
print "Title: ",$metadata->title,"\n";
[...]}
}

Changed answer due to feedback
Try chomping your input. It may confuse the module you are passing the values to. E.g. change
my $from = <STDIN>;
to
chomp(my $from = <STDIN>);

Related

Perl Temp Converter

Here's my code for Perl, and when I run it, it will display my menu and I can exit with 3. But both 1 and 2 on the menu aren't registering as value input and instead going straight to else and giving an error resulting in an endless loop.
#! c:\Strawberry\perl\bin\Perl.exe
# This is a simple Temperature converter
# that will convert Fahrenheit to Celsius
# and Celsius to Fahrenheit.
use strict;
my $fahr = 0;
my $cel = 0;
my $choice = 0;
my $input = 0;
print "\n";
print "*********************************************\n";
print "*** This is a Temperature Converter ***\n";
print "*********************************************\n";
print "1. Celsius to Fahrenheit.\n";
print "2. Fahrenheit to Celsius. \n";
print "3. Exit\n";
print "*********************************************\n\n";
print "Enter a choice (1-3): ";
my $choice = <STDIN>;
chomp ($choice);
if(&IsNumeric($choice) == 0) {
$choice = 0;
}
while ($choice != 3) {
# Do conversion from C to F
if ($choice == 1) {
print "\nEnter a Temperature: ";
chomp ($cel = "" );
$fahr = ($cel * (9 / 5)) + 32;
# Format to one decimal
$fahr = sprintf("%.1f", $fahr);
print "$cel degrees Celsius = ";
print "$fahr degrees Fahrenheit\n";
}
# Do conversion from F to C
elsif ($choice == 2) {
print "\nEnter a Temperature: ";
chomp ($fahr = "" );
$cel = ($fahr - 32) * 5 / 9;
# Format to one decimal
$cel = sprintf("%.1f", $cel);
print "$fahr degrees Fahrenheit = ";
print "$cel degrees Celsius\n";
}
# Display Error Message
else {
print "\nYou entered and invalid choice please choose a choice from the menu.\n\n";
}
print "\nEnter a Choice (1-3): ";
chomp ($choice = "" );
if(&IsNumeric($choice) == 0) {
$choice = 0;
}
}
# Sub Name: IsNumeric.
# Description: This sub validates the input to check to see if
# the input is a Numeric value
# Example: 100, 1,000, $10.00, and 14.00 are valid inputs.
sub IsNumeric {
my $InputString = shift;
if ($InputString !~ /^[0-9|.|,]*$/) {
return 0;
}
else {
return 1;
}
}
There's a lot of room for improvement in your code, but since this isn't a code review, I'll answer the question you asked and leave it at that. Your pattern of (not) reading input and chomping it is wrong. Instead of this:
print "\nEnter a Temperature: ";
chomp ($cel = "" );
You need to read from STDIN and then chomp without also assigning to an empty string:
$cel = <STDIN>;
chomp($cel);
Or just:
chomp($cel = <STDIN>);
If you had included use warnings at the top of your script, you would have seen the following message or something very similar to it:
Argument "" isn't numeric in numeric ne (!=) at foo.pl line 59,
line 1.
And that would have alerted you that something wasn't being set to the value you thought it was being set to.
Your code never retrieves a temperature. You have
chomp( $cel = "" );
and
chomp ($fahr = "" );
which just assigns the null string to those variables. You need <> in place of ""
Note also that you should never call a Perl subroutine using an ampersand. Wherever you read instructions to do that, it is very out of date. You should also always use warnings 'all' in addition to use strict, and you should avoid using capital letters for local variables
Finally, your regex /^[0-9|.|,]*$/ uses a character class which will match any one of the characters 0 through 9, a comma, a dot or a pipe. I am sure that's not what you meant. Perl won't handle numbers containing comma or pipe characters
After entering an option 1 or 2 you have asked to enter a temperature, but you are not reading this temperature from input.
This chomp ($cel = "" ); and chomp ($fahr = "" );, you didn't read input and choping it. Write it as:
chomp ($cel = <STDIN>);
and
chomp ($fahr = <STDIN>);
And there is no need to declare $choice two times. This code needs lot of improvement.

PERL Fixed Width to CSV based on Input Files

EDITED: I'm attempting to create a brief script that calls for an input fixed width file and a file with the start position and length of each attribute and then outputs the file as CSV instead of fixed width. I haven't messed with removing whitespace yet and am currently focusing on building the file reader portion.
Fixed:
My current issue is that this code returns data from the third row for $StartPosition and from the fourth row for $Length when they should both be first found on the first row of COMMA. I have no idea what is prompting this behavior.
Next issue: It only reads the first record in practice_data.txt I'm guessing it's something where I need to tell COMMA to go back to the beginning?
while (my $sourceLine = <SOURCE>) {
$StartPosition = 0;
$Length = 0;
$Output = "";
$NextRecord ="";
while (my $commaLine = <COMMA>) {
my $Comma = index($commaLine, ',');
print "Comma location found at $Comma \n";
$StartPosition = substr($commaLine, 0, $Comma);
print "Start position is $StartPosition \n";
$Comma = $Comma + 1
$Length = substr($commaLine, $Comma);
print "Length is $Length \n";
$NextRecord = substr($sourceLine, $StartPosition, $Length);
$Output = "$Output . ',' . $NextRecord";
}
print OUTPUT "$Output \n";
}
practice_data.txt
1234512345John Doe 123 Mulberry Lane Columbus Ohio 43215Johnny Jane
5432154321Jason McKinny 423 Thursday Lane Columbus Ohio 43212Jase Jamie
4321543212Mike Jameson 289 Front Street Cleveland Ohio 43623James Sarah
Each record is 100 characters long.
Definitions.txt:
0,10
10,10
20,10
30,20
50,10
60,10
70,5
75,15
90,10
It always helps to provide enough information so that we can at least do some testing without having to read your code and imagine what the data must look like.
I suggest you use unpack, after first building a template from the file that holds the field specifications. Note that the A field specifier trims trailing spaces from the data.
It is all but essential to use the Text::CSV module to parse or generate well-formed CSV data. And I have used the autodie pragma to avoid having to explicitly check and report on the status of every I/O operation.
I have used this data
my_source_data.txt
12345678 ABCDE1234FGHIJK
my_field_spec.txt
0,8
10,5
15,4
19,6
And this program
use strict;
use warnings;
use 5.010;
use autodie;
use Text::CSV;
my #template;
open my $field_fh, '<', 'my_field_spec.txt';
while ( <$field_fh> ) {
my (#info) = /\d+/g;
die unless #info == 2;
push #template, sprintf '#%dA%d', #info;
}
my $template = "#template";
open my $source_fh, '<', 'my_source_data.txt';
my $csv = Text::CSV->new( { binary => 1, eol => $/ } );
while ( <$source_fh> ) {
my #fields = unpack $template;
$csv->print(\*STDOUT, \#fields);
}
output
12345678,ABCDE,1234,FGHIJK
It looks like you're slightly confused on how to read the contents of the COMMA filehandle.. Each time you read <COMMA>, you're reading another line from that file. Instead, read a line into a scalar like my $line = <FH> and use that instead:
while (my $source_line = <SOURCE>) {
$StartPosition = 0;
$Length = 0;
$Output = "";
$Input = $_;
$NextRecord ="";
while (my $comma_line = <COMMA>) {
my $Comma = index($comma_line, ',');
print "Comma location found at $Comma \n";
$StartPosition = substr($comma_line, 0, $Comma);
print "Start position is $StartPosition \n";
$Length = substr($comma_line, $Comma);
print "Length is $Length \n";
$NextRecord = substr($Input, $StartPosition, $Length) + ',';
$Output = "$Output$NextRecord";
}
print OUTPUT "$Output \n";
}

Extracting substring in perl

I need to extract a substring from a given string in my perl program.
The string is of the form:
<PrefixString>_<MyString>_<SuffixString>.pdf
Example: abcd_ThisIsWhatIWant_xyz.pdf
I need to extract "ThisIsWhatIWant"
Can anyone help me please?
Thanks!
This is what I am trying through a subroutine:
sub extractString{
my ($fileName) = #_;
my $offset = 2;
my $delimeter = '_';
my $fileNameLen = index($fileName, $delimeter, $offset);
my $extractedFileName = substr($fileName, 8, $fileNameLen-1);
return $extractedFileName;
}
You can either use split or a regular expression. This short program shows both alternatives.
use strict;
use warnings;
my $filename = 'abcd_ThisIsWhatIWant_xyz.pdf';
my ($substring1) = $filename =~ /_([^_]*)_/;
print $substring1, "\n";
my $substring2 = (split /_/, $filename)[1];
print $substring2, "\n";
output
ThisIsWhatIWant
ThisIsWhatIWant

How to make LWP and HTML::TableExtract spitting out CSV with Text::CSV

I am currently working on a little parser.
i have had very good results with the first script! This was able to run great!
It fetches the data from the page: http://192.68.214.70/km/asps/schulsuche.asp?q=n&a=20
(note 6142 records) - But note - the data are not separated, so the subequent work with the data is a bit difficult. Therefore i have a second script - see below!
Note - friends helped me with the both scripts. I need to introduce myself as a true novice who needs help in migration two in one. So, you see, my Perl-knowlgedge is not so elaborated that i am able to do the migration into one on my own! Any and all help would be great!
The first script: a spider and parser: it spits out the data like this:
lfd. Nr. Schul- nummer Schulname Straße PLZ Ort Telefon Fax Schulart Webseite
1 0401 Mädchenrealschule Marienburg, Abenberg, der Diözese Eichstätt Marienburg 1 91183  Abenberg  09178/509210 Realschulen mrs-marienburg.homepage.t-online.de
2 6581 Volksschule Abenberg (Grundschule) Güssübelstr. 2 91183  Abenberg  09178/215 09178/905060 Volksschulen home.t-online.de/home/vs-abenberg
3 6913 Mittelschule Abenberg  Güssübelstr. 2 91183  Abenberg  09178/215 09178/905060 Volksschulen home.t-online.de/home/vs-abenberg
4 0402 Johann-Turmair-Realschule Staatliche Realschule Abensberg Stadionstraße 46 93326  Abensberg  09443/9143-0,12,13 09443/914330 Realschulen www.rs-abensberg.de
But i need to separate the data: with commas or someting like that!
And i have a second script. This part can do the CSV-formate. i want to ombine it with the spider-logic. But first lets have a look at the first script: with the great spider-logic.
see the code that is appropiate:
#!/usr/bin/perl
use strict;
use warnings;
use HTML::TableExtract;
use LWP::Simple;
use Cwd;
use POSIX qw(strftime);
my $te = HTML::TableExtract->new;
my $total_records = 0;
my $suchbegriffe = "e";
my $treffer = 50;
my $range = 0;
my $url_to_process = "http://192.68.214.70/km/asps/schulsuche.asp?q=";
my $processdir = "processing";
my $counter = 50;
my $displaydate = "";
my $percent = 0;
&workDir();
chdir $processdir;
&processURL();
print "\nPress <enter> to continue\n";
<>;
$displaydate = strftime('%Y%m%d%H%M%S', localtime);
open OUTFILE, ">webdata_for_$suchbegriffe\_$displaydate.txt";
&processData();
close OUTFILE;
print "Finished processing $total_records records...\n";
print "Processed data saved to $ENV{HOME}/$processdir/webdata_for_$suchbegriffe\_$displaydate.txt\n";
unlink 'processing.html';
die "\n";
sub processURL() {
print "\nProcessing $url_to_process$suchbegriffe&a=$treffer&s=$range\n";
getstore("$url_to_process$suchbegriffe&a=$treffer&s=$range", 'tempfile.html') or die 'Unable to get page';
while( <tempfile.html> ) {
open( FH, "$_" ) or die;
while( <FH> ) {
if( $_ =~ /^.*?(Treffer <b>)(d+)( - )(d+)(</b> w+ w+ <b>)(d+).*/ ) {
$total_records = $6;
print "Total records to process is $total_records\n";
}
}
close FH;
}
unlink 'tempfile.html';
}
sub processData() {
while ( $range <= $total_records) {
getstore("$url_to_process$suchbegriffe&a=$treffer&s=$range", 'processing.html') or die 'Unable to get page';
$te->parse_file('processing.html');
my ($table) = $te->tables;
for my $row ( $table->rows ) {
cleanup(#$row);
print OUTFILE "#$row\n";
}
$| = 1;
print "Processed records $range to $counter";
print "\r";
$counter = $counter + 50;
$range = $range + 50;
$te = HTML::TableExtract->new;
}
}
sub cleanup() {
for ( #_ ) {
s/s+/ /g;
}
}
sub workDir() {
# Use home directory to process data
chdir or die "$!";
if ( ! -d $processdir ) {
mkdir ("$ENV{HOME}/$processdir", 0755) or die "Cannot make directory $processdir: $!";
}
}
But as this-above script-unfortunatley does not take care for the separators i have had to take care for a method, that does look for separators. In order to get the data (output) separated.
So with the separation i am able to work with the data - and store it in a mysql-table.. or do something else...So here [below] are the bits - that work out the csv-formate Note - i want to put the code below into the code above - to combine the spider-logic of the above mentioned code with the logic of outputting the data in CSV-formate.
where to set in the code Question: can we identify this point to migrate the one into the other... !?
That would be amazing... I hope i could make clear what i have in mind...!? Are we able to use the benefits of the both parts (/scripts ) migrating them into one?
So the question is: where to set in with the CSV-Script into the script (above)
#!/usr/bin/perl
use warnings;
use strict;
use LWP::Simple;
use HTML::TableExtract;
use Text::CSV;
my $html= get 'http://192.68.214.70/km/asps/schulsuche.asp?q=a&a=20';
$html =~ tr/\r//d; # strip carriage returns
$html =~ s/ / /g; # expand spaces
my $te = new HTML::TableExtract();
$te->parse($html);
my #cols = qw(
rownum
number
name
phone
type
website
);
my #fields = qw(
rownum
number
name
street
postal
town
phone
fax
type
website
);
my $csv = Text::CSV->new({ binary => 1 });
foreach my $ts ($te->table_states) {
foreach my $row ($ts->rows) {
# trim leading/trailing whitespace from base fields
s/^\s+//, s/\s+$// for #$row;
# load the fields into the hash using a "hash slice"
my %h;
#h{#cols} = #$row;
# derive some fields from base fields, again using a hash slice
#h{qw/name street postal town/} = split /\n+/, $h{name};
#h{qw/phone fax/} = split /\n+/, $h{phone};
# trim leading/trailing whitespace from derived fields
s/^\s+//, s/\s+$// for #h{qw/name street postal town/};
$csv->combine(#h{#fields});
print $csv->string, "\n";
}
}
The thing is that i have had very good results with the first script! It fetches the data from the page: http://192.68.214.70/km/asps/schulsuche.asp?q=n&a=20
(note 6142 records) - But note - the data are not separated...!
And i have a second script. This part can do the CSV-formate. i want to combine it with the spider-logic.
where is the part to insert? I look forward to any and all help.
if i have to be more precice - just let me know...
Since you have entered a complete script, I'll assume you want critique of the whole thing.
#!/usr/bin/perl
use strict;
use warnings;
use HTML::TableExtract;
use LWP::Simple;
use Cwd;
use POSIX qw(strftime);
my $te = HTML::TableExtract->new;
Since you only use $te in one block, why are you declaring and initializing it in this outer scope? The same question applies to most of your variables -- try to declare them in the innermost scope possible.
my $total_records = 0;
my $suchbegriffe = "e";
my $treffer = 50;
In general, english variable names will enable you to collaborate with far more people than german names. I understand german, so I understand the intent of your code, but most of SO doesn't.
my $range = 0;
my $url_to_process = "http://192.68.214.70/km/asps/schulsuche.asp?q=";
my $processdir = "processing";
my $counter = 50;
my $displaydate = "";
my $percent = 0;
&workDir();
Don't use & to call subs. Just call them with workDir;. It hasn't been necessary to use & since 1994, and it can lead to a nasty gotcha because &callMySub; is a special case which doesn't do what you might think, while callMySub; does the Right Thing.
chdir $processdir;
&processURL();
print "\nPress <enter> to continue\n";
<>;
$displaydate = strftime('%Y%m%d%H%M%S', localtime);
open OUTFILE, ">webdata_for_$suchbegriffe\_$displaydate.txt";
Generally lexical filehandles are preferred these days: open my $outfile, ">file"; Also, you should check for errors from open or use autodie; to make open die on failure.
&processData();
close OUTFILE;
print "Finished processing $total_records records...\n";
print "Processed data saved to $ENV{HOME}/$processdir/webdata_for_$suchbegriffe\_$displaydate.txt\n";
unlink 'processing.html';
die "\n";
sub processURL() {
print "\nProcessing $url_to_process$suchbegriffe&a=$treffer&s=$range\n";
getstore("$url_to_process$suchbegriffe&a=$treffer&s=$range", 'tempfile.html') or die 'Unable to get page';
while( <tempfile.html> ) {
open( FH, "$_" ) or die;
while( <FH> ) {
if( $_ =~ /^.*?(Treffer <b>)(d+)( - )(d+)(</b> w+ w+ <b>)(d+).*/ ) {
$total_records = $6;
print "Total records to process is $total_records\n";
}
}
close FH;
}
unlink 'tempfile.html';
}
sub processData() {
while ( $range <= $total_records) {
getstore("$url_to_process$suchbegriffe&a=$treffer&s=$range", 'processing.html') or die 'Unable to get page';
$te->parse_file('processing.html');
my ($table) = $te->tables;
for my $row ( $table->rows ) {
cleanup(#$row);
print OUTFILE "#$row\n";
This is the line to change if you want to put commas in separating your data. Look at the join function, it can do what you want.
}
$| = 1;
print "Processed records $range to $counter";
print "\r";
$counter = $counter + 50;
$range = $range + 50;
$te = HTML::TableExtract->new;
}
It's very strange to initialize $te at the end of the loop instead of the beginning. It's much more idiomatic to declare and initialize $te at the top of the loop.
}
sub cleanup() {
for ( #_ ) {
s/s+/ /g;
Did you mean s/\s+/ /g;?
}
}
sub workDir() {
# Use home directory to process data
chdir or die "$!";
if ( ! -d $processdir ) {
mkdir ("$ENV{HOME}/$processdir", 0755) or die "Cannot make directory $processdir: $!";
}
}
I haven't commented on your second script; perhaps you should ask it as a separate question.

Why isn't my Perl format working?

All,
I'm trying to get the code at the bottom to look like a check. Could you help me troubleshoot?
Thanks,
Frank
format STDOUT =
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
$chkno $chkno
$first $last
$addr[0]
$addr[1]
$phone $date
Pay to the
Order of: $payee $amount
For: $memo Stamped Signature
nova> perl proj3_3.pl
Use of comma-less variable list is deprecated at proj3_3.pl line 48.
Name "main::date" used only once: possible typo at proj3_3.pl line 24.
Name "main::last" used only once: possible typo at proj3_3.pl line 12.
Name "main::payee" used only once: possible typo at proj3_3.pl line 27.
Name "main::phone" used only once: possible typo at proj3_3.pl line 21.
Name "main::amount" used only once: possible typo at proj3_3.pl line 30.
Name "main::first" used only once: possible typo at proj3_3.pl line 9.
Name "main::memo" used only once: possible typo at proj3_3.pl line 33.
What is your Check Number? asdf
What is your First Name? asdf
What is your Last Name? adsf
What is your street address (i.e. 555 Anywhere St.)? asdf
What is your city, state, zip code (i.e. Los Angeles, CA, 90210)? as
What is your Phone Number? df
What is the Date? asdf
Who is the Payee? asd
What is the Check Amount? fa
What is the Check's Purpose (MEMO)? sdf
asdf asdf
$first $last
$addr[0]
$addr[1]
$phone $date
Pay to the
Order of: $payee $amount
For: $memo Stamped Signature
.
format STDOUT_TOP =
--------------------------------------------------------------
Edit... Sorry, new to this. Here's my code:
#!/usr/bin/perl -w
use FileHandle
print "What is your Check Number? ";
chomp($chkno = <STDIN>);
print "What is your First Name? ";
chomp($first = <STDIN>);
print "What is your Last Name? ";
chomp($last = <STDIN>);
print "What is your street address (i.e. 555 Anywhere St.)? ";
chomp($addr[0] = <STDIN>);
print "What is your city, state, zip code (i.e. Los Angeles, CA, 90210)? ";
chomp($addr[1] = <STDIN>);
print "What is your Phone Number? ";
chomp($phone = <STDIN>);
print "What is the Date? ";
chomp($date = <STDIN>);
print "Who is the Payee? ";
chomp($payee = <STDIN>);
print "What is the Check Amount? ";
chomp($amount = <STDIN>);
print "What is the Check's Purpose (MEMO)? ";
chomp($memo = <STDIN>);
#print "? ";
#chomp($ = <STDIN>);
#print "? ";
#chomp($ = <STDIN>);
# #>>>> right-justified
# #|||| centered
# #####.## numeric field holder
# #* multiline field holder
# now this line format will automatically apply to CHECK
format STDOUT =
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
$chkno $chkno
$first $last
$addr[0]
$addr[1]
$phone $date
Pay to the
Order of: $payee $amount
For: $memo Stamped Signature
.
# and this page header format will automatically apply to CHECK
format STDOUT_TOP =
--------------------------------------------------------------
.
write STDOUT; #send to the output
Formats consist of a format line followed by the variables that go in that line (comma separated):
#!/usr/bin/perl
use strict;
use warnings;
our $first = "Chas.";
our $last = "Owens";
our $chkno = "123456";
our #addr = ("123 Nowhere Lane", "Citysville, Townshire, 12345");
our $phone = "123-456-7890";
our $date = "2009-08-10";
our $payee = "Stack Overflow";
our $amount = "0.02";
our $amount_str = "no dollars and two cents";
our $memo = "my two cents worth";
write;
format STDOUT =
#<<<<<<<<<<<<< #<<<<<<<<<<<<<<<< #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
$first, $last, $chkno
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$addr[0]
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Date:
$addr[1]
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #<<<<<<<<<<<<<<<<<<<<<<<
$phone, $date
Pay to the
Order of: #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ########.##
$payee, $amount
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< dollars
$amount_str
Signature
Memo: #<<<<<<<<<<<<<<<<<<<<<<<<<<< _________________________
$memo
.
That said, formats have some really bad drawbacks and you should really use a different solution. Another option is to use Perl6::Form. It is a backport of the draft of formats for Perl 6:
#!/usr/bin/perl
use strict;
use warnings;
use Perl6::Form;
my $first = "Chas.";
my $last = "Owens";
my $chkno = "123456";
my #addr = ("123 Nowhere Lane", "Citysville, Townshire, 12345");
my $phone = "123-456-7890";
my $date = "2009-08-10";
my $payee = "Stack Overflow";
my $amount = "0.02";
my $amount_str = "no dollars and two cents";
my $memo = "my two cents worth";
print form
"{<<<<<<<<<<<<<} {<<<<<<<<<<<<<<<<} {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}",
$first, $last, $chkno,
"{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<}",
$addr[0],
"{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<} Date:",
$addr[1],
"{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<} {<<<<<<<<<<<<<<<<<<<<<<<}",
$phone, $date,
"",
"Pay to the",
'Order of: {<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<} ${>>>>>>>.<<}',
$payee, $amount,
"{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<} dollars",
$amount_str,
" Signature",
"Memo: {<<<<<<<<<<<<<<<<<<<<<<<<<<<} _________________________",
$memo;