RE: Opening & writing to UTF-8 files; copyright symbol again -- solution
[email protected] ("PHILLIPS M.E.") Mon, 16 Nov 2015 11:01:18 +0000
| Newsgroups | perl.perl4lib |
|---|---|
| Message-ID | <DE5A87935C81284782298B0D297D678422447234@CISCMRMBS02.mds.ad.dur.ac.uk> |
> However, combining Jon Gorman's recommendation with some Googling, I get: >=20 > my $outfile=3D'4788022.edited.bib'; > open (my $output_marc, '>', $outfile) or die "Couldn't open file $!" ; > binmode($output_marc, ':utf8'); >=20 > The open statement may not be quite correct, as I am not familiar with th= e > more current techniques for opening file handles that John mentioned. > However, when I use those instructions to open the output file rather tha= n what > I had before, the copyright symbol does indeed come across as C2 A9 as it= was > in the original record. I didn't want to use the utf8, because I've tried= that > before and ended up with double-encoding (and a real mess). But I'll cont= inue > testing. I think I understand how your original problem came about, but I may not be= able to explain it! It is important to understand that inside Perl a stri= ng can be encoded in one of two ways: 1) stored in UTF-8, in which case all ASCII-range characters (roughly space= , A-Z, a-z, 0-9 and most of the punctuation you see on a keyboard) will be = stored in a single byte per character, and other characters will be stored = in 2, 3, or 4 bytes 2) stored in an eight-bit character set such as ISO Latin 1. In this situat= ion all characters are stored as a single byte, but non-western European ch= aracters will be unavailable. Perl tries to store strings in the second form by preference, as it saves m= emory and processing time, but it does this in a way which is transparent t= o the user, so if you have the string "abc" it will be in the second form. = If you append a copyright symbol it will still be in the second form as th= at symbol is present in ISO Latin 1, but if you append a w-circumflex (as u= sed in Welsh, and not available in ISO Latin 1) or any Chinese, Greek, Cyri= llic character, then the string will be re-encoded in UTF-8 and Perl will f= lag it to remember that is how it has been stored. You as a user do not (g= enerally) need to worry. The complication is what to do when reading stuff from files or writing the= m out again, because then Perl has to decide how to represent stuff for the= outside world. To be successful, you have to tell Perl what encoding is u= sed for anything you are reading in, so that it can be stored appropriately= . If you read in a copyright symbol from a UTF-8 encoded file but fail to = tell Perl it was in UTF-8, Perl will think it is character C2 followed by A= 9. Now A9 happens to be the copyright symbol in ISO Latin 1, but C2 is A-c= ircumflex. If you write it out again, Perl will operate in ISO Latin 1 unl= ess instructed otherwise, and you will get C2 A9 in the file, which is prob= ably fine, but Perl did not know that it was meant to be a single character= so processing you might have done, like regular expression matches and fin= ding the length of the string, would not have worked as expected. In your case, if the input was MARC records encoded in UTF-8, the Perl MARC= modules will have picked this up and will correctly flag all the data as U= TF-8. But Perl is then at liberty to store it in memory as ISO Latin 1 to s= ave space. When you use the as_usmarc() function the MARC::File::USMARC.pm= module will build a single string containing the whole record, but as far = as I can tell from the source code, it does not do anything special about t= he character set. If the record had UTF-8 encoding when read in, the as_usm= arc() value will be flagged as being in UTF-8. If you have not specified U= TF-8 during the open command or via binmode, then when writing the string t= o the file it would be converted to your local 8-bit encoding (e.g. ISO-Lat= in-1). This would result in a record which is a bit of a mess, to say the = least, because the LDR will indicate Unicode and the content may not be. Y= ou might also get the warning "wide character in print" if any characters o= utside ISO Latin 1 were included, but a copyright symbol would silently be = converted to the wrong representation. Any record in MARC8, however, will be read in as such and will not be mucke= d about with by Perl: it will assume it is all in the local 8-bit encoding,= and to output it successfully you should avoid opening the output file wit= h UTF-8 encoding. In summary: 1. If reading UTF-8 encoded records via the MARC modules, make sure any fil= e you write is opened with '>:encoding(UTF-8)' 2. If handling records encoded in MARC8, use '>:raw' when outputting. 3. Do not use '>:raw' with UTF-8 encoded records as any characters in the r= ange U+0080 to U+00FF are at risk of being mangled because Perl's internal = encoding of the string may not be what you expect, being dependent on wheth= er characters from U+0100 upwards are included. It *is* possible to read and write records in a mixture of encodings, but y= ou will need to keep your head!! If you are modifying records you need to = ensure any additional text you introduce is supplied in the appropriate enc= oding as the MARC modules are not clever enough to handle automatically cre= ating the Field objects in an encoding that matches the Record's encoding. It might be argued that all MARC files should be read and written with ":ra= w", in which case the as_usmarc function would need to be modified so that = if the record has Unicode encoding it gets converted into a byte-stream of = UTF-8 before being returned. But there are so many other design decisions = that could be questioned, around whether it is more helpful to be like Perl= and make the actual encoding transparent, or whether users should be made = to grapple with the MARC-8 versus UTF-8 issues for their own good! As they= stand, the MARC modules allow you to do what you need to do, without hidin= g the complications. =20 Matthew