Using the Record Separator

I use Perl for years, but there are some details that I still am unable to use correctly in Perl. I think they should be my fault. And probably, if I rtfm I would get an answer. Nevertheless, and although this is not perlmonks, I'll post my problem anyway.

I use local $/ = "\n\n" as record separator to read a file. The code that processes that file, uses a module, that decides to open a config file, and that expects that the record separator is a single new line. That library fails.

My main doubt is if all modules shout set the record separator explicitly, as we never know if someone changed it, or if there is any way I can change my own copy of it without messing with other modules.


Without seeing your code I can't be certain that this is the answer to your problem. However, it seems likely that it is.

local $var essentially hides a variable with another variable that has the same name. That original variable is still there just not visible in the scope where local was called, which includes functions and methods called from within that scope, so in

no strict 'vars';

sub do_that_thing { print $myvar, "\n"; }

$myvar = 'alice';
local $myvar = 'bob';

do_that_thing( );

local completely hides the original assignment and do_that_thing will see the localized variable.

As long as the localization happens in a sub or block, then the previous version becomes visible on exiting the sub or block:

$myvar = "alice";

local $myvar = "bob";
print "$myvar\n"; # bob

print "$myvar\n"; # alice

So it is safe (and good) to have modules localize $/ within the function or method in which it is used, but not outside of a function or method.

If you don't control the module, you can also write your code so that the localization of $/ happens after the config reading code or in its own block, or if the config reading and input file reading are intermingled, then create a sub to do the input file reading and only localize $/ in that sub.

How about doing something like this?

# initialize variables
local $/ = "\n\n";
# read file here
# process the data here

Oh, eh1mnwy already said the same thing. I guess I left the window open longer than I realized before replying. :)

I think I see your point. The module in question is simply assuming that $/ is set to its default value, and breaking when it's not.

It is a shame that per-filehandle record separators are not supported. (IO::Handle allows you to control quite a lot of stuff on a per-filehandle basis, such as autoflush, but not the record separator.) It is a shame that $/ cannot be lexicalised (like $_ can).

Ultimately the lesson from this is that whenever you want to read from a file, if you care about the record separators at all, then you need to do local $/ = ..., even if you just want the default separator, because you never know what your caller has done.

Clearly this module you're using is not doing that. It should. File a bug report.

Leave a comment

About Alberto Simões

user-pic I blog about Perl. D'uh!