Inserting lines into text files, or howto "fix" vCards having no n: entry

A

analogquack

I am needing to copy a line of text within a text file and insert a
slightly modified version of that line at a different point in same
file. I have had difficulty finding any text I can grok which would
assist me in this matter.

If you don't care about the details of my specific project, any general
example would be very helpful and gratefully accepted. If you do care
about the details (or if it helps someone else who is trying to perform
the same task) the specifics of my challenge and the script I am
working with follow.

The goal is to add N: and FN: entries into vCards having none by
copying the data from the ORG: line. In English, some contacts have
company names but no first and last name, which are represented on the
N: and FN: lines. Some address book apps expect to have a line in the
N: or FN: fields (Outlook for example) while others don't care (Palm
Desktop for example).

To be specific, the text files look like this:

BEGIN:VCARD
VERSION:2.1
N:LastName;FirstName
FN:FirstName LastName
TITLE:Title
ORG:Company
ADR;WORK:;;WorkAddress;WorkCity;WorkState;WorkZip;WorkCountry
URL;WORK:Website
TEL;WORK:Work#

When there is no N: or FN: fields, they would look like so:

BEGIN:VCARD
VERSION:2.1
TITLE:Title
ORG:Company
ADR;WORK:;;WorkAddress;WorkCity;WorkState;WorkZip;WorkCountry
URL;WORK:Website
TEL;WORK:Work#

I need to copy the ORG: line, "ORG:Company" in this example, and have
it inserted just below the VERSION:2.1 line like so:

BEGIN:VCARD
VERSION:2.1
N:Company
FN:Company
TITLE:Title
ORG:Company
ADR;WORK:;;WorkAddress;WorkCity;WorkState;WorkZip;WorkCountry
URL;WORK:Website
TEL;WORK:Work#

I would like to integrate this into a Perl script I have found in a
prior thread which works well except for this one lacking feature. The
script parses a single Palm Desktop-exported vCard file having multiple
contacts within it and output separate vCard files, one for each
contact. If I could integrate the two functions I would be able to
transform my thousand or so contacts in one fell swoop into files that
can be imported into Outlook. =)

Here is the script as I am currently using it:

#!/usr/bin/perl -w
use strict;
# vcard 2.1 - rfc2425,rfc2426

my $pathname = 'C:/No_Install/_Analog/Office/Output';
my $sourcefilename = '../Palm.vcf';

$/ = ''; # set paragraph mode
open SOURCE, "< $pathname/$sourcefilename"
or die "Couldn't open $sourcefilename for reading: $!";
while ( <SOURCE> ) {
chomp;
my $sinkfilename;
if ( /^(fn[;:].+)/im ) {
( undef, $sinkfilename ) = split /(?<!\\):/, $1, 2;
}
elsif ( /^(n[;:].+)/im ) {
( undef, $sinkfilename ) = split /(?<!\\):/, $1, 2;
# n: field is "lastname;firstname"
# change to "firstname lastname"
$sinkfilename = join ' ', reverse split /(?<!\\);/,
$sinkfilename;
}
$sinkfilename =~ s/[^\w~,\- ]//g;
my $count = '';
if ( -e "$pathname/$sinkfilename" ) {
1 while -e "$pathname/" . ++$count . "$sinkfilename" . ".vcf";
}
$sinkfilename .= ".vcf" ;
open SINK, "> $pathname/$count$sinkfilename"
or die "Couldn't open $count$sinkfilename for writing: $!";
print SINK "$_\n" or die "can't write $count$sinkfilename: $!";
close SINK or die "couldn't close $count$sinkfilename: $!";
}
close SOURCE or die "couldn't close $sourcefilename: $!";

__END__


You can see the thread I gaffed this from here for reference:
http://groups.google.com/group/comp...st&q=split+palm+vcard&rnum=1#79e269b866d12028
 
G

Gunnar Hjalmarsson

I am needing to copy a line of text within a text file and insert a
slightly modified version of that line at a different point in same
file.

perldoc -q "insert a line"
 
A

analogquack

Thank you. Your clue results in a helpful pointer to the Tie::File
module.

It will take me quite a bit to figure out the rest of the puzzle. If
anyone else has any clues regarding how I might use the Tie::File
module to accomplish this task, I'd appreciate it.

Please understand that I am a Perl novice and the following is the best
I can do to describe where I'm trying to go with this. I've got to use
some plain English pseudocode where I've got no idea how to write the
Perl stuff in order to convey myself. I imaging it would be something
like (assuming this processing is done before the vCard file is split
into multiple vCards):

