# perldoc > CHAR

---
type: CommandReference
command: perlfaq
mode: perldoc
section: 
source: perldoc
---

## Quick Reference

- `s/(.)\g1/$1/g` — remove consecutive duplicate characters
- `tr///cs` — squash duplicate characters (no replacements)
- `substr($string, $offset, $length, $replacement)` — access/change substring
- `Text::ParseWords::quotewords($delimiter, $keep, $text)` — split CSV respecting quotes
- `getc(FILEHANDLE)` — read single character from filehandle
- `Term::ReadKey` — read single character from terminal (raw mode)
- `select($rin, undef, undef, 0)` — check if input waiting on filehandle
- `$SIG{INT} = sub { ... }` — trap signals (e.g., Ctrl-C)

## Name

Perl FAQ entries covering string manipulation, character input from files/keyboard, multibyte character matching, and signal trapping. Extracted from perlfaq4, perlfaq5, perlfaq6, and perlfaq8.

## Synopsis

These are excerpts from the Perl FAQ (perlfaq) documentation. They provide solutions to common Perl programming questions.

## Examples

### String Manipulation

**Remove consecutive pairs of characters**

Use substitution with a back-reference:

perl
s/(.)\g1/$1/g;
Or use transliteration with the `s` (squash) and `c` (complement) options:

perl
my $str = 'Haarlem';
$str =~ tr///cs;   # Now "Harlem"
**Access or change N characters of a string**

Use `substr()`:

perl
my $string = "Just another Perl Hacker";
my $first_char = substr($string, 0, 1);   # 'J'

# Change a substring:
substr($string, 13, 4, "Perl 5.8.0");

# Or as an lvalue:
substr($string, 13, 4) = "Perl 5.8.0";
**Split a character-delimited string except when inside delimiters (e.g., CSV)**

Use the `Text::ParseWords` module (standard):

perl
use Text::ParseWords;
@new = quotewords(",", 0, $text);
For proper CSV handling, use `Text::CSV` or `Text::CSV_XS` from CPAN.

A manual regex approach (by Jeffrey Friedl) for quoted fields:

perl
my @new = ();
push(@new, $+) while $text =~ m{
    "([^\"\\]*(?:\\.[^\"\\]*)*)",?  # groups phrase inside quotes
    | ([^,]+),?
    | ,
}gx;
push(@new, undef) if substr($text, -1, 1) eq ',';
### Character Input

**Read a single character from a file or keyboard**

Use built-in `getc()` for filehandles, but not for terminals. For STDIN, use `Term::ReadKey` from CPAN:

perl
use Term::ReadKey;
open my $tty, '<', '/dev/tty';
print "Gimme a char: ";
ReadMode "raw";
my $key = ReadKey 0, $tty;
ReadMode "normal";
printf "\nYou said %s, char number %03d\n", $key, ord $key;
Alternatively, use POSIX code to disable line buffering and echo:

perl
#!/usr/bin/perl -w
use strict;
$| = 1;
for (1..4) {
    print "gimme: ";
    my $got = getone();
    print "--> $got\n";
}
exit;

BEGIN {
    use POSIX qw(:termios_h);
    my ($term, $oterm, $echo, $noecho, $fd_stdin);
    $fd_stdin = fileno(STDIN);
    $term = POSIX::Termios->new();
    $term->getattr($fd_stdin);
    $oterm = $term->getlflag();
    $echo = ECHO | ECHOK | ICANON;
    $noecho = $oterm & ~$echo;

    sub cbreak {
        $term->setlflag($noecho);
        $term->setcc(VTIME, 1);
        $term->setattr($fd_stdin, TCSANOW);
    }
    sub cooked {
        $term->setlflag($oterm);
        $term->setcc(VTIME, 0);
        $term->setattr($fd_stdin, TCSANOW);
    }
    sub getone {
        my $key = '';
        cbreak();
        sysread(STDIN, $key, 1);
        cooked();
        return $key;
    }
}
END { cooked() }
**Check if a character is waiting on a filehandle**

