Основы
Hello World
use strict/warnings — лучшая практика
#!/usr/bin/perl
use strict;
use warnings;
print "Hello, World!\n";
# variables: scalar ($), array (@), hash (%)
my $name = "Alice";
my @nums = (1, 2, 3);
my %ages = (Alice => 30, Bob => 25);
print "Name: $name\n";Комментарии
=pod =cut для многострочных комментариев
#!/usr/bin/perl
# This is a single-line comment
print "Hello\n"; # inline comment
=pod
This is a multi-line
comment using POD (Plain Old Documentation)
=cut
print "After POD\n";Запуск скрипта
Параметры командной строки perl
my $str = "Hello";
my $num = 42;
my $float = 3.14;
my $bool = 1; # true (0, "", "0", undef are false)
# String repetition
my $line = "-" x 20;
# String concatenation
my $greeting = "Hello, " . $str;
print "$greeting\n"; # Hello, HelloФункция say
say автоматически добавляет перенос строки
my $name = "Alice";
my $age = 30;
print "Name: $name\n"; # interpolates
print 'Name: $name\n'; # literal (no interpolation)
# Disambiguate variable name with braces
my $fruit = "apple";
print "I have many ${fruit}s.\n"; # I have many apples.
# Array interpolation (joins with $")
my @arr = (1, 2, 3);
print "Array: @arr\n"; # Array: 1 2 3shebang
env perl более переносимо
use strict;
use warnings;
# Without strict, this would create a global:
# $foo = 42; # ERROR under strict
my $foo = 42; # OK with my
# Warnings catch common mistakes
my $undef_var;
# print $undef_var; # warns: uninitialized value
# Modern bundle (v5.36+ enables strict, warnings, features)
use v5.36;
# Disable specific warnings locally
no warnings 'uninitialized';say & features
say is like print but appends a newline. Enable features with 'use feature' or a version bundle like 'use v5.36'. Signatures give named parameters instead of unpacking @_.
use v5.10;
use feature 'say';
say "Hello!"; # adds newline automatically
say "No need for \n";
# Equivalent to:
print "Hello!\n";
# Signatures (v5.20+, not default)
use feature 'signatures';
no warnings 'experimental::signatures';
sub greet ($name) {
say "Hi, $name";
}Скаляры
Скалярные переменные
Префикс $ обозначает скаляр
my $sum = 5 + 3; # 8
my $diff = 10 - 4; # 6
my $prod = 6 * 7; # 42
my $div = 10 / 3; # 3.3333...
my $int = int(10 / 3); # 3
my $mod = 10 % 3; # 1
my $pow = 2 ** 10; # 1024
# Augmented assignment
my $x = 5;
$x += 3; # $x = 8
$x **= 2; # $x = 64
$x++; # $x = 65
$x--; # $x = 64Строковые операции
Частые строковые функции
my $a = "Hello";
my $b = "World";
# Concatenation
my $combined = $a . ", " . $b; # Hello, World
# Repetition
my $line = "=" x 30;
# Augmented concatenation
my $str = "abc";
$str .= "def"; # abcdef
# Numeric vs string comparison
print "match" if "abc" eq "abc"; # string equal
print "match" if 123 == 123; # numeric equalИнтерполяция строк
Двойные кавычки интерполируют, одинарные — нет
my $x; # undef
print "undef\n" if !defined $x;
$x = undef; # explicitly undef
undef $x; # another way
# Defined-or operator
my $val = $x // "default";
# Warning if used as string:
# print $x; # Use of uninitialized value
if (defined $val) {
print "Defined: $val\n";
}Числовые операции
Числовые функции
my $name = "Alice";
# q() = single quote (no interpolation)
my $s1 = q(Hello $name); # Hello $name
# qq() = double quote (interpolation)
my $s2 = qq(Hello $name); # Hello Alice
# qw() = quoted words (produces a list)
my @words = qw(apple banana cherry);
# equivalent to ("apple", "banana", "cherry")
# Custom delimiters
my $s3 = q{don't worry};
my $s4 = q!bang!;undef
undef означает неопределённое значение
my $name = "Alice";
# Single-quote style (no interpolation)
my $text = <<'END';
Hello $name,
This is literal.
END
# Double-quote style (interpolation)
my $msg = <<"EOF";
Hello $name,
Interpolated here.
EOF
print $msg;
# Indented heredoc (v5.26+)
my $code = <<~EOT;
line 1
line 2
EOTBoolean Truthiness
Only undef, 0, '', and '0' are false; even '00' and '0.0' are true! Use || for truthy defaults and // for defined defaults. References (even empty arrays) are always true.
# False values: undef, 0, "", "0"
# Everything else is true
print "true\n" if 1; # true
print "true\n" if "hello"; # true
print "true\n" if "0"; # NOT printed (false!)
print "true\n" if ""; # NOT printed (false)
print "true\n" if []; # true (reference is true)
# Logical operators
my $x = 0 || "default"; # default (|| tests truthiness)
my $y = 0 // "default"; # default (// tests definedness)
my $z = "a" && "b"; # b (returns last evaluated)Массивы
Операции с массивами
Префикс @ для массивов, $ для доступа к элементам
my @nums = (1, 2, 3, 4, 5);
my @strs = qw(a b c);
my @mixed = (1, "two", 3.14);
# Access by index (0-based)
print $nums[0]; # 1
print $nums[-1]; # 5 (last element)
# Modify an element
$nums[0] = 100;
# Range operator
my @range = (1 .. 10); # 1 to 10
# Length in scalar context
my $count = scalar @nums; # 5
my $count2 = @nums; # 5Функции массивов
push/pop/unshift/shift
my @stack = (1, 2, 3);
# push/pop operate on the END
push @stack, 4, 5; # (1, 2, 3, 4, 5)
my $last = pop @stack; # 5
# unshift/shift operate on the START
unshift @stack, 0; # (0, 1, 2, 3, 4)
my $first = shift @stack; # 0
# @stack is now (1, 2, 3, 4)
print "@stack\n";
# splice is the general-purpose tool
splice(@stack, 1, 0, 'x'); # insert at index 1Срезы массивов
@ срез возвращает список
my @nums = (10, 20, 30, 40, 50);
# Array slice (returns a list)
my @slice = @nums[1, 3]; # (20, 40)
my @range = @nums[0 .. 2]; # (10, 20, 30)
# Modify multiple elements via slice
@nums[1, 3] = (200, 400);
# Last index
my $last_idx = $#nums; # 4
# Negative indices count from the end
print $nums[-1]; # 50
print $nums[-2]; # 400Сортировка
<=> числовое сравнение, cmp строковое сравнение
my @arr = (1, 2, 3, 4, 5);
# splice ARRAY, OFFSET, LENGTH, LIST
my @removed = splice(@arr, 1, 2); # removes (2, 3)
# @arr is now (1, 4, 5)
# Insert without removing (LENGTH = 0)
splice(@arr, 1, 0, 'a', 'b'); # (1, 'a', 'b', 4, 5)
# Replace elements
splice(@arr, 0, 2, 'x'); # replace first 2 with 'x'
# Remove everything from offset to end
my @rest = splice(@arr, 2);map и grep
map преобразует, grep фильтрует
my @nums = (3, 1, 4, 1, 5, 9);
# Default sort is lexicographic (string)
my @s1 = sort @nums;
# Numeric ascending
my @s2 = sort { $a <=> $b } @nums;
# Numeric descending
my @desc = sort { $b <=> $a } @nums;
# String sort
my @words = qw(banana apple Cherry);
my @alpha = sort { lc($a) cmp lc($b) } @words;
# Schwartzian transform (sort by computed key)
my @sorted = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [$_, length($_)] } @words;Array length & scalar context
An array's behavior depends on context: scalar context gives length, list/string context gives elements joined by $" (default space). Use scalar() to force scalar context when needed.
my @arr = (1, 2, 3, 4, 5);
# Scalar context returns length
my $len = @arr; # 5
print "Length: $len\n";
# String context joins with $"
print "Array: @arr\n"; # 1 2 3 4 5
# Change separator
local $" = ", ";
print "@arr\n"; # 1, 2, 3, 4, 5
# Last index
print "Last index: $#arr\n"; # 4
# Force scalar context
print scalar(@arr); # 5Хеши
Операции с хешами
Префикс % обозначает хеш
my %ages = (Alice => 30, Bob => 25, Carol => 35);
# Fat comma => auto-quotes the left side
my %h2 = (name => 'Alice', age => 30);
# Same as list of pairs:
my %h3 = ('name', 'Alice', 'age', 30);
# Access / modify a value
print $ages{Alice}; # 30
$ages{Dave} = 40; # add
$ages{Alice} = 31; # modify
# Delete a key
delete $ages{Bob};Итерация хешей
each/keys/values
my %h = (a => 1, b => 2, c => 3);
# Iterate keys
for my $k (keys %h) {
print "$k => $h{$k}\n";
}
# Get all values
my @vals = values %h;
# each() returns key/value pairs iteratively
while (my ($k, $v) = each %h) {
print "$k = $v\n";
}
# Sorted keys
for my $k (sort keys %h) { ... }Функции хешей
exists/delete/keys/values
my %h = (name => 'Alice', age => 30);
# exists checks if a key is present
if (exists $h{name}) {
print "Name exists\n";
}
# defined checks if the value is defined
if (defined $h{age}) { ... }
# delete removes a key, returns its value
my $removed = delete $h{age};
# Delete multiple keys (slice)
delete @h{qw(name age)};
# Empty a hash
%h = ();Срезы хешей
@ срез хеша
my %h = (a => 1, b => 2, c => 3);
# Hash slice (returns list of values)
my @vals = @h{qw(a b)}; # (1, 2)
# Modify slice
@h{qw(a b)} = (10, 20);
# Key/value slice (v5.20+)
my @pairs = %h{qw(a b)}; # (a, 10, b, 20)
# Index/value slice
my @iv = %h{qw(a b)};
# Check multiple keys
my @present = grep { exists $h{$_} } qw(a x b);Multidimensional Hashes
Perl hashes hold scalar values, so nested structures use hash/array references. Auto-vivification automatically creates intermediate references when you assign to a deep key, which is convenient but can hide bugs.
my %people = (
alice => { age => 30, city => 'NYC' },
bob => { age => 25, city => 'LA' },
);
# Access nested values
print $people{alice}{age}; # 30
print $people{bob}{city}; # LA
# Add new nested entry
$people{carol}{age} = 35;
$people{carol}{city} = 'SF';
# Auto-vivification creates intermediate refs
$people{dave}{hobbies}[0] = 'reading';
# %people{dave} created automaticallyCommon Hash Operations
scalar keys %h gives the count. reverse %h swaps keys/values (lossy if values aren't unique). Merging with (%a, %b) flattens then rebuilds. List::Util provides min/max over the values.
my %h = (a => 1, b => 2, c => 3);
# Number of keys
my $count = scalar keys %h;
# Reverse a hash (values become keys)
my %rev = reverse %h; # (1 => a, 2 => b, 3 => c)
# Merge hashes
my %merged = (%h, d => 4);
# Check if empty
print "empty\n" if !keys %h;
# Reset each() iterator
keys %h; # resets iterator (v5.18+)
# Get min/max value
use List::Util qw(min max);
my ($min, $max) = (min(values %h), max(values %h));Ссылки
Создание ссылок
\ создаёт ссылку, [] {} создают анонимные ссылки
my $score = 85;
if ($score >= 90) {
print "A\n";
} elsif ($score >= 80) {
print "B\n";
} elsif ($score >= 70) {
print "C\n";
} else {
print "F\n";
}
# Statement modifier form
print "Pass\n" if $score >= 60;
# Note: it is 'elsif', NOT 'elseif'Разыменование
Разыменование стрелкой ->
my $logged_in = 0;
unless ($logged_in) {
print "Please log in\n";
}
# Equivalent: if (!$logged_in) { ... }
# Statement modifier
print "Error\n" unless $ok;
# unless/else (often avoid for readability)
unless ($ok) {
print "fail\n";
} else {
print "pass\n";
}Вложенные структуры
Стрелку можно опустить между смежными индексами
# while loop
my $i = 0;
while ($i < 5) {
print "$i\n";
$i++;
}
# until = while NOT
my $done = 0;
until ($done) {
$done = check_something();
}
# do-while style (runs at least once)
my $n = 0;
do {
print $n++;
} while ($n < 3);
# Infinite loop
while (1) { last if $done; }Массив ссылок на массивы
Двумерный массив
# C-style for loop
for (my $i = 0; $i < 5; $i++) {
print "$i\n";
}
# foreach over a list (for and foreach are interchangeable)
foreach my $item (1, 2, 3) {
print "$item\n";
}
# Range operator
for my $n (1 .. 10) {
print "$n ";
}
# Uses default $_ variable
for (1 .. 5) {
print "$_\n";
}last/next/redo
last exits the loop (like break), next skips to the next iteration (like continue), redo restarts the current iteration without re-evaluating the condition. Labels let you control outer loops.
# last = break out of loop
for my $i (1 .. 10) {
last if $i == 5;
print "$i ";
}
# prints: 1 2 3 4
# next = skip to next iteration
for my $i (1 .. 5) {
next if $i % 2 == 0;
print "$i "; # 1 3 5
}
# redo = re-run current iteration without re-checking
my $count = 0;
while (<$fh>) {
$count++;
redo if $count < 3;
}
# Labeled loops for nested control
OUTER: for my $i (1..3) {
for my $j (1..3) {
last OUTER if $i == 2 && $j == 2;
}
}given/when (switch)
given/when provides switch/case semantics using smart matching. It's experimental, so disable the warning or prefer explicit if/elsif chains for production code. The match can compare against values, arrays, or regexes.
use feature 'switch';
no warnings 'experimental::smartmatch';
my $val = 2;
given ($val) {
when (1) { print "one\n" }
when (2) { print "two\n" }
when ([3, 4]) { print "three or four\n" }
when (/^\d+$/) { print "a number\n" }
default { print "something else\n" }
}
# given/when is experimental; many prefer if/elsif
if ($val == 1) { print "one\n" }
elsif ($val == 2) { print "two\n" }Управление потоком
if-elsif-else
elsif, а не elseif
my $str = "Hello, World";
# =~ binding operator applies a regex
if ($str =~ /World/) {
print "Matched\n";
}
# Negated match
if ($str !~ /foo/) {
print "No foo\n";
}
# Case-insensitive with /i
if ($str =~ /hello/i) {
print "Found hello\n";
}
# Implicit match against $_
for ("cat", "dog", "bat") {
print "$_\n" if /at/; # cat, bat
}unless
unless — отрицание if
my $str = "Hello, World";
# Replace first occurrence
$str =~ s/World/Perl/; # Hello, Perl
# Replace all (g = global)
my $text = "a-b-c";
$text =~ s/-/_/g; # a_b_c
# Case-insensitive replace
$str =~ s/hello/hi/i;
# Evaluate replacement as code (e)
my $s = "2+3";
$s =~ s/(\d+)\+(\d+)/$1 + $2/e; # 5
# tr/// transliteration (character mapping)
my $dna = "ACGT";
$dna =~ tr/ACGT/TGCA/; # TGCAwhile и until
until — отрицание while
my $str = "2024-01-15";
# Numbered capture groups
if ($str =~ /(\d{4})-(\d{2})-(\d{2})/) {
print "Year: $1\n"; # 2024
print "Month: $2\n"; # 01
print "Day: $3\n"; # 15
}
# Named captures (v5.10+)
if ($str =~ /(?<year>\d{4})-(?<month>\d{2})/) {
print $+{year}; # 2024
print $+{month}; # 01
}
# Capture all matches globally
my @nums = ($str =~ /(\d+)/g); # (2024, 01, 15)for и foreach
for/foreach взаимозаменяемы
# Quantifiers
/a/ # exactly one
/a?/ # 0 or 1
/a*/ # 0 or more
/a+/ # 1 or more
/a{3}/ # exactly 3
/a{2,4}/ # 2 to 4
/a{3,}/ # 3 or more
# Greedy vs non-greedy (lazy)
/a.*/ # greedy (matches as much as possible)
/a.*?/ # non-greedy (matches as little as possible)
# Anchors
/^start/ # beginning of string
/end$/ # end of string
/\bword\b/ # word boundary
/\A/ # absolute start of string
/\z/ # absolute end of stringlast и next
last=break, next=continue
# Predefined classes
/\d/ # digit [0-9]
/\D/ # non-digit
/\w/ # word char [a-zA-Z0-9_]
/\W/ # non-word char
/\s/ # whitespace
/\S/ # non-whitespace
/./ # any char except newline
# Custom classes
/[aeiou]/ # any vowel
/[^0-9]/ # NOT a digit
/[a-z]/i # case-insensitive range
# POSIX classes
/[[:alpha:]]/ # letters
/[[:digit:]]/ # digits
/[[:space:]]/ # whitespace
/[[:upper:]]/ # uppercasegiven-when
Аналог switch-case (экспериментально)
# Common modifiers
/abc/i # case-insensitive
/abc/g # global (all matches)
/abc/m # multiline (^/$ match line boundaries)
/abc/s # single-line (. matches newline)
/abc/x # extended (whitespace and comments)
# Extended mode for readability
my $re = qr{
^\d{4} # year
-
\d{2} # month
-
\d{2} # day
}x;
# qr// pre-compiles a regex
my $pattern = qr/\bword\b/i;
if ($str =~ $pattern) { ... }Подпрограммы
Определение подпрограмм
@_ — массив аргументов
my $s = "Hello, World";
# length
print length($s); # 12
# substr STRING, OFFSET, LENGTH, REPLACEMENT
print substr($s, 0, 5); # Hello
print substr($s, 7); # World
print substr($s, -5); # World (negative offset)
# 4-argument substr (replace in place)
my $str = "Hello, World";
substr($str, 0, 5) = "Hi"; # Hi, World
# Lvalue form modifies original
substr($s, 0, 1) = "J";Деструктуризация аргументов
my ($a, $b) = @_ разрушает аргументы
my $s = "Hello, World";
# index STRING, SUBSTR, POSITION
my $pos = index($s, "World"); # 7
my $pos2 = index($s, "o"); # 4 (first)
my $pos3 = index($s, "o", 5); # 8 (from position 5)
my $not = index($s, "xyz"); # -1 (not found)
# rindex searches from the end
my $last = rindex($s, "o"); # 8
# Count occurrences of 'l'
my $count = 0;
my $p = 0;
while (($p = index($s, "l", $p)) >= 0) {
$count++;
$p++;
}Вариативные аргументы
@_ содержит все аргументы
my $line = "Hello\n";
# chomp removes the line terminator (safe)
chomp $line; # $line = "Hello", returns 1
# chomp a whole array at once
my @lines = ("a\n", "b\n", "c\n");
chomp @lines; # removes \n from all
# chop removes the last character (any char, risky)
my $s = "abc";
chop $s; # "ab"
# chomp returns the count removed
my $cnt = chomp(my $x = "test\n"); # $cnt = 1
# chop returns the removed character
my $c = chop my $y = "xyz"; # $c = 'z'Возвращаемые значения
Можно вернуть несколько значений (список)
# split /PATTERN/, EXPR, LIMIT
my @parts = split /,/, "a,b,c"; # (a, b, c)
my @lim = split /,/, "a,b,c", 2; # (a, "b,c")
# Split on whitespace
my @words = split /\s+/, "one two three";
# Split into characters
my @chars = split //, "abc"; # (a, b, c)
# join SEPARATOR, LIST
my $csv = join(",", "a", "b", "c"); # a,b,c
my $str = join(" - ", @parts);
# Empty separator joins without delimiter
my $joined = join("", @chars); # abcСсылки на подпрограммы
Анонимная подпрограмма
my $s = "Hello World";
print lc($s); # hello world
print uc($s); # HELLO WORLD
print lcfirst($s); # hello World
print ucfirst($s); # Hello World (already capped)
# In-string case modifiers (\L \U \u \l \E)
my $name = "Alice";
print "\L$name\E"; # alice (\L starts, \E ends)
print "\U$name\E"; # ALICE
print "\u$name"; # Alice (capitalize first char)
print "\l$name"; # alice (lowercase first char)
# Title-case each word via regex
$s =~ s/(\w+)/\u\L$1/g; # Hello Worldsprintf & printf
printf prints formatted text; sprintf returns it. Format specifiers: %s string, %d integer, %f float, %x hex, %o octal, %b binary. Width and precision control padding: %5d, %05d, %.2f.
# printf FORMAT, LIST (prints)
printf "Name: %s, Age: %d\n", "Alice", 30;
printf "Pi: %.2f\n", 3.14159; # 3.14
printf "%5d\n", 42; # ' 42' (right-aligned)
printf "%-5d|\n", 42; # '42 |' (left-aligned)
printf "%05d\n", 42; # '00042' (zero-padded)
# sprintf returns the formatted string
my $date = sprintf("%04d-%02d-%02d", 2024, 1, 15);
# "2024-01-15"
# Common specifiers: %s %d %f %x %o %b %%
printf "Hex: %x, Oct: %o, Bin: %b\n", 255, 8, 5;Регулярные выражения
Базовое сопоставление
=~ оператор привязки
sub greet {
my $name = shift; # default: shifts from @_
print "Hello, $name!\n";
}
greet("Alice"); # Hello, Alice!
# Subs are global by default
sub add {
my ($a, $b) = @_; # unpack arguments
return $a + $b;
}
print add(2, 3); # 5
# Forward declaration (define later)
sub compute;
print compute(4);Захват
$1 $2 или $+{name}
sub sum {
my $total = 0;
$total += $_ for @_; # iterate all args
return $total;
}
print sum(1, 2, 3, 4); # 10
# Named arguments via hash
sub info {
my %args = @_;
print "Name: $args{name}\n";
print "Age: $args{age}\n";
}
info(name => "Alice", age => 30);
# Pass arrays/hashes by reference
sub total {
my $arr = shift;
my $sum = 0;
$sum += $_ for @$arr;
return $sum;
}Подстановка
s/// подстановка, tr/// транслитерация
# Single return value
sub square { return $_[0] ** 2; }
# Return a list
sub min_max {
my @nums = @_;
my ($min, $max) = ($nums[0], $nums[0]);
for (@nums) {
$min = $_ if $_ < $min;
$max = $_ if $_ > $max;
}
return ($min, $max);
}
my ($lo, $hi) = min_max(3, 1, 4, 1, 5);
# Implicit return (last expression)
sub name { "Alice" } # returns "Alice"
# Void context (no return needed)
sub log_msg { print "log: $_[0]\n"; }Кванторы регулярных выражений
Кванторы регулярных выражений
sub get_data {
if (wantarray) {
return (1, 2, 3); # list context
} elsif (defined wantarray) {
return "scalar"; # scalar context
} else {
return; # void context
}
}
my @list = get_data(); # (1, 2, 3)
my $scalar = get_data(); # "scalar"
get_data(); # void
# localtime is context-sensitive
my $t = localtime(); # scalar: "Mon Jan 1 12:00:00 2024"
my @t = localtime(); # list: (sec, min, hour, mday, ...)
# scalar() forces scalar context
print scalar(localtime());Классы символов
Предопределённые и пользовательские классы символов
# Anonymous sub (coderef)
my $greet = sub {
my $name = shift;
return "Hi, $name";
};
print $greet->("Alice"); # Hi, Alice (preferred)
print &$greet("Bob"); # older syntax
# Closure: captures outer variable
sub make_counter {
my $count = 0;
return sub { return ++$count; };
}
my $c = make_counter();
print $c->(); # 1
print $c->(); # 2 (count persists)
# Pass subs as arguments
sub apply { my ($f, $x) = @_; return $f->($x); }
print apply(sub { $_ * 2 }, 21); # 42Named Arguments & Defaults
Collect named args into a hash and apply defaults with //= (defined-or). You can mix positional and named args by putting the hash last. Returning a reference is common for option bundles.
sub create_user {
my %args = @_;
# Apply defaults with defined-or
$args{role} //= 'user';
$args{active} //= 1;
return \%args;
}
my $user = create_user(
name => 'Alice',
email => '[email protected]',
);
print $user->{role}; # user (default applied)
# Mix positional + named (named must come last)
sub point {
my ($x, $y, %opts) = @_;
print "($x, $y) color=$opts{color}\n";
}
point(1, 2, color => 'red');Строковые функции
Строковые операции
Преобразование регистра
# 3-argument open (recommended)
open(my $fh, '<', 'input.txt')
or die "Cannot open input.txt: $!";
# Read the whole file
my @lines = <$fh>;
close $fh;
# Modes: < read, > write (truncate), >> append,
# +< read/write, +> read/write (truncate)
open(my $out, '>', 'output.txt')
or die "Cannot write: $!";
open(my $app, '>>', 'log.txt')
or die "Cannot append: $!";Подстрока
substr можно использовать для замены
open(my $fh, '<', 'file.txt') or die $!;
# Line by line (memory efficient)
while (my $line = <$fh>) {
chomp $line;
print "Read: $line\n";
}
# Slurp entire file (undef the record separator)
local $/;
my $content = <$fh>;
# Read all lines into an array
my @lines = <$fh>;
close $fh;
# $. holds the current line number
open(my $f2, '<', 'file.txt') or die $!;
while (<$f2>) { print "$.: $_"; }Разделение и объединение
split/join
open(my $fh, '>', 'out.txt') or die $!;
print $fh "Hello, File!\n";
print $fh "Second line\n";
# Formatted write
printf $fh "%d: %s\n", 1, "first";
# Autoflush (no buffering)
use IO::Handle;
$fh->autoflush(1);
# Or via select
my $old = select($fh); $| = 1; select($old);
close $fh; # flushes and closesПоиск
index/rindex/sprintf
# <> reads from files in @ARGV, or STDIN if none
while (<>) {
print "$.: $_";
}
# Equivalent to: while (my $line = <ARGV>) { ... }
# Read all of stdin (or all arg files)
my @input = <>;
# Specify files programmatically
@ARGV = ('file1.txt', 'file2.txt');
while (<>) {
chomp;
print ">> $_\n";
}
# In-place editing
$^I = '.bak'; # backup extension
while (<>) { s/foo/bar/g; print; }chomp и chop
chomp удаляет перенос строки, chop удаляет последний символ
# Lexical filehandle (modern, recommended)
open(my $fh, '<', $file) or die $!;
# Bareword filehandle (legacy, avoid)
open(IN, '<', $file) or die $!;
my $line = <IN>;
close IN;
# Standard handles: STDIN, STDOUT, STDERR
print STDOUT "to stdout\n";
my $err = <STDIN>;
warn "to stderr\n"; # STDERR is unbuffered
# Duplicate a handle
open(my $log, '>&', STDERR) or die $!;
# Open a filehandle to a string (in-memory)
open(my $mem, '>', \my $buffer) or die $!;
print $mem "stored\n";File::Slurper
File::Slurper is the modern, efficient way to read/write whole files. It handles encodings and binmode correctly, unlike hand-rolled slurp code. Prefer it over do { local $/; <$fh> } in new code.
use File::Slurper qw(read_text write_text append_text read_lines);
# Read entire file as a string
my $text = read_text('file.txt');
# Write a string to a file
write_text('out.txt', "Hello\n");
# Append to a file
append_text('log.txt', "New entry\n");
# Read all lines (without newlines by default)
my @lines = read_lines('data.csv');
# Binary files
use File::Slurper qw(read_binary write_binary);
my $bytes = read_binary('image.png');
write_binary('copy.png', $bytes);Файловый ввод-вывод
Открытие файла
$! — системное сообщение об ошибке
opendir(my $dh, '.') or die "Cannot open dir: $!";
while (my $entry = readdir($dh)) {
next if $entry =~ /^\./; # skip dotfiles
print "$entry\n";
}
# Or get all entries at once
rewinddir($dh);
my @entries = readdir($dh);
closedir $dh;
# Filter to only subdirectories
opendir(my $d, '.') or die $!;
my @dirs = grep { -d $_ } readdir($d);
closedir $d;Запись в файл
> перезапись, >> добавление
# glob expands filename wildcards
my @files = glob('*.txt');
my @perl = glob('*.pl *.pm');
# Angle-bracket form (legacy)
my @same = <*.txt>;
# With a path
my @logs = glob('/var/log/*.log');
# Recursive globbing
use File::Glob qw(:bsd_glob);
my @all = bsd_glob('**/*.pm'); # needs :bsd_glob
# Capture flags: GLOB_ERR, GLOB_MARK (trailing / on dirs)
my @marked = bsd_glob('*', GLOB_MARK);Алмазный оператор
<> алмазный оператор
# Create a directory (default perms modified by umask)
mkdir 'newdir' or die $!;
mkdir 'newdir', 0755; # explicit permissions
# Create nested directories
use File::Path qw(make_path remove_tree);
make_path('a/b/c');
make_path('x/y', { mode => 0755 });
# Remove a directory (must be empty)
rmdir 'emptydir' or die $!;
# Remove recursively
remove_tree('a');
# Change working directory
chdir '/tmp' or die $!;
# Get current directory
use Cwd qw(getcwd);
print getcwd();Проверки файлов
Операторы проверки файлов
use File::Find;
my @found;
find(sub {
return if -d; # skip directories
push @found, $File::Find::name
if /\.pm\z/;
}, '.');
# finddepth visits directories after their contents
finddepth(sub {
unlink $_ if -f and -M $_ > 30; # delete old files
}, '/tmp/old');
# Multiple starting directories
find(\&wanted, qw(src lib t));
sub wanted {
my $path = $File::Find::name;
print "$path\n" if -f $path;
}File::Slurper
Современный модуль чтения/записи файлов
use Path::Tiny;
# Create a path object
my $path = path('/tmp/test.txt');
# File operations
$path->touch;
$path->spew("content\n");
my $content = $path->slurp;
my @lines = $path->lines;
# Path manipulations
my $parent = $path->parent;
my $child = $path->child('sub.txt');
my $abs = $path->absolute;
my $rel = $abs->relative('/tmp');
# Iterate directory contents
for my $f (path('.')->children) {
print $f, "\n" if $f->is_file;
}File::Copy & unlink
File::Copy provides copy and move across filesystems. File::Spec builds portable paths without hardcoding separators. unlink deletes files (returns the count removed); it cannot delete directories.
use File::Copy qw(copy move);
# Copy a file
copy('src.txt', 'dst.txt') or die $!;
copy('src.txt', 'dst.txt', 8192); # buffer size
# Move / rename
move('old.txt', 'new.txt') or die $!;
# Portable paths via File::Spec
use File::Spec;
my $path = File::Spec->catfile('dir', 'file.txt');
my ($vol, $dir, $file) = File::Spec->splitpath($path);
# Delete files
unlink qw(a.txt b.txt c.txt);
my $removed = unlink grep { -f } glob('*.tmp');Директории
Открытие директории
readdir читает записи директории
my $scalar = 42;
my @arr = (1, 2, 3);
my %h = (a => 1);
# Backslash creates a reference
my $sref = \$scalar;
my $aref = \@arr;
my $href = \%h;
my $cref = \&some_sub;
# Check what a reference points to
print ref($aref); # ARRAY
print ref($href); # HASH
print ref($sref); # SCALAR
print ref($cref); # CODE
# A non-reference returns ''
print ref("hi"); # (empty string)glob
glob wildcard имён файлов
my $aref = [1, 2, 3];
my $href = { a => 1, b => 2 };
# Whole array
my @arr = @$aref; # or @{ $aref }
my $len = scalar @$aref;
# Single element via arrow
print $aref->[0]; # 1
print $#$aref; # last index (2)
# Whole hash
my %h = %$href;
my @keys = keys %$href;
# Single value via arrow
print $href->{a}; # 1
# Block form for clarity
my @arr2 = @{ $aref };Операции с директориями
Операции с директориями и файлами
# Anonymous array reference: [ ]
my $arr = [1, 2, 3];
print $arr->[0]; # 1
# Anonymous hash reference: { }
my $h = { name => 'Alice', age => 30 };
print $h->{name};
# Nested anonymous structures
my $data = {
users => [ { name => 'a' }, { name => 'b' } ],
count => 2,
};
# Anonymous subroutine
my $code = sub { print "hi\n" };File::Find
Рекурсивный обход дерева директорий
# Array of arrays (2D)
my $matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
print $matrix->[1][2]; # 6 (second arrow optional)
# Hash of hashes
my $config = {
db => { host => 'localhost', port => 3306 },
app => { name => 'MyApp', debug => 1 },
};
print $config->{db}{host}; # localhost
# Auto-vivification creates intermediate refs
my %tree;
$tree{a}{b}{c} = 1; # builds the path automaticallyArrow Operator
The arrow -> dereferences: $ref->[idx] for arrays, $ref->{key} for hashes, $ref->() for code, $obj->method for objects. Between consecutive subscripts the arrow is optional for readability.
my $arr = [[1, 2], [3, 4]];
my $h = { users => [{ name => 'a' }] };
# Arrow between subscripts (second is optional)
print $arr->[0][1]; # 2
print $h->{users}[0]{name}; # a
# Arrow for coderef calls
my $greet = sub { "hi $_[0]" };
print $greet->("Bob"); # hi Bob
# Method calls on objects
$obj->method($arg);
# Slicing a dereferenced array
my @vals = @{ $h->{users} };Reference Types
ref() returns the class name for blessed references (objects), which can hide the underlying type. Scalar::Util::reftype() always returns the true structural type. Use weaken() to break reference cycles.
use Scalar::Util qw(reftype weaken);
my $aref = [];
my $href = {};
my $cref = sub {};
print ref($aref); # ARRAY
print reftype($aref); # ARRAY
# A blessed reference (object)
my $obj = bless {}, 'MyClass';
print ref($obj); # MyClass
print reftype($obj); # HASH (the underlying type)
# Type checks
if (ref($r) eq 'ARRAY') { ... }
if (ref($r) eq 'HASH') { ... }
# weaken avoids circular-reference memory leaks
weaken($r);Форматы
Определение формата
format определяет формат вывода
# use: runs at compile time, calls the module's import()
use strict;
use warnings;
use Data::Dumper;
# require: runs at runtime, does NOT call import()
require MyModule;
# Conditional loading (runtime)
if ($DEBUG) {
require Carp;
Carp->import('confess');
}
# use with version and import list
use v5.36;
use List::Util 1.33 qw(first sum max);
# use MODULE VERSION LIST
use Data::Dumper qw(Dumper);Спецификаторы формата
Спецификаторы полей формата