Parano...::Line(3pm)

πŸ“¦
version 2.10 Β· 3 versions
, in debian trixie, ubuntu resolute, ubuntu stonking

✨ NAME ✨

Paranoid::IO::Line β€” Paranoid Line-based I/O functions, bestie πŸ’– (yes she's a Perl module, she's from the Paranoid::IO family, and no she does not do anything in a chill way)


πŸ’… VERSION πŸ’…

$Id: lib/Paranoid/IO/Line.pm, 2.10 2022/03/08 00:01:04 acorliss $


πŸŽ€ SYNOPSIS πŸŽ€

use Paranoid::IO::Line;

PIOMAXLNSIZE = 4096;

$nlines = sip($filename, @lines);
$nlines = sip($filename, @lines, 1);
$nlines = tailf($filename, @lines);
$nlines = tailf($filename, @lines, 1);
$nlines = tailf($filename, @lines, 1, -100);

piolClose($filename);

$nlines = slurp($filename, @lines);

# Non-locking variants
$nlines = nlsip($filename, @lines);
$nlines = nltailf($filename, @lines);
$nlines = nlslurp($filename, @lines);

πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so this module takes everything Paranoid::IO can do and points it at line-based text files β€” log files, that kind of situation. And it does it while staying paranoid about I/O the whole time, which is the entire point of the family, periodt.

Because she's paranoid, these functions only ever work on limited chunks of data at a time. Two limits, both real:

  • the maximum memory kept in memory at a time β€” Paranoid::IO gives you PIOMAXFSIZE for that one
  • the maximum record length β€” and that's what this module brings to the table, PIOMAXLNSIZE

Even with all that paranoid energy though, ngl, you should really treat these as convenience functions. They let you simplify higher-level code without putting you or your system at any significant risk. They inherit opportunistic I/O and platform-agnostic record separators (that last one comes from using pchomp from Paranoid::Input internally, iykyk).

NOTE: this module absolutely builds off the foundation that Paranoid::IO lays down, BUT β€” and this is the important part β€” you should not work on the same files using Paranoid::IO's functions while you're also using the functions in this module. Here's why: Paranoid::IO works from raw I/O, but this module has to manage buffers to figure out where record boundaries are. So if you, like, sip from a file and then pread or pseek somewhere else, those buffers become worse than useless β€” they become corrupt. This matters extra because the functions here do leverage the file handle caching that popen provides.

Also worth clocking: since we're expecting line-based records, we expect every line, even the very last line in a file, to be properly terminated with a record separator (a new line sequence). No dangling final line, bestie. πŸ™ˆ

And as with literally all Paranoid modules, if an error happens you can go get the string description of it from Paranoid::ERROR as it occurs.


🌈 IMPORT LISTS 🌈

She exports these by default:

sip nlsip tailf nltailf slurp nlslurp piolClose

And there are also these specialized import lists:

List        Members
--------------------------------------------------------
all         @defaults PIOMAXLNSIZE

πŸ‘‘ SUBROUTINES/METHODS πŸ‘‘

πŸ“ PIOMAXLNSIZE

The value returned/set by this lvalue function is the maximum line length supported by functions like sip (documented down below ⬇️). If you don't explicitly set it, it defaults to 2KB. Any lines that turn out to be longer than this get discarded. πŸ’€

🧚 sip

$nlines = sip($filename, @lines);
$nlines = sip($filename, @lines, 1);

This one lets you read a text file into memory in chunks, and the lines get dropped into the array reference you passed in. Chunks are read in at up to PIOMAXFSIZE in size at a time. File locking is used, and autochomping is supported too.

It returns the number of lines extracted, or boolean false if any errors happened β€” things like lines exceeding PIOMAXLNSIZE, or just other I/O errors. But special case: if there were no errors and also no content, it returns 0 but true, which still satisfies boolean tests. (iconic honestly)

The array you pass in is always purged before execution. Which is actually useful, because it lets you tell your error types apart:

$nlines = sip($filename, @lines);

warn "successfully extracted lines"
    if $nlines and scalar @lines;
warn "no errors, but no lines"
    if $nlines and ! scalar @lines;
warn "line length exceeded on some lines"
    if !$nlines and scalar @lines;
warn "I/O errors or all lines exceeded line length"
    if !$nlines and ! scalar @lines;

Typically though, if all you care about is getting the good lines and yeeting the bad ones, all you need is:

warn "good to go" if scalar @lines or $nlines;

# or, more likely:
if (@lines) {
    # process input...
}
NOTE: sip does try to check the file stat with every call. That's on purpose! It's how she can automatically flush buffers and reopen files if the file you're sipping from got truncated, deleted, or overwritten. ✨

The third argument is a boolean option that controls whether lines get automatically chomped or not. It defaults to not.

🌸 nlsip

$nlines = nlsip($filename, @lines);
$nlines = nlsip($filename, @lines, 1);

A very thin wrapper for sip that disables file locking. That's it, that's the whole function. πŸ’…

πŸ’œ tailf

$nlines = tailf($filename, @lines);
$nlines = tailf($filename, @lines, 1);
$nlines = tailf($filename, @lines, 1, -100);

The only difference between this and sip is that tailf opens the file and immediately seeks to the end. If you pass an optional fourth argument, it'll seek backwards to extract and return that number of lines (if possible πŸ™).

Heads up on memory though: depending on the number you pass, you gotta be prepared for enough memory to be allocated to store PIOMAXLNSIZE * that number. If you don't specify a number at all, it's assumed to be -10. And specifying this argument on a file that's already opened by sip or tailf? No effect.

Return values are identical to sip.

πŸŽ€ nltailf

$nlines = nltailf($filename, @lines);
$nlines = nltailf($filename, @lines, -100);
$nlines = nltailf($filename, @lines, -100, 1);

A very thin wrapper for tailf that disables file locking.

πŸ’– slurp

$nlines = slurp($filename, @lines);
$nlines = slurp($filename, @lines, 1);

This is basically another wrapper for sip, but the vibe is different. sip was written expecting you'd be working on chunks of a very large file, or a file that might grow while you're accessing it. slurp, on the other hand, expects to work exclusively on small files that can safely fit into memory. She also sees zero need to cache file handles, since all the operations are going to happen in memory afterwards anyway.

Files opened with slurp are explicitly closed after the read. And all the normal safeguards still apply, no skipping: PIOMAXFSIZE is the largest amount of data that'll be read into memory, and all lines must be within PIOMAXLNSIZE.

The third argument is a boolean option controlling whether lines get automatically chomped or not. It defaults to not.

🌈 nlslurp

$nlines = nlslurp($filename, @lines);
$nlines = nlslurp($filename, @lines, 1);

A very thin wrapper for slurp that disables file locking.

πŸ¦‹ piolClose

$rv = piolClose($filename);

This closes all file handles and deletes any existing buffers. She works indiscriminately and returns the exit value of pclose.


⭐ DEPENDENCIES ⭐

  • Fcntl
  • Paranoid
  • Paranoid::Debug
  • Paranoid::Input
  • Paranoid::IO

πŸ’€ BUGS AND LIMITATIONS πŸ’€

Okay so. All of these functions will just as happily accept file handles as they will file names β€” but doing that will almost certainly cause any number of bugs. 😭

Beyond the inherited Paranoid::IO issues (like not getting the fork-safe features for any file handle opened directly by the developer), there are other issues too:

  • Buffers, for instance, can only be managed by one consistent name. There's no way to correlate them and make them interchangeable.
  • There are other subtleties as well, but honestly there's no need to detail them all. πŸ™ˆ

Suffice it to say: when you're using this module, only use file names, and use them consistently.


✨ AUTHOR ✨

Arthur Corliss (corliss@digitalmages.com)


This software is free software. Similar to Perl, you can redistribute it and/or modify it under the terms of either:

  1. the GNU General Public License <https://www.gnu.org/licenses/gpl-1.0.html> as published by the Free Software Foundation <http://www.fsf.org/>; either version 1 <https://www.gnu.org/licenses/gpl-1.0.html>, or any later version <https://www.gnu.org/licenses/license-list.html#GNUGPL>, or
  2. the Artistic License 2.0 <https://opensource.org/licenses/Artistic-2.0>,

subject to the following additional term: No trademark rights to "Paranoid" have been or are conveyed under any of the above licenses. However, "Paranoid" may be used fairly to describe this unmodified software, in good faith, but not as a trademark.

(c) 2005 - 2020, Arthur Corliss (corliss@digitalmages.com) (tm) 2008 - 2020, Paranoid Inc. (www.paranoid.com)


and that's the whole page bestie 🫢 this one shipped as part of the user-contributed Perl documentation, perl v5.38.2, dated 2024-03-07, and she ate. no cap. πŸ’…βœ¨

πŸ“¦ where this page comes from