MARC(3pm)
π MARC(3pm) β the Perl module for wrangling MAchine Readable Cataloging records π
okay bestie, sit down, hydrate, and let's talk about MARC.pm, which is the Perl extension that lets you manipulate MAchine Readable Cataloging records. yes it's a mouthful. no we're not abbreviating it, the acronym is literally in the name, it's giving library science royalty. π
β¨ NAME β¨
MARC.pm β a Perl extension to manipulate MAchine Readable Cataloging records. periodt. π
πΈ SYNOPSIS πΈ
here's the whole vibe in code form, no notes:
use MARC;
# constructors
$x=MARC->new();
$x=MARC->new("filename","fileformat");
$x->openmarc({file=>"makrbrkr.mrc",'format'=>"marcmaker",
increment=>"5", lineterm=>"\n",
charset=>\%char_hash});
$record_num=$x->createrecord({leader=>"00000nmm 2200000 a 4500"});
# input/output operations
$y=$x->nextmarc(10); # increment
$x->closemarc();
print $x->marc_count();
$x->deletemarc({record=>'2',field=>'110'});
$y=$x->selectmarc(['4','21-50','60']);
# character translation
my %inc = %{$x->usmarc_default()}; # MARCMaker input charset
my %outc = %{$x->ustext_default()}; # MARCBreaker output charset
# data queries
@records = $x->searchmarc({field=>"245"});
@records = $x->searchmarc({field=>"260",subfield=>"c",
regex=>"/19../"});
@records = $x->searchmarc({field=>"245",notregex=>"/huckleberry/i"});
@results = $x->getvalue({record=>'12',field=>'856',subfield=>'u'});
# header and control field operations
$rldr = $x->unpack_ldr($record);
print "Desc is $rldr->{Desc}";
next if ($x->bib_format($record) eq 'SERIALS');
$rff = $x->unpack_008($record);
last if ($rff->{'Date1'}=~/00/ or $rff->{'Date2'}=~/00/);
# data modifications
$x->addfield({record=>"2", field=>"245",
i1=>"1", i2=>"4", ordered=>'y', value=>
[a=>"The adventures of Huckleberry Finn /",
c=>"Mark Twain ; illustrated by E.W. Kemble."]});
my $update245 = {field=>'245',record=>2,ordered=>'y'};
my @u245 = $x->getupdate($update245);
$x->deletemarc($update245);
$x->addfield($update245, @u245_modified);
# outputs
$y = $x->output({'format'=>"marcmaker", charset=>\%outc});
$x->output({file=>">>my_text.txt",'format'=>"ascii",record=>2});
$x->output({file=>">my_marcmaker.mkr",'format'=>"marcmaker",
nolinebreak=>'y',lineterm=>'\n'});
$x->output({file=>">titles.html",'format'=>"html", 245=>"Title: "});
# manipulation of individual marc records.
@recs = $x[1..$#$x];
grep {$_->unpack_ldr() && 0} @recs;
@LCs = grep {$_->unp_ldr{Desc} eq 'a' &&
$_->getvalue({field=>'040'}) =~/DLC\c_.DLC/} @recs;
foreach my $rec (@LCs) {
print $rec->output({format=>'usmarc'});
}
# manipulation as strings.
foreach my $rec (@LCs) {
my $stringvar = $rec->as_string();
$stringvar=~s[^(
100\s # main entries of this stripe..
..\s # (don't care about indicators)
\c_.\s*
)(\S) # take the first letter..
] [
${1}uc($2) # and upcase it. All authors have
# upcase first letters in my library.
]xm; # x means 'ignore whitespace and allow
# embedded comments'.
$rec->from_string($stringvar);
my ($i2,$article) = $stringvar =~/245 .(.) \c_.(.{0,9})/;
$article = substr($article,0,$i2) if $i2=~/\d/;
print "article $article is not common" unless $COMMON_ARTS{$article};
}
likeβ¦ it's a lot but it's also everything. we love a thorough queen. π«Ά
π¦ DESCRIPTION π¦
so MARC.pm is a Perl 5 module for reading in, manipulating, and outputting bibliographic records in the USMARC format. heads up bestie: you need Perl 5.004 or greater for MARC.pm to work properly. and since it's a Perl module, you use MARC.pm from one of your own Perl scripts β it does not come with a little CLI, sorry, you're writing code. if you just want to see what sorts of conversions are possible, there's a web interface to MARC.pm where you can upload MARC files and retrieve the results (details are down below in the section called "Web Interface").
BUT β and this is the main character part β to get the full functionality you'll probably want to install MARC.pm on your server or PC. MARC.pm can handle both single and batches of MARC records, no cap. the limit on how many records can be in a batch is determined by the memory capacity of the machine you're running. if memory is an issue for you, MARC.pm will let you read in records from a batch gradually. MARC.pm also includes a variety of tools for searching, removing, and even creating records from scratch. it's giving full service fr fr.
π« Types of Conversions π«
here's what it can turn things into, the glow-up menu:
- MARC -> ASCII β separates the MARC fields out into separate lines. simple. clean. iconic.
- MARC <-> MARCMaker β the MARCMaker format is a format that was developed by the Library of Congress for use with their DOS based MARCMaker and MARCBreaker utilities. this format is particularly useful for making global changes (ie. with a text editor's search and replace) and then converting back to MARC (
MARC.pmwill read properly formatted MARCMaker records, so you can go both ways bestie). for more information about the MARCMaker format seehttp://lcweb.loc.gov/marc/marcsoft.html - MARC -> HTML β the MARC to HTML conversion creates an HTML file from the fields and field labels that you supply. you could possibly use this to create HTML bibliographies from a batch of MARC records. cute AND functional.
- MARC <-> XML β XML support is handled by
MARC::XMLwhich is a subclass ofMARC.pmand is also available for download from the CPAN. - MARC -> URLS β this conversion will extract URLs from a batch of MARC records. the URLs are found in the
856field, subfieldu. the HTML page that is generated can then be used with link-checking software to determine which URLs need to be repaired. hopefully library system vendors will soon support this activity soon and make this conversion unnecessary! π (we love the optimism, honestly.)
π Downloading and Installing π
π Download π
the module is provided in standard CPAN distribution format. it will extract into a directory MARC-version with any necessary subdirectories. change into the MARC top directory. download the latest version from http://www.cpan.org/modules/by-module/MARC/ β go get her. π
π Unix π
perl Makefile.PL
make
make test
make install
π Win9x/WinNT/Win2000 π
perl Makefile.PL
perl test.pl
perl install.pl
π Test π
once you have installed, you can check if Perl can find it. change to some other directory and execute from the command line:
perl -e "use MARC"
if you do not get any response that means everything is OK! β¨ if you get an error like Can't locate method "use" via package MARC. then Perl is not able to find MARC.pm β double check that the file copied it into the right place during the install. (yes, the silence is the success. it's giving quiet luxury.)
π§ Todo π§
the to-do list, aka the vision board:
- Support for other MARC formats (UKMARC, FINMARC, etc).
- Create a map and instructions for using and extending the
MARC.pmdata structure. - Develop better error catching mechanisms.
- Support for MARC <-> Unicode character conversions.
- MARC <-> EAD (Encoded Archival Description) conversion?
- MARC <-> DC/RDF (Dublin Core Metadata encoded in the Resource Description Framework)?
somebody get this module a team of girlies, the ambition is unmatched. π€π
π Web Interface π
a web interface to MARC.pm is available at http://libstaff.lib.odu.edu/cgi-bin/marc.cgi where you can upload records and observe the results. if you'd like to check out the cgi script take a look at http://libstaff.lib.odu.edu/depts/systems/iii/scripts/MARCpm/marc-cgi.txt. however, to get the full functionality you will want to install MARC.pm on your server or PC. yeah, it said it twice. because it means it. π₯
π Option Templates π
a MARC record is a complex structure. hence, most of the methods have a number of options. since a series of operations frequently uses many the same options for each method, you can create a single variable that forms a "template" for the desired options. the variable points to a hash β and the hash keys have been selected so the same hash works for all of the related methods. it's giving reusable slay.
my $loc852 = {record=>1, field=>'852', ordered=>'y'};
my ($found) = $x->searchmarc($loc852);
if (defined $found) {
my @m852 = $x->getupdate($loc852);
$x->deletemarc($loc852);
# change @m852 as desired
$x->updaterecord($loc852, @m852fix);
}
else {
$x->addfield($loc852, @m852new);
}
the following methods are specifically designed to work together using Option Templates. the required options are shown as bold. any "(default)" options are shown in parentheses. although deletemarc() permits an array for the record option, a single record should be used in a Template. the subfield option must not be used in a Template that uses both deletemarc and one of the methods that acts on a complete field like addfield(). the value option must not be used with updaterecord(). π¨ read that twice, bestie, it's a real constraint.
deletemarc() - field (all), record (all), subfield [supplemental]
searchmarc() - field, regex, notregex, subfield [supplemental]
getvalue() - record, field, subfield, delimiter [supplemental]
getupdate() - record, field
addfield() - record, field, i1 (' '), i2 (' '), value, ordered ('y')
updaterecord() - record, field, i1 (' '), i2 (' '), ordered ('y')
the methods that accept a subfield option also accept specifying it as a supplemental parameter. supplemental parameters append/overwrite the hash values specified in the template. so you can start with a template and then tweak it inline:
$x->deletemarc($loc852, 'subfield','k');
my $f260 = {field=>"260",regex=>"/19../"};
my @records=$x->searchmarc($f260,'subfield','c');
foreach $found (@records) {
$value = $x->getvalue($f260,'record',"$found",'field',"245");
print "TITLE: $value\n";
}
π METHODS π
here is a list of the methods in MARC.pm that are available to you for reading in, manipulating and outputting MARC data. buckle up, it's a whole roster. π
β new() β
creates a new MARC object. a fresh one. a baby. π£
$x = MARC->new();
you can also use the optional file and format parameters to create and populate the object with data from a file. if a file is specified it will read in the entire file. if you wish to read in only portions of the file see openmarc(), nextmarc(), and closemarc() below. the format defaults to 'usmarc' if not specified. it is only used when a file is given.
$x = MARC->new("mymarc.dat","usmarc");
$x = MARC->new("mymarcmaker.mkr","marcmaker");
creates a new MARC::Rec object:
$rec=MARC::Rec->new();
$rec=MARC::Rec->new($filehandle,"usmarc");
MARC::Rec objects are typically created by reading from a filehandle using nextrec() and a proto MARC::Rec object or by directly stuffing the @{$rec->{'array'}} array. yes, you can just shove data in there yourself if that's your energy. π
more of this manual is coming, bestie β this was just part 1, don't go anywhere π
π openmarc() π
okay so this one opens up a file and starts reading data into a MARC object, bestie. if you don't tell it a format, openmarc() will just default to USMARC because she knows what you need. the increment parameter is literally how many records you want her to read from the file. no increment? then the file just gets opened and zero records get read in β she's just peeking in the door, not sitting down. and if increment is set to -1, then the entire file gets read in. the whole thing. no crumbs left. π
$x = new MARC;
$x->openmarc({file=>"mymarc.dat",'format'=>"usmarc",
increment=>"1"});
$x->openmarc({file=>"mymarcmaker.mkr",'format'=>"marcmaker",
increment=>"5"});
π note:openmarc()will return the number of records read in. if the file opens successfully but no records are read, it returns"0 but true"β iconic behavior, honestly. like this:
$y=$x->openmarc({file=>"mymarc.dat",'format'=>"usmarc",
increment=>"5"});
print "Read in $y records!";
so when the MARCMaker format is specified, you can use the lineterm parameter to override the CRLF line-ending default π β and yeah, that's because the format was originally released for MS-DOS, so it's got history, okay, respect the elders. a charset parameter takes a hash-reference to a user supplied character translation table. the "usmarc.txt" table that ships with the LoC. MARCMaker utility is used internally as the default. and if you only wanna tweak like two little characters, you can use the usmarc_default method to get a hash-reference to it β see the example below, i gotchu:
$x->openmarc({file=>"makrbrkr.mrc",'format'=>"marcmaker",
increment=>"5",lineterm=>"\n",
charset=>\%char_hash});
π nextmarc() π
once a file is open, nextmarc() is your girl for reading in the next group of records. you can pass the increment too, anytime, if the number needs to change β no judgment, moods shift. an increment of -1 will read in the rest of the file. and heads up: specifying the increment will change the value that was set with openmarc(). otherwise, that value is the default. she remembers. π«Ά
$x->nextmarc();
$x->nextmarc(10);
$x->nextmarc(-1);
π note: just likeopenmarc(),nextmarc()will return the number of records read in. besties matching besties.
$y=$x->nextmarc();
print "$y more records read in!";
π¦ nextrec() π¦
MARC:Rec instances can read from a filehandle and produce a new MARC::Rec instance β¨ and if nextrec is passed a string, it will read from that instead. the string should be formatted according to the {format} field of the instance. so basically, know your format, okay?
the cases where a new instance can't be created get classified by a status value:
my ($newrec,$status) = $rec->nextrec();
so $status is undef if we are at the end of the filehandle. but if the data read from the filehandle cannot be made into a marc record, $status will be negative. for example, $status is -1 if there is a distinction between recsize and the leader definition of recsize, and -2 if the leader is not numeric. dinosaurs said numbers only π¦.
an idiom for reading records incrementally with MARC::Recs is:
my $proto=MARC::Rec->new($filehandle,$format);
while (1) {
my ($rec,$status)=$proto->nextrec();
last unless $status;
die "Bad record, bad, bad record: error $status"
if $status <0;
print $rec->output({$format=>'ascii'});
# or replace print and output with your own functions/methods.
}
close $filehandle or die "File $filehandle is not happy on close\n";
and if you are getting records from an external source as strings, the idiom is:
my $proto=MARC::Rec->new($filehandle,$format);
while (1) {
my $string = get_external_marc();
last unless $string;
my ($rec,$status)=$proto->nextrec($string);
last unless $status;
die "Bad record, bad, bad record: error $status"
if $status <0;
print $rec->output({$format=>'ascii'});
# or replace print and output with your own functions/methods.
}
π closemarc() π
if you are finished reading in records from a file, you should close it immediately. don't leave her hanging, that's so rude π₯Ί
$x->closemarc();
π add_map() π
add_map() takes a recnum and a ref to a field in ($tag, $i1,$i2,a=>"bar",...) or ($tag, $field) formats and will append to the various indices that we have hanging off that record. it is intended for use in creating records de novo and as a component for rebuild_map(). it carefully does not copy subfield values or entire fields, maintaining some reference relationships β very mindful, very demure.
what this means for indices created with add_map is that you can directly edit subfield values in $marc->[recnum]{array} and the index will adjust automatically. vice-versa, if you edit subfield values in $marc->{recnum}{tag}{subfield_code} the fields in $marc->[recnum]{array} will adjust too. it's giving mutual support, it's giving healthy relationship π
but if you change structural information in the array with such an index, you must rebuild the part of the index related to the current tag (and possibly the old tag if you change the tag). no skipping steps, bestie.
use MARC 1.02;
while (<>) {
chomp;
my ($author,$title) = split(/\t/);
my $rnum = $x->createrecord({leader=>
"00000nmm 2200000 a 4500"});
my @auth = (100, ' ', ' ', a=>$author);
my @title = (245, ' ', ' ', a=>$title);
push @{$x->[$rnum]{array}}, \@auth;
$x->add_map($rnum,\@auth);
push @{$x->[$rnum]{array}}, \@title;
$x->add_map($rnum,\@title);
}
MARC::Rec::add_map($rfield) does not need the record specification and has the same effect as add_map. slay, less typing β¨
π rebuild_map π
rebuild_map takes a recnum and a tag and will synchronise the index with the array elements of the marc record at the recnum with that tag. it's the glow-up, the refresh, the factory reset for your index π
#Gonna change all 099's to 092's since this is a music collection.
grep {$->[0] =~s/099/092} @{$x->[$recnum]{array}};
#Oops, now the index is out of date on the 099's...
$x->rebuild_map($recnum,099);
#... and the 092's since we now have new ones.
$x->rebuild_map($recnum,092);
#All fixed.
MARC::Rec::rebuild_map($tag) does not need the record number and has the same effect as rebuild_map.
β rebuild_map_all β
rebuild_map takes a recnum and will synchronise the index with the array elements of the marc record at the recnum. (yeah yeah, it says the same thing as the one above β i'm not the one who wrote the docs, i'm just the one making it cute π)
MARC::Rec::rebuild_map_all() does not need the record number and has the same effect as rebuild_map_all.
πΈ getfields πΈ
getfields takes a template and returns an array of fieldrefs from the record number implied by that template. the fields referred are fields from the $marc->[$recnum]{array} group. and they are all the fields from the first one with the tag from the template to the last with that tag β the whole sandwich, crust to crust π₯ͺ
some marc records (e.g. cjk) may have fields with other tags mixed in though, so watch out. consecutive calls to updatefields with a different tag and the same record are probably a bad idea unless you have assurance that fields with the same tag are always together. like, that's on you and your data, friend.
MARC::Rec::getfields is identical to getfields, but ignores any record specification in the template.
π
marc_count() π
returns the total number of records in a MARC object. this method was previously named length(), but that conflicts with the Perl built-in of the same name, so... yeah, name collision, embarrassing. use the new name, the old one is deprecated and will disappear shortly. don't get attached π₯²
$length=$x->marc_count();
π«Ά getfirstvalue() π«Ά
getfirstvalue will return the first value of a field or subfield or indicator or i12 in a particular record found in the MARC object. and the best part? it does not depend on the index being up to date. she needs nothing from you, she's self-sufficient π
MARC::Rec::getfirstvalue is identical to getfields, but ignores any record specification in the template.
π getvalue() π
this method will retrieve MARC field data from a specific record in the MARC object. getvalue() takes four parameters: record, field, subfield, and delimiter. and since a single MARC record could contain several of the fields or subfields, the results are returned to you as an array. plural behavior, plural energy π
- if you only pass
recordandfield, you will be returned the entire field without subfield delimiters. - optionally you can use
delimiterto specify what character to use for the delimiter, and you will also get the subfield delimiters. - if you also specify
subfield, your results will be limited to just the contents of that subfield. - repeated subfield occurances will end up in separate array elements in the order in which they were read in. chronology matters, bestie π°οΈ
- the subfield designations
'i1','i2'and'i12'can be used to get indicator(s).
#get the 650 field(s)
@results = $x->getvalue({record=>'1',field=>'650'});
#get the 650 field(s) with subfield delimiters (ie. |x |v etc)
@results = $x->getvalue({record=>'1',field=>'650',delimiter=>'|'});
#get all of the subfield u's from the 856 field
@results = $x->getvalue({record=>'12',field=>'856',subfield=>'u'});
MARC::Rec::getvalue($template) is identical to getvalue, but ignores any record specification.
π§ unpack_ldr($record) π§
returns a ref to a hash version of the record'th LDR. installs the ref in $marc as $marc->[$record]{unp_ldr}. it's unpacking, it's revealing, it's the leader in her most honest form β¨
my $rldr = $x->unpack_ldr(1);
print "Desc is $rldr{Desc}";
my ($m040) = $x->getvalues({record=>'1',field=>'040'});
print "First record is LC, let's leave it alone"
if $rldr->{'Desc'} eq 'a' && $m040=~/DLC\s*\c_c\s*DLC/;
the hash version contains the following information β the columns are Key, 000-Pos, length, and Function [standard value], so read it like that, okay? π
rec_lenβ pos00-04, length5β Logical Record LengthRecStatβ pos05, length1β Record StatusTypeβ pos06, length1β Type of RecordBLvlβ pos07, length1β Bibliographic LevelCtrlβ pos08, length1Undefldrβ pos09-11, length3β[x22]base_addrβ pos12-16, length5β Base Address of DataELvlβ pos17, length1β Encoding LevelDescβ pos18, length1β Descriptive Cataloging Formln_recβ pos19, length1β Linked-Record Codelen_len_fieldβ pos20, length1β Length "length of field"[4]len_start_charβ pos21, length1β Length "start char pos"[5]len_implβ pos22, length1β Length "implementation dep"[0]Undef2ldrβ pos23, length1β[0]
MARC::Rec::unpack_ldr() is identical to unpack_ldr, but does not need the record number.
π get_hash_ldr($record) π
takes a record number. returns a ref to the cached version of the hash ldr if it exists. and she does this without overwriting the hash ldr β very respectful of boundaries, we love to see it. this allows external code to safely manipulate hash versions of the ldr. it's giving trust. it's giving consent. π€
my $rhldr = $marc->get_hash_ldr($record);
return undef unless $rhldr;
$rhldr->{'Desc'} =~ s/a/b/;
$ldr = $x->pack_ldr($record);
MARC::Rec::get_hash_ldr() is identical to get_hash_ldr, but does not need the record number.
β¨ pack_ldr($record) β¨
okay bestie, this one takes a record number and updates the ldr that needs updating. simple as that π
$marc->[$record]{'unp_ldr'}{'Desc'} =~ s/a/b/;
my $ldr = $x->pack_ldr($record);
return undef unless $ldr;
MARC::Rec::pack_ldr() is literally the same as pack_ldr, except it doesn't need the record number. no cap. π«Ά
β¨ bib_format($record) β¨
Takes a record number and returns the "format" that gets used to figure out what the fixed fields in 008 actually mean. It will force an update of the ldr based on any existing hash version. slay. π
foreach $record (1..$#$x) {
next if $x->bib_format($record) eq 'SERIALS';
# serials are hard
do_something($x->[record]);
}
MARC::Rec::bib_format() is identical to bib_format, but does not need the record number. periodt. π
β¨ unpack_008($record) β¨
Returns a ref to the hash version of the 008 field, based on the field's value. Then installs that ref as $marc->[$record]{unp_008}. it's giving "I know what that field means" energy β¨
foreach $record (1..$#$x) {
my $rff = $x->unpack_008($record);
print "Record $record: Y2K problem possible"
if ($rff->{'Date1'}=~/00/ or $rff->{'Date2'}=~/00/);
}
MARC::Rec::unpack_008() is identical to unpack_008, but does not need the record number. π¦
β¨ get_hash_008($record) β¨
Takes a record number. Returns a ref to the cached version of the hash 008 if it exists. Does this without overwriting the hash 008. That lets external code safely mess with hash versions of the 008. iconic behavior. π
my $rh008 = $marc->get_hash_008($record);
return undef unless $rh008;
$rh008->{'Date1'} =~ s/00/01/;
my $m008 = $x->pack_008($record);
return undef unless $m008;
MARC::Rec::get_hash_008() is identical to get_hash_008, but does not need the record number. π
β¨ pack_008($record) β¨
Takes a record number and updates the appropriate 008. Will force an update of the ldr based on any existing hash version. the way it just packs everything back up... obsessed. π
foreach $record (1..$#$x) {
my $rff = $x->unpack_008($record);
$rff->{'Date1'}='2000';
print "Record:$record Y2K problem created";
$x->pack_008($record);
# New value is in the 008 field of $record'th marc
}
MARC::Rec::pack_008() is identical to pack_008, but does not need the record number. πΈ
β¨ deletefirst() β¨
deletefirst() takes a template. It deletes the field data for a first match, using the template, and leaves the rest alone. If the template has a subfield element, it deletes based on the subfield information in the template. If the last subfield of a field is deleted, deletefirst() also deletes the field. It complains about attempts to delete indicators. If there is no match, it does nothing. Deletefirst also rebuilds the map if the template asks for that $do_rebuild_map. Deletefirst returns the number of matches deleted (that would be 0 or 1), or undef if it feels grumpy (i.e. carps). π€
MARC::Rec::deletefirst($template) is identical to deletefirst, but ignores any record number specified by $template.
Most use of deletefirst is expected to be by MARC::Tie. π
β¨ deletemarc() β¨
This method will let you remove a specific record, fields or subfields from a MARC object. Accepted parameters include: record, field and subfield. Note: you can use the .. operator to delete a range of records. deletemarc() will return the number of items deleted (be they records, fields or subfields). The record parameter is optional. It defaults to all user records [1..$#marc] if not specified. no stress. π
#delete all the records in the object
$x->deletemarc();
#delete records 1-5 and 7
$x->deletemarc({record=>[1..5,7]});
#delete all of the 650 fields from all of the records
$x->deletemarc({field=>'650'});
#delete the 110 field in record 2
$x->deletemarc({record=>'2',field=>'110'});
#delete all of the subfield h's in the 245 fields
$x->deletemarc({field=>'245',subfield=>'h'});
β¨ updatefirst() β¨
updatefirst() takes a template, and an array from $marc->[recnum]{array}. It replaces/creates the field data for a first match, using the template and the array, and leaves the rest alone. If the template has a subfield element, (this includes indicators) it ignores all other information in the array and only updates/creates based on the subfield information in the array. If the template has no subfield information then indicators are left untouched unless a new field needs to be created, in which case they are left blank. bestie behavior. π¦
MARC::Rec::updatefirst($template) is identical to deletefirst, but ignores any record number specified by $template.
Most use of updatefirst() is expected to be from MARC::Tie. It does not currently provide a useful return value. π
β¨ updatefields() β¨
updatefields() takes a template which specifies recnum, a $do_rebuild_map and a field (needs the field in case $rafields->[0] is empty). It also takes a ref to an array of fieldrefs formatted like the output of getfields(), and replaces/creates the field data. It assumes that it should replace the fields with the first tag in the fieldrefs. It calls rebuild_map() if $do_rebuild_map. slay. π₯
#Let's kill the *last* 500 field.
my $loc500 = {record=>1,field=>500,rebuild_map=>1};
my @rfields = $x->getfields($loc500);
pop @rfields;
$x->updatefields($loc500,\@rfields);
β¨ getmatch() β¨
getmatch() takes a subfield code (can be an indicator) and a fieldref. Returns 0 or a ref to the value to be updated. lowkey handy. π
#Let's update the value of i2 for the *last* 500
my $loc500 = {record=>1,field=>500,rebuild_map=>1};
my @rfields = $x->getfields($loc500);
my $rvictim = pop @rfields;
my $rval = getmatch('i2',$rvictim);
$$rval = "4" if $rval;
MARC::Rec::getmatch($subf,$rfield) is identical to getmatch; π
β¨ insertpos() β¨
insertpos() takes a subfield code (can not be an indicator), a value, and a fieldref. Updates the fieldref with the first place that the fieldref can match. Assumes there is no exact subfield match in $fieldref. it's giving "insert with intention" β¨
#Let's update the value of subfield 'a' for the *last* 500
my $value = "new info";
my $loc500 = {record=>1,field=>500,rebuild_map=>1};
my @rfields = $x->getfields($loc500);
my $rvictim = pop @rfields;
my $rval = getmatch('a',$rvictim);
if ($rval) {
$$rval = $value ;
} else {
$x->insertpos('a',$value,$rvictim);
}
MARC::Rec::insertpos($subf,$value,$rfield) is identical to insertpos; π
β¨ selectmarc() β¨
This method will select specific records from a MARC object and delete the rest. You can specify both individual records and ranges of records in the same way as deletemarc(). selectmarc() will also return the number of records deleted. iconic selection process. π
$x->selectmarc(['3']);
$y=$x->selectmarc(['4','21-50','60']);
print "$y records selected!";
β¨ searchmarc() β¨
This method will let you search through a MARC object, and retrieve record numbers for records that matched your criteria. You can search for: 1) records that contain a particular field, or field and subfield ; 2) records that have fields or subfields that match a regular expression ; 3) and records that have fields or subfields that do not match a regular expression. The record numbers are returned to you in an array which you can then use with deletemarc(), selectmarc() and output() if you want. π
- 1) Field/Subfield Presence:
@records=$x->searchmarc({field=>"245"});
@records=$x->searchmarc({field=>"245",subfield=>"a"});
- 2) Field/Subfield Match:
@records=$x->searchmarc({field=>"245",
regex=>"/huckleberry/i"});
@records=$x->searchmarc({field=>"260",subfield=>"c",
regex=>"/19../"});
- 3) Field/Subfield NotMatch:
@records=$x->searchmarc({field=>"245",
notregex=>"/huckleberry/i"});
@records=$x->searchmarc({field=>"260",
subfield=>"c",notregex=>"/19../"});
β¨ createrecord() β¨
You can use this method to initialize a new record. It only takes one optional parameter, leader which sets the 24 characters in the record leader: see http://lcweb.loc.gov/marc/bibliographic/ecbdhome.html for more details on the leader. Note: you do not need to pass character positions 00-04 or 12-16 since these are calculated by MARC.pm if outputting to MARC you can assign 0 to each position. If no leader is passed a default USMARC leader will be created of "00000nam 2200000 a 4500". createrecord() will return the record number for the record that was created, which you will need to use later when adding fields with addfield(). Createrecord now makes the new record an instance of an appropriate MARC::Rec subclass. slay. π
use MARC;
my $x = new MARC;
$record_number = $x->createrecord();
$record_number = $x->createrecord({leader=>
"00000nmm 2200000 a 4500"});
MARC::Rec::createrecord($leader) returns an instance of a suitable subclass of MARC::Rec. π
β¨ getupdate() β¨
The getupdate() method returns an array that contains the contents of a field in a defined order that permits restoring the field after deleting it. This permits changing only individual subfields while keeping other data intact. If a field is repeated in the record, the resulting array separates the field information with an element containing "\036" - the internal field separator which can never occur in real MARC data parameters. A non-existing field returns "undef". An example will make the structure clearer. The next two MARC fields (shown in ASCII) will be described in the following array:
246 30 $aPhoto archive
246 3 $aAssociated Press photo archive
my $update246 = {field=>'246',record=>2,ordered=>'y'};
# next two statements are equivalent
my @u246 = $x->getupdate($update246);
# or
my @u246 = ('i1','3','i2','0',
'a','Photo archive',"\036",
'i1','3','i2',' ',
'a','Associated Press photo archive',"\036");
After making any desired modifications to the data, the existing field can be replaced using the following sequence (for non-repeating fields):
$x->deletemarc($update246));
my @records = ();
foreach my $y1 (@u246) {
last if ($y1 eq "\036");
push @records, $y1;
}
$x->addfield($update246, @records);
π«Ά updaterecord()
Okay so updaterecord() is basically the main character version of that whole sequence we just did β more complete, with actual error checking, and it can split your update array into multiple addfield() calls when you're dealing with repeating fields. π
It takes an array of key/value pairs, formatted exactly like whatever getupdate() spits out, and it replaces/creates the field data. For repeated tags, a "\036" element is what delimits the data into separate addfield() commands. It hands you back the number of successful addfield() commands, or "undef" if it flops. π
$repeats = $x->updaterecord($update246, @u246); # same as above
β¨ addfield()
This one lets you add fields to a record you specify. Ngl the syntax looks kinda confusing at first glance, but once it clicks you'll be adding fields to records you read in or records you made with createrecord() like it's nothing. π
addfield() takes six parameters:
recordβ the record number you're adding the field tofieldβ the field you wanna create (ie.245)i1β one character for the first indicatori2β one character for the second indicatorvalueβ the subfield data you're adding to the fieldorderedβ leave it alone andaddfield()will automatically try to insert your new field in tag order (ie. a500field before a520field). Set it to"no"and it just chucks the field on the end instead.
Here's the vibe: πΈ
$y = $x->createrecord(); # $y will store the record number created
$x->addfield({record=>"$y", field=>"100", i1=>"1", i2=>"0",
value=> [a=>"Twain, Mark, ", d=>"1835-1910."]});
$x->addfield({record=>"$y", field=>"245",
i1=>"1", i2=>"4", value=>
[a=>"The adventures of Huckleberry Finn /",
c=>"Mark Twain ; illustrated by E.W. Kemble."]});
So that example initialized a brand new record, then added a 100 field and a 245 field. β¨ If you want some more creative uses of addfield(), go peek at the EXAMPLES section, bestie.
Here's the cute part: the value parameters β i1 and i2 included β can be specified using a separate array. That means you can restore field(s) straight from the array getupdate() returned, either as-is or with your own little tweaks. Just know that if you include i1 and i2 key/value pairs, they must come first and in that order. π
# same as "100" example above
my @v100 = 'i1','1','i2',"0",'a',"Twain, Mark, ",
'd',"1835-1910.";
$x->addfield({record=>"$y", field=>"100"}, @v100);
π add_005s()
add_005s takes a specification of records (it defaults to everything) and updates the indicated records with fresh 005 fields β that's the date of last transaction, iykyk. π
π output() π
output() is the multifunctional queen of creating formatted output from a MARC object. She's got three parameters: file, format, and records. π«Ά
- If
fileis specified, the output goes straight into that file. Important: you gotta specify with>and>>whether you're creating the file or appending to it! Do not skip this part, fr fr. π - If no
fileis specified, then the results of the output get returned to a variable instead (both variations are shown below π).
The MARC standard includes a control field (005) that records the date of last automatic processing. This gets implemented as a side-effect of output() whenever you write to a file, or if you explicitly request it via an add_005s field of the template. The current time gets stamped on the records the template indicates. β°
Valid format values currently include usmarc, marcmaker, ascii, html, urls, and isbd. The optional records parameter lets you pass an array of record numbers you wanna output. You must pass that array as a reference, hence that slash in \@records below. If you don't include records, the output defaults to every record in the object. π
The lineterm parameter can override the line-ending default for any of the formats. MARCMaker defaults to CRLF (the format was originally released for MS-DOS), while the others use '\n' as the default.
With the MARCMaker format, a charset parameter accepts a hash-reference to a user supplied character translation table. The "ustext.txt" table supplied with the LoC. MARCBreaker utility is used internally as the default. You can use the ustext_default method to grab a hash-reference to it if you only wanna modify a couple of characters. Example's down below π
The MARCMaker Specification requires that long lines be split to less than 80 columns. That behavior is the default, but the nolinebreak parameter can override it β and then the output ends up looking a whole lot like the ascii format.
MARC::Rec::output($template) is the same as output except that it ignores record number(s) and only outputs its caller. (E.g., with $format eq 'urls' it does not output html header and footer information.)
π« MARC
$x->output({file=>">mymarc.dat",'format'=>"usmarc"});
$x->output({file=>">mymarc.dat",'format'=>"usmarc",
records=>\@records});
$y=$x->output({'format'=>"usmarc"}); #put the output into $y
π« MARCMaker
$x->output({file=>">mymarcmaker.mkr",'format'=>"marcmaker"});
$x->output({file=>">mymarcmaker.mkr",'format'=>"marcmaker",
records=>\@records});
$y=$x->output({'format'=>"marcmaker"}); #put the output into $y
$x->output({file=>"brkrtest.mkr",'format'=>"marcmaker",
nolinebreak=>"1", lineterm=>"\n",
charset=>\%char_hash});
π« ASCII
$x->output({file=>">myascii.txt",'format'=>"ascii"});
$x->output({file=>">myascii.txt",'format'=>"ascii",
records=>\@records});
$y=$x->output({'format'=>"ascii"}); #put the output into $y
π« HTML
The HTML output method has some extra parameters of its own.
fields, when set to "all", will output all of the fields. Or you can pass the tag number along with a label you want used for that tag. That gives you HTML output containing only the specified tags, using your label in place of the MARC code. π·οΈ
$x->output({file=>">myhtml.html",'format'=>"html",
fields=>"all"});
#this will only output the 100 and 245 fields, with the
#labels "Title: " and "Author: "
$x->output({file=>">myhtml.html",'format'=>"html",
245=>"Title: ",100=>"Author: "});
$y=$x->output({'format'=>"html"});
If you wanna build that HTML file in stages, there are four other format values available to you:
"html_header""html_start""html_body""html_footer"
Careful to use the >> append when adding to a file though! π
$x->output({file=>">myhtml.html",
'format'=>"html_header"}); # Content-type
$x->output({file=>">>myhtml.html",
'format'=>"html_start"}); # <BODY>
$x->output({file=>">>myhtml.html",
'format'=>"html_body",fields=>"all"});
$x->output({file=>">>myhtml.html",
'format'=>"html_footer"});
π« URLS
$x->output({file=>"urls.html",'format'=>"urls"});
$y=$x->output({'format'=>"urls"});
π« ISBD
An experimental output format that attempts to mimic the ISBD. π§ͺ
$x->output({file=>"isbd.txt",'format'=>"isbd"});
$y=$x->output({'format'=>"isbd"});
π« XML
Roundtrip conversion between MARC and XML is handled by the subclass MARC::XML. MARC::XML is available for download from the CPAN. β
π¦ usmarc_default()
This method returns a hash reference to a translation table between mnemonics delimited by curly braces and single-byte character codes in the MARC record. Multi-byte characters are not currently supported. The hash has keys of the form '{esc}' and values of the form chr(0x1b). It's used during MARCMaker input. πΈ
my %inc = %{$x->usmarc_default()};
printf "dollar = %s\n", $inc{'dollar'}; # prints '$'
$inc{'yen'} = 'Y';
$x->openmarc({file=>"makrbrkr.mrc",'format'=>"marcmaker",
charset=>\%inc});
MARC::Rec::usmarc_default is identical to usmarc_default;
π§ ustext_default()
This method returns a hash reference to a translation table between single-byte character codes and mnemonics delimited by curly braces. Again, multi-byte characters are not currently supported. The hash has keys of the form chr(0x1b) and values of the form '{esc}'. This one's used during MARCMaker output. β¨
my %outc = %{$x->ustext_default()};
printf "dollar = %s\n", $outc{'$'}; # prints '{dollar}'
$outc{'$'} = '{uscash}';
printf "dollar = %s\n", $outc{'$'}; # prints '{uscash}'
$y = $x->output({'format'=>"marcmaker", charset=>\%outc});
MARC::Rec::ustext_default is identical to ustext_default;
π as_string()
as_string() takes no parameters and returns a (Unix) newline separated version of the record. π«Ά
Format is: $tag<SPACE>$i1$i2<SPACE>$subfields
where $subfields are separated by "\c_" binary subfield indicators.
Tag 000 is ldr.
Subclasses may need to override this format. If that's the case, they should override from_string. π
π from_string()
from_string() takes a string parameter and updates the calling record's {array} information. It assumes the string is formatted like the output of as_string(). Simple as that, bestie. π
β¨ EXAMPLES β¨
okay bestie, here's where it gets fun β a few examples to fire your imagination π₯
- This one reads the complete contents of a MARC file called
mymarc.datand then spits it back out as a MARCMaker file calledmymkr.mkr. iconic behavior.
#!/usr/bin/perl
use MARC;
$x = MARC->new("mymarc.dat","marcmaker");
$x->output({file=>"mymkr.mkr",'format'=>"marcmaker");
- lowkey important tea β: the MARC object occupies a fair number of working memory, and you may want to do conversions on very large files. In that case you're gonna want
openmarc(),nextmarc(),deletemarc(), andclosemarc()to read in portions of the MARC file, do something with the record(s), remove them from the object, and then read in the next record(s). This example reads in one record at a time from a MARC file calledmymarc.datand converts it to a MARC Maker file calledmyfile.mkr.
#!/usr/bin/perl
use MARC;
$x = new MARC;
$x->openmarc({file=>"mymarc.dat",'format'=>"usmarc"});
while ($x->nextmarc(1)) {
$x->output({file=>">>myfile.mkr",'format'=>"marcmaker"});
$x->deletemarc(); #empty the object for reading in another
}
- picture this π: you have a tab delimited text file of data for online journals you have access to from Dow Jones Interactive, and you wanna create a batch of MARC records to load into your catalog. bestie, this is so your moment. You can use
createrecord(),addfield()andoutput()to create records as you read in your delimited file. When you're done, you then output to a file in USMARC. slay.
#!/usr/bin/perl
use MARC;
$x = new MARC;
open (INPUT_FILE, "delimited_file");
while ($line=<INPUT_FILE>) {
($journaltitle,$issn) = split /\t/,$line;
$num=$x->createrecord();
$x->addfield({record=>$num,
field=>"022",
i1=>" ", i2=>" ",
value=>$issn});
$x->addfield({record=>$num,
field=>"245",
i1=>"0", i2=>" ",
value=>[a=>$journaltitle]});
$x->addfield({record=>$num,
field=>"260",
i1=>" ", i2=>" ",
value=>[a=>"New York (N.Y.) :",
b=>"Dow Jones & Company"]});
$x->addfield({record=>$num,
field=>"710",
i1=>"2", i2=>" ",
value=>[a=>"Dow Jones Interactive."]});
$x->addfield({record=>$num,
field=>"856",
i1=>"4", i2=>" ",
value=>[u=>"http://www.djnr.com",
z=>"Connect"]});
}
close INPUT_FILE;
$x->output({file=>">dowjones.mrc",'format'=>"usmarc"})
- and likeβ¦ maybe you have periodicals coming in that you want to order by location and then title? MARC::Rec's get you out of some array indexing. π
#!/usr/bin//perl use MARC 1.03;
my @newmarcs=@$marc[1..$#$marc]; # array slice. my @sortmarcs =
sort by_loc_oclc @newmarcs; @marc[1..$#$marc] = @sortmarcs;
sub by_loc_title {
my ($aloc,$atitle) = loc_title($a);
my ($bloc,$btitle) = loc_title($b);
return $aloc cmp $bloc || $atitle cmp
$btitle; }
sub loc_title {
my ($rec)=@_;
my $n049 = $rec->getfirstvalue({field=>040});
my ($loc) = $n049=~/(ND\S+)/; # Or the first two letters of your
OCLC
# location.
my $title = $rec->getfirstvalue({field=>100,delimiter=>" "});
return ($loc,$title);
}
π¬ NOTES π¬
bestie, please let us know if you run into any difficulties using MARC.pm β we'd be happy to try to help π«Ά Also, please contact us if you notice any bugs, or if you would like to suggest an improvement/enhancement. Email addresses are listed at the bottom of this page.
Development of MARC.pm and other library oriented Perl utilities is conducted on the Perl4Lib listserv. Perl4Lib is an open list and is an ideal place to ask questions about MARC.pm. Subscription information is available at http://www.vims.edu/perl4lib π¬
Two global boolean variables are reserved for test and debugging π Both are "0" (off) by default.
- The
$TESTvariable disables internal error messages generated usingCarp. It also overrides thedate_stampin the"005"field with a constant"19960221075055.7". It should only be used in the automatic test suite. - The
$DEBUGvariable adds verbose diagnostic messages.
Since both variables are used only in testing, MARC::Rec uses $MARC::TEST and $MARC::DEBUG rather than define a second pair. no cap π
π AUTHORS π
- Chuck Bearden
cbearden@rice.edu - Bill Birthisel
wcbirthisel@alum.mit.edu - Derek Lane
dereklane@pobox.com - Charles McFadden
chuck@vims.edu - Ed Summers
ed@cheetahmail.com
the whole squad fr fr π
π SEE ALSO π
perl(1), http://lcweb.loc.gov/marc
π COPYRIGHT π
Copyright (C) 1999,2000, Bearden, Birthisel, Lane, McFadden, and Summers. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. 23 April 2000. Portions Copyright (C) 1999,2000, Duke University, Lane.
and that's the whole page bestie!! π this one shipped as perl v5.36.0, dated 2022-10-13, and it's MARC(3pm) β go forth and catalog, you absolute icon π¦β¨
π¦ where this page comes from
- ubuntu noble: libmarc-perl 1.07-8 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libmarc-perl 1.07-8 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libmarc-perl 1.07-8 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms