Remove BOM and problematic Unicode characters from text files
Project description
Uneff
A versatile tool to remove BOM (Byte Order Mark) and problematic Unicode characters from text files and streams.
Overview
Uneff is designed to clean text files by removing BOM markers and other invisible Unicode characters that can cause issues when processing data files. It's especially useful for:
- Cleaning CSV files before data processing
- Fixing encoding issues in text files
- Removing invisible control characters that break parsers
- Normalizing text data from various sources
I created this for use in my own personal project(s) and am including it here in case someone may find it useful as a starting point for their own data processing pipeline issues and challenges.
⚠️ Warning and Disclaimer
Risk of Unintended Consequences
IMPORTANT: Modifying text data by removing or replacing characters can have unintended consequences. While Uneff is designed to help with common data cleaning challenges, improper use may introduce new issues into your data.
- Always create backups of your original files before processing
- Always validate the results after processing to ensure data integrity
- Never use on production data without thorough testing
- Be especially cautious when working with:
- Internationalized text
- Complex data formats (like JSON, XML)
- Files with custom encoding
- Critical data where character precision matters
No Warranty
This tool is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
Recommended Workflow
- Analyze first: Use the
--analyzeflag to identify issues without modifying files - Test on samples: Process a small sample before running on large datasets
- Verify results: Implement verification checks after processing
- Document changes: Keep records of what was processed and why
- Limit scope: Only process the specific characters you know are problematic
Features
- Removes UTF-8, UTF-16, and UTF-32 BOM markers
- Handles invisible and problematic Unicode characters
- Supports replacing characters instead of just removing them
- Works with in-memory content (no direct file I/O required)
- Configurable via a simple CSV file - no code changes needed
- Can be used as a command-line tool or as a library in your projects
- Available in both Python and JavaScript versions
- Preserves original files by creating clean copies
- Detailed reporting of changes made
- Precise location reporting of problematic characters (line, column, and absolute position)
Installation
Python Package (Recommended)
Install from PyPI:
pip install uneff
Install from Git
Install the latest development version:
pip install git+https://github.com/mkiiim/uneff.git
Download Individual Files
Python Script
- Download
uneff.pyto your project directory - Make it executable (optional, for Unix-like systems):
chmod +x uneff.py
JavaScript Version
- Download
uneff.jsto your project directory - Ensure you have Node.js installed
Usage
Command Line
Python Package
uneff myfile.csv
Python Script
python uneff.py myfile.csv
Options
# Using package
uneff myfile.csv [-m mappings.csv] [-o output.csv] [-q] [-a]
# Using script
python uneff.py myfile.csv [-m mappings.csv] [-o output.csv] [-q] [-a]
-m, --mapping: Path to custom character mappings file-o, --output: Path to save cleaned file (default: adds uneffd_ prefix)-q, --quiet: Suppress status messages-a, --analyze: Only analyze file without cleaning
JavaScript Version
node uneff.js myfile.csv
Options:
node uneff.js myfile.csv [-m|--mapping mappings.csv] [-o|--output output.csv] [-q|--quiet] [-a|--analyze]
In Your Code
Python Version
import uneff
# Clean a file
uneff.clean_file('myfile.csv')
# Clean text directly without file I/O
dirty_text = 'Hello�World with invisible\u200bspaces'
clean_text = uneff.clean_text(dirty_text)
# Use with custom options
uneff.clean_file(
file_path='myfile.csv',
mapping_file='custom_mappings.csv',
output_path='cleaned.csv',
verbose=False
)
# Working with in-memory content (storage-agnostic)
content_bytes = get_content_from_your_system() # Your custom storage API
cleaned_content, char_counts = uneff.clean_content(content_bytes)
save_content_to_your_system(cleaned_content) # Your custom storage API
# Analyze file without modifying it
analysis = uneff.analyze_file('myfile.csv')
print(f"Found {analysis['problematic_char_count']} problematic characters")
JavaScript Version
const uneff = require('./uneff');
// Clean a file
uneff.cleanFile('myfile.csv');
// Clean text directly without file I/O
const dirtyText = 'Hello�World with invisible\u200bspaces';
const cleanText = uneff.cleanText(dirtyText);
// Use with custom options
uneff.cleanFile(
'myfile.csv',
'custom_mappings.csv',
'cleaned.csv',
false // verbose mode off
);
// Working with in-memory content (storage-agnostic)
const contentBytes = getContentFromYourSystem(); // Your custom storage API
const [cleanedContent, charCounts] = uneff.cleanContent(contentBytes);
saveContentToYourSystem(cleanedContent); // Your custom storage API
// Analyze file without modifying it
const analysis = uneff.analyzeFile('myfile.csv');
console.log(`Found ${analysis.problematic_char_count} problematic characters`);
Configuring Problematic Characters
On first run, Uneff creates a default uneff_mappings.csv file with common problematic characters. You can edit this file to customize how characters are handled:
| Character | Unicode | Name | Remove | Replacement |
|---|---|---|---|---|
| � | \ufffd | Replacement Character | True | |
| [empty] | \u0000 | NULL | True | |
| [empty] | \u2028 | Line Separator | True | " " |
| [empty] | \u2029 | Paragraph Separator | True | "\n" |
| [empty] | \u200b | Zero Width Space | True | |
| | \ufeff | BOM | True | |
| ... | ... | ... | ... | ... |
- To stop processing a specific character, change
TruetoFalse - To add new characters, add a new row with the appropriate Unicode escape sequence
- Add a replacement character in the "Replacement" column to replace instead of remove
- If "Remove" is True and "Replacement" is empty, the character is removed completely
Notes on the Replacement Field
- Do not add quotes around replacement values in the CSV file unless they contain special characters
- Leave empty to remove the character completely
- For whitespace characters:
- Use
\nfor newline - Use
\tfor tab - Use a space character for space
- Use
- For multiple characters, simply type them as-is (e.g.,
-->)
Special Characters in Replacements
When your replacement contains special characters like commas, you must use quotes in the CSV:
| Character | Unicode | Name | Remove | Replacement |
|---|---|---|---|---|
| [empty] | \u2063 | Invisible Separator | True | "," |
The CSV should look like this in a text editor:
Character,Unicode,Name,Remove,Replacement
,\u2063,Invisible Separator,True,","
Without the quotes, the comma would be interpreted as a new field separator.
Additional Examples
| Replacement For | Raw Text in CSV | Notes |
|---|---|---|
| Comma | "," | Must be quoted |
| Quote | """" | Double quotes to escape |
| Tab character | \t | Escape sequence |
| Newline | \n | Escape sequence |
| Comma + text | ",.txt" | Must be quoted |
What Characters are Processed?
By default, Uneff handles these types of characters:
- BOM (Byte Order Mark) characters
- Replacement Character (�)
- Control characters (NULL, SUB, FS, GS, RS, US)
- Zero-width spaces and joiners
- Bidirectional text control characters
- Line and paragraph separators (now replaced with spaces and newlines by default)
- Other invisible formatting characters
Advanced Analysis Features
The analysis functions provide detailed information about problematic characters in your text:
# Analyze a file and get detailed report
analysis = uneff.analyze_file('myfile.csv')
# Get total count of problematic characters
print(f"Found {analysis['problematic_char_count']} problematic characters")
# Get information about each type of problematic character
for detail in analysis['character_details']:
print(f"Character: {detail['name']} [Unicode: {detail['unicode']}]")
print(f"Count: {detail['count']} instances")
# Print locations of up to 10 instances
for location in detail['sample_locations']:
print(f" Found at Line {location['line']}, Column {location['column']}")
print(f" Context: ...{location['context']}...")
In JavaScript:
// Analyze a file and get detailed report
const analysis = uneff.analyzeFile('myfile.csv');
// Get total count of problematic characters
console.log(`Found ${analysis.problematic_char_count} problematic characters`);
// Get information about each type of problematic character
for (const detail of analysis.character_details) {
console.log(`Character: ${detail.name} [Unicode: ${detail.unicode}]`);
console.log(`Count: ${detail.count} instances`);
// Print locations of up to 10 instances
for (const location of detail.sample_locations) {
console.log(` Found at Line ${location.line}, Column ${location.column}`);
console.log(` Context: ...${location.context}...`);
}
}
The analysis results include:
- Exact line and column positions of each problematic character
- Absolute position in the file
- Surrounding context to help identify issues
- BOM detection and encoding information
- Character counts and statistics
First-Run Behavior
When running Uneff for the first time:
- If a mapping file doesn't exist, Uneff automatically creates one with default settings.
- This happens seamlessly, allowing you to immediately process files without errors.
- The default mapping file (
uneff_mappings.csv) is created in the same directory as the script.
Why Process These Characters?
Common Problems Caused by Invisible Characters
- Parser Failures: Many data processing systems and parsers fail when encountering unexpected Unicode characters
- Data Processing Errors: Characters like zero-width spaces break tokenization, field detection, and data extraction
- Database Import Issues: ETL processes often reject data with control characters
- Inconsistent Behavior: The same file might work in one system but fail in another
- Hard-to-Debug Problems: Since these characters are invisible, problems they cause can be extremely difficult to diagnose
Specific Use Cases
- CSV Processing: BOM markers can cause the first column name to be misread
- Data Analysis: Invisible characters can cause misalignment in data processing
- API Integrations: Data passed between systems can accumulate problematic characters
- Text Mining: Control characters can corrupt text analysis
- Custom Storage Systems: Applications with their own storage systems can now leverage Uneff's capabilities
Integration Options
For Standard File Processing
Use the traditional methods with direct file I/O:
uneff.clean_file('myfile.csv')
uneff.cleanFile('myfile.csv');
For Custom Storage Systems
Use the storage-agnostic core functions:
# 1. Get content using your storage API
content = your_app.get_file_content(file_id)
# 2. Use Uneff to clean it in memory
cleaned_content, char_counts = uneff.clean_content(content)
# 3. Save using your storage API
your_app.save_file_content(file_id, cleaned_content)
// 1. Get content using your storage API
const content = your_app.get_file_content(file_id);
// 2. Use Uneff to clean it in memory
const [cleaned_content, char_counts] = uneff.cleanContent(content);
// 3. Save using your storage API
your_app.save_file_content(file_id, cleaned_content);
Safe vs. Unsafe: When to Use Uneff
When It's Safe to Process These Characters
- Data Processing Pipelines: Before feeding data into analysis tools or databases
- Plain Text Content: Regular text documents, logs, configuration files
- Data Exchange: Files being transferred between different systems
- CSV Files: Almost always safe to remove BOM and control characters
- Legacy Data Cleanup: Fixing old files with encoding issues
When to Be Cautious
- Rich Text Documents: Some zero-width characters have specific formatting purposes in RTF, HTML, or Word docs
- Bidirectional Text: Languages like Arabic or Hebrew sometimes use special Unicode control characters
- Source Code: Some IDEs and development tools might use BOM markers intentionally
- XML/HTML: These formats sometimes use control characters with specific meanings
Potential Risks When Processing Characters
- Text Layout Changes: Modifying bidirectional controls might affect text rendering in some languages
- Formatting Loss: Some invisible characters serve legitimate formatting purposes
- Semantic Change: In rare cases, modifying zero-width joiners could change how text is displayed
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uneff-1.0.1.tar.gz.
File metadata
- Download URL: uneff-1.0.1.tar.gz
- Upload date:
- Size: 22.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1db6bbf7ae2c1ee89d091fb63d166681988f1c5d2956525feac7b0110e4c78b
|
|
| MD5 |
5e2ea3c605f602671d8859b916651d9f
|
|
| BLAKE2b-256 |
8cdb1f925359e9d995500de7be77b206952bb7c7576e64239982b43c4753fc5a
|
File details
Details for the file uneff-1.0.1-py3-none-any.whl.
File metadata
- Download URL: uneff-1.0.1-py3-none-any.whl
- Upload date:
- Size: 16.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45f50cf89cb28c0bffb3146a9709205c1dad7c341b6b52d7b9938fc9cd373370
|
|
| MD5 |
8edf455ff2d58abf5336fdf35ca98774
|
|
| BLAKE2b-256 |
db2d7e8932602f55887ae6dd8c0ec19a42e72817262ae8c5bb23b35a25d133aa
|