Skip to content
Back to skills

Foundations Character Encodings

ASecurity

Every bioinformatics pipeline starts with reading data from files. FASTA sequences, GenBank records, PDB coordinates, GFF annotations -- they all live as bytes on disk. When your Python script reads g

  • 4 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
data-aipythontestinggitdatabase

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add Pavel-Kravchenko/Bioinformatics --skill foundations-character-encodings --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Foundations Character Encodings?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Foundations Character Encodings
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pavel-kravchenko-foundations-character-encodings/badge)](https://www.skillsdirectory.com/skills/pavel-kravchenko-foundations-character-encodings)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: foundations-character-encodings
description: Every bioinformatics pipeline starts with reading data from files. FASTA sequences, GenBank records, PDB coordinates, GFF annotations -- they all live as bytes on disk. When your Python script reads g
tool_type: python
primary_tool: Python
---

# Character Encodings and Binary Data in Bioinformatics

## Pitfalls

- **Latin-1 never fails — but that does not mean it is right**: Latin-1 decodes every byte 0–255 successfully, silently producing wrong characters when the file is actually UTF-8 with multi-byte sequences.
- **UTF-8 BOM confusion**: Windows editors may prepend `\xef\xbb\xbf`. Decoders that don't strip the BOM insert an invisible garbage character at position 0, silently breaking FASTA header parsing. Use `'utf-8-sig'` to auto-strip.
- **Windows line endings in sequences**: `\r` at the end of a DNA line is invisible in most editors but breaks sequence length, regex, and split operations.
- **Non-breaking space vs. regular space**: Same visual width, different byte values — database lookups and split() treat them differently.

## From Bits to Bytes

| Notation | Example | Meaning |
|----------|---------|---------|
| Binary | `01000001` | 8 bits = 1 byte |
| Decimal | `65` | Human-friendly |
| Hexadecimal | `0x41` | 2 hex digits = 1 byte |

```python
for value in [0, 10, 32, 48, 65, 90, 97, 122, 127, 200, 255]:
    char = chr(value) if 32 <= value < 127 else '--'
    print(f"{value:7d}  {format(value,'08b'):>10}  0x{format(value,'02X')}  '{char}'")
```

## ASCII

Byte values 0–127. All modern encodings are backward-compatible with ASCII.

| Range | Content |
|-------|---------|
| 0–31 | Control chars (`\n`=10, `\t`=9, `\r`=13) |
| 32 | Space |
| 48–57 | `0`–`9` |
| 65–90 | `A`–`Z` |
| 97–122 | `a`–`z` |

**Bioinformatics relevance:** DNA (`ATGCN`), RNA (`AUGC`), and amino acid codes are pure ASCII — sequence data itself never has encoding issues. Problems arise in annotations, author names, organism names, and free-text fields.

```python
# FASTQ quality scores: Phred+33 maps quality 0-93 to ASCII 33-126
quality_string = "IIIIIIIIFFFFFFDDDDDBBBB"
for char in quality_string[:6]:
    phred = ord(char) - 33
    print(f"'{char}' -> Phred {phred} -> error rate {10**(-phred/10):.6f}")
```

## Encoding Zoo

Byte values 128–255 are where encodings diverge.

| Encoding | Scope | Key property |
|----------|-------|--------------|
| **Latin-1** (ISO 8859-1) | Western European | Every byte 0–255 valid; never raises error |
| **CP1251** | Cyrillic (Windows) | Byte 0xC0 = Cyrillic А |
| **CP866** | Cyrillic (DOS) | Byte 0xC0 = box-drawing char |
| **UTF-8** | Universal Unicode | Variable-length: ASCII=1B, Latin/Cyrillic=2B, CJK=3B, emoji=4B |

**UTF-8 is the default for all modern bioinformatics tools and databases.**

```python
# Same byte, three interpretations
byte_value = bytes([0xC0])
print(f"Latin-1: '{byte_value.decode('latin-1')}'")   # À
print(f"CP1251:  '{byte_value.decode('cp1251')}'")    # А (Cyrillic)
print(f"CP866:   '{byte_value.decode('cp866')}'")     # box-drawing
try:
    byte_value.decode('utf-8')
except UnicodeDecodeError:
    print("UTF-8:   ERROR — 0xC0 alone is not valid UTF-8")
```

```python
# UTF-8 variable-length in action
examples = [
    ('A', 'ASCII letter'),
    ('\u03b1', 'Greek alpha (protein structure)'),
    ('\u00e9', 'e-acute (French author names)'),
    ('\u0410', 'Cyrillic A (Russian annotations)'),
    ('\U0001F9EC', 'DNA emoji'),
]
for char, context in examples:
    enc = char.encode('utf-8')
    print(f"{char}  {' '.join(f'{b:02X}' for b in enc):<20} {len(enc)}B  {context}")
```

## Examining Raw File Bytes

```python
def hexdump(data, width=16):
    for i in range(0, len(data), width):
        chunk = data[i:i+width]
        hex_part = ' '.join(f'{b:02X}' for b in chunk)
        ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
        print(f"{i:04X}  {hex_part:<{width*3}}  {ascii_part}")

# BOM detection
bom_content = b'\xef\xbb\xbf>sp|P04637|P53_HUMAN\nMEEPQ\n'
decoded_with_bom    = bom_content.decode('utf-8')
decoded_without_bom = bom_content.decode('utf-8-sig')  # strips BOM
print(f"utf-8:     starts with U+{ord(decoded_with_bom[0]):04X}")   # FEFF (invisible)
print(f"utf-8-sig: starts with '{decoded_without_bom[0]}'")         # >
```

## Windows vs. Unix Line Endings

| System | Ending | Bytes |
|--------|--------|-------|
| Unix/macOS | `\n` (LF) | `0A` |
| Windows | `\r\n` (CR+LF) | `0D 0A` |
| Old Mac | `\r` (CR) | `0D` |

Many bioinformatics tools expect Unix line endings. `\r\n` causes extra `\r` in sequence lines, off-by-one coordinates, or FASTA parsers that include `\r` in the sequence.

```python
def parse_fasta_robust(raw_bytes):
    """Parse FASTA from raw bytes, handling any line ending style."""
    text = raw_bytes.decode('utf-8').replace('\r\n', '\n').replace('\r', '\n')
    sequences = {}; current_header = None; current_seq = []
    for line in text.strip().split('\n'):
        line = line.strip()
        if line.startswith('>'):
            if current_header is not None:
                sequences[current_header] = ''.join(current_seq)
            current_header = line[1:]; current_seq = []
        elif line:
            current_seq.append(line)
    if current_header is not None:
        sequences[current_header] = ''.join(current_seq)
    return sequences
```

## Pitfalls (General)

- **Coordinate systems**: BED 0-based half-open; VCF/GFF 1-based inclusive — mixing causes off-by-one errors
- **Batch effects**: Always check for batch confounding before interpreting biological signal
- **Multiple testing**: Apply FDR correction (Benjamini-Hochberg) for thousands of features

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.