Use `Term::ReadKey` or the `select()` function:

perl
sub key_ready {
    my($rin, $nfd);
    vec($rin, fileno(STDIN), 1) = 1;
    return $nfd = select($rin, undef, undef, 0);
}
For determining the number of characters waiting, use the `FIONREAD` ioctl:

perl
require './sys/ioctl.ph';
$size = pack("L", 0);
ioctl(FH, FIONREAD(), $size) or die "Couldn't call ioctl: $!\n";
$size = unpack("L", $size);
If `h2ph` is unavailable, obtain the constant manually (e.g., by grepping headers or compiling a small C program) and hard-code it:

perl
$FIONREAD = 0x4004667f;   # example value, system-dependent
$size = pack("L", 0);
ioctl(FH, $FIONREAD, $size) or die "Couldn't call ioctl: $!\n";
$size = unpack("L", $size);
Note: `FIONREAD` works only on stream-based filehandles (sockets, pipes, tty devices), not on regular files.

### Multibyte Character Matching

**Make `\w` match national character sets**

Add `use locale;` in your script. Then `\w` will use the current locale's character class. See `perllocale` for details.

**Match strings with multibyte characters**

Perl 5.6+ has some multibyte support; Perl 5.8+ is recommended. Use Unicode and the `Encode` module for legacy encodings. For older Perls, use `Unicode::String`, `Unicode::Map8`, or `Unicode::Map`.

Example: Searching for a two-byte encoded character (Martian encoding) where pairs of ASCII uppercase letters represent a single character:

perl
# Approach 1: separate multibyte pairs with spaces
$martian =~ s/([A-Z][A-Z])/ $1 /g;
print "found GX!\n" if $martian =~ /GX/;

# Approach 2: extract into an array
my @chars = $martian =~ m/([A-Z][A-Z]|[^A-Z])/g;
foreach my $char (@chars) {
    print "found GX!\n", last if $char eq 'GX';
}

# Approach 3: iterative match with \G
while ($martian =~ m/\G([A-Z][A-Z]|.)/gs) {
    if ($1 eq 'GX') {
        print "found GX!\n";
        last;
    }
}

# Approach 4: zero-width negative look-behind
print "found GX!\n" if $martian =~ m/
    (?<![A-Z])
    (?:[A-Z][A-Z])*?
    GX
/x;
### Signal Handling

**Trap control characters/signals**

Set the `%SIG` hash with a handler for the signal name:

perl
# Anonymous subroutine
$SIG{INT} = sub { syswrite(STDERR, "ouch\n", 5) };

# Reference to a function
$SIG{INT} = \&ouch;

# Name of the function as a string
$SIG{INT} = "ouch";
Perl 5.8.0+ handles signals safely by looking at `%SIG` after the signal has been caught, rather than during the C-level signal handler.

## See Also

- [perlfaq4](https://perldoc.perl.org/perlfaq4) — String manipulation
- [perlfaq5](https://perldoc.perl.org/perlfaq5) — File I/O
- [perlfaq6](https://perldoc.perl.org/perlfaq6) — Regular expressions
- [perlfaq8](https://perldoc.perl.org/perlfaq8) — Interprocess communication
- [Text::ParseWords](https://perldoc.perl.org/Text::ParseWords)
- [Text::CSV](https://metacpan.org/pod/Text::CSV)
- [Text::CSV_XS](https://metacpan.org/pod/Text::CSV_XS)
- [Term::ReadKey](https://metacpan.org/pod/Term::ReadKey)
- [POSIX](https://perldoc.perl.org/POSIX)
- [Encode](https://perldoc.perl.org/Encode)
- [perlipc](https://perldoc.perl.org/perlipc) — Signals
- [perllocale](https://perldoc.perl.org/perllocale)
- [perlunicode](https://perldoc.perl.org/perlunicode)
- [perluniintro](https://perldoc.perl.org/perluniintro)