use Tie::File

tie @array, 'Tie::File', $sourcefilename or die;

for (@array) {
If, in a given paragraph there are no lines beginning with "N:" or
"FN:" then {
$org = the line of this paragraph beginning with "ORG:"
$n = $org except we've replaced "ORG" with "N"
$fn = $org except we've replaced "ORG" with "FN"
Insert $n and $fn as two new lines just after the line of this
paragraph which begins with "VERSION:2.1"
}
}

How would I go about completing the "for" statement in proper Perl?

I'm providing an example vCard to complete the illustration, with the
first record (paragraph) being a record which should be ignored,
followed by three that should have "N:" and "FN:" lines inserted into
them:

BEGIN:VCARD
VERSION:2.1
N:LastName;FirstName
FN:FirstName LastName
TITLE:Title
ORG:Company
ADR;WORK:;;WorkAddress;WorkCity;WorkState;WorkZip;WorkCountry

BEGIN:VCARD
VERSION:2.1
ORG:1 Day Paint and Body
ADR;WORK:;;27592 Camino Capistrano;Laguna Niguel;CA
NOTE:7:30am-6pm Mon-Fri
TEL;WORK:(949) 582-1821
X-Palm-Custom1:http://www.1daypaint.com/
END:VCARD

BEGIN:VCARD
VERSION:2.1
ORG:2 Advanced Studios
ADR;WORK:;;65 Enterprise;Aliso Viejo;CA;92656
TEL;WORK:949.443.2112
TEL;FAX:949.330.7581
EMAIL:[email protected]
X-Palm-Custom1:http://www.2advanced.com/
END:VCARD

BEGIN:VCARD
VERSION:2.1
ORG:604 List
X-Palm-Custom1:www.party.net
END:VCARD
 
G

Gunnar Hjalmarsson

Please understand that I am a Perl novice and the following is the best
I can do to describe where I'm trying to go with this.

use Tie::File

tie @array, 'Tie::File', $sourcefilename or die;

for (@array) {
If, in a given paragraph there are no lines beginning with "N:" or
"FN:" then {
$org = the line of this paragraph beginning with "ORG:"
$n = $org except we've replaced "ORG" with "N"
$fn = $org except we've replaced "ORG" with "FN"
Insert $n and $fn as two new lines just after the line of this
paragraph which begins with "VERSION:2.1"
}
}

Then you are not ready for the task. If you want to learn Perl, check
out http://learn.perl.org/
 
J

John W. Krahn

I am needing to copy a line of text within a text file and insert a
slightly modified version of that line at a different point in same
file. I have had difficulty finding any text I can grok which would
assist me in this matter.

If you don't care about the details of my specific project, any general
example would be very helpful and gratefully accepted. If you do care
about the details (or if it helps someone else who is trying to perform
the same task) the specifics of my challenge and the script I am
working with follow.

The goal is to add N: and FN: entries into vCards having none by
copying the data from the ORG: line. In English, some contacts have
company names but no first and last name, which are represented on the
N: and FN: lines. Some address book apps expect to have a line in the
N: or FN: fields

That is probably because according to http://www.imc.org/pdi/vcard-21.rtf the
N field is REQUIRED.
(Outlook for example) while others don't care (Palm
Desktop for example).

[snip]

I would like to integrate this into a Perl script I have found in a
prior thread which works well except for this one lacking feature. The
script parses a single Palm Desktop-exported vCard file having multiple
contacts within it and output separate vCard files, one for each
contact. If I could integrate the two functions I would be able to
transform my thousand or so contacts in one fell swoop into files that
can be imported into Outlook. =)

Here is the script as I am currently using it:

Since I posted the script you are borrowing I might as well post this update
as well. :) I did some testing so hopefully this will work correctly.

#!/usr/bin/perl
use warnings;
use strict;

my $pathname = 'C:/No_Install/_Analog/Office/Output';
my $sourcefilename = '../Palm.vcf';

$/ = ''; # set paragraph mode

open SOURCE, '<', "$pathname/$sourcefilename"
or die "Cannot open '$sourcefilename' $!";

while ( <SOURCE> ) {
chomp;

my $fn;
if ( /^(fn[;:].+)/im ) {
$fn = ( split /(?<!\\):/, $1, 2 )[ 1 ];
}

my $n;
if ( /^(n[;:].+)/im ) {
$n = ( split /(?<!\\):/, $1, 2 )[ 1 ];
# n: field is "lastname;firstname"
# change to "firstname lastname"
$n = join ' ', reverse split /(?<!\\);/, $n;
}

my $org;
if ( !$fn && !$n && /^(org[;:].+)/im ) {
$org = ( split /(?<!\\):/, $1, 2 )[ 1 ];
$fn = $n = $org;
s/(^version:.*\n)/$1N:$n\nFN:$fn\n/im;
}

( my $sinkfilename = $n || $fn || $org ) =~ s/[^\w~,\- ]+//g;
my $count = '';
if ( -e "$pathname/$sinkfilename.vcf" ) {
1 while -e "$pathname/" . ++$count . "$sinkfilename.vcf";
}
$sinkfilename = "$count$sinkfilename.vcf";

open SINK, '>', "$pathname/$sinkfilename"
or die "Cannot open '$sinkfilename' $!";
print SINK "$_\n"
or die "Cannot write '$sinkfilename' $!";
close SINK
or die "Cannot close '$sinkfilename' $!";
}

close SOURCE or die "Cannot close '$sourcefilename' $!";

__END__



John
 
A

analogquack

Woah!

John,

I envy your skills and am grateful for your contribution. I'll let you
know how this works out for me.

I'll also do my best to learn the logic of your parsing and the syntax
of your regex so as to be able to better formulate my questions in the
future. =)

Gunnar,

While I respectfully understand your stance of "you need to do some
Perl literacy work", I'll note here I have already read two complete
O'Reilly books on Perl and I still struggle to understand it. The
language itself is truly obfuscated on its own. but regex of any flavor
has really never been easy to work out as a beginner for any task
beyond the most simple pattern matching. (At least that would be the
typical experience as conveyed to me by most humans I interact with,
though I know many people seem to have natural knack for such things.)

The point of this being to say that I feel community forums such as
Usenet are the appropriate place to learn the more difficult aspects of
any language. Also it should be said that I already have done the RTFM
type work on my end but apparently am just too thick-skulled to grok
this stuff without a little hand-holding.

That being said, I appreciate your tolerance of my my newbie naiveté
and the link to http://learn.perl.org/ . I'll review the material
there. Thanks for the tip. ;)
 
D

DJ Stunks

John said:
Since I posted the script you are borrowing I might as well post this update
as well. :) I did some testing so hopefully this will work correctly.

'borrowing' would indicate it's going to be returned afterward, right?
:p

anyhow, just thought I'd mention that there's quite a few modules on
CPAN for parsing vFiles, and getting and setting vCard (and vCalendar)
attributes in an OO way...

y'all might want to check-ch-check-check-check-ch-check it out.

-jp
 
B

Ben Morrow

Quoth (e-mail address removed):
While I respectfully understand your stance of "you need to do some
Perl literacy work", I'll note here I have already read two complete
O'Reilly books on Perl and I still struggle to understand it.

Which two? I would recommend starting with 'Learning Perl' by Randal
Schwartz, moving on to the Camel Book ('Programming Perl', by Larry Wall
et al.). If those are the two, then you perhaps need to read them again,
more carefully. In particular, if you are learning programming, as
opposed to simply learning Perl, then you must expect it to take a lot
of work. THe concepts involved are not easy.
The language itself is truly obfuscated on its own.

Oi! :)
I wouldn't recommend saying that around here, especially from your
position as someone who doesn't understand Perl yet. From my POV,
(well-written) Perl is one of the clearest languages I've used, and the
most like English in feel (as opposed to the COBOL/SQL/AppleScript-type
languages, which look like English but don't feel like it). If, when you
have a decent grasp of the language, you still feel this, then I would
suggest using something else. This is not meant to put you off using
Perl, or to be disparaging(sp?): different people have different tastes,
and you'll probably be happier writing in a language which fits yours.
but regex of any flavor has really never been easy to work out as a
beginner for any task beyond the most simple pattern matching. (At
least that would be the typical experience as conveyed to me by most
humans I interact with, though I know many people seem to have natural
knack for such things.)

This is certainly true, and the remedy for it is practice. In the
meanwhile, don't be ashamed to use simpler solutions you understand
better. Certainly make heavy use of the /x switch and comments in your
regexes: they will help you understand them later. And don't be put off
if people here say things like 'don't use index for that, use a regex:
it's more Perlish': they are (mostly) honestly trying to help you use
the language as designed. As a rule, if you've read their code and made
a good effort to understand it from the docs (which can be a little
tricky to understand themselves, at times, I know), and you provide
evidence of this, people will be willing to explain things further until
you do understand them.

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top