| |
| |
| |
| |
| |
| |
|
|
| import re |
| from typing import Iterator, Optional |
|
|
|
|
| |
| |
| |
| DEVANAGARI_PATTERN = r'[\u0900-\u097F\uA8E0-\uA8FF]+' |
|
|
| |
| HINDI_PUNCTUATION = r'[।॥,;:!?\-—–()\[\]{}"\'\'""]' |
|
|
| |
| DEVANAGARI_NUMBERS = r'[\u0966-\u096F]' |
| ARABIC_NUMBERS = r'[0-9]' |
|
|
| |
| |
| _WHITESPACE_PATTERN = re.compile(r'\s+') |
| _SPACE_BEFORE_PUNCT = re.compile(r'\s+([,;:!?।॥])') |
| _PUNCT_AFTER_SPACE = re.compile(r'([,;:!?।॥])([^\s])') |
| _OPEN_QUOTE_SPACE = re.compile(r'([""\'\([{])\s+') |
| _CLOSE_QUOTE_SPACE = re.compile(r'\s+([""\'\]}])') |
| _CLOSE_QUOTE_WORD = re.compile(r'([""\'\]}])([^\s])') |
| _DASH_NORMALIZE = re.compile(r'[—–]') |
| _QUOTE_NORMALIZE_DOUBLE = re.compile(r'[""]') |
| _QUOTE_NORMALIZE_SINGLE = re.compile(r'[\'\']') |
| _INVISIBLE_CHARS = re.compile(r'[\u200B-\u200D\uFEFF]') |
|
|
| def normalize_hindi_text(text): |
| """ |
| Normalize Hindi text using regex patterns for proper formatting. |
| |
| This function: |
| 1. Normalizes whitespace |
| 2. Handles punctuation properly (no space before, one space after) |
| 3. Normalizes common characters (dashes, quotes) |
| 4. Removes invisible characters |
| |
| Args: |
| text (str): Raw Hindi text |
| |
| Returns: |
| str: Normalized Hindi text |
| """ |
| if not text: |
| return text |
| |
| |
| text = re.sub(r'\s+', ' ', text).strip() |
| |
| |
| |
| |
| |
| text = re.sub(r'\s+([,;:!?।॥])', r'\1', text) |
|
|
| |
| |
| text = re.sub(r'([,;:!?।॥])([^\s])', r'\1 \2', text) |
| |
| |
| |
| |
| |
| text = re.sub(r'([“"‘\([{])\s+', r'\1', text) |
| |
| text = re.sub(r'\s+([”"’\])}])', r'\1', text) |
| |
| text = re.sub(r'([”"’\])}])([^\s])', r'\1 \2', text) |
| |
| |
|
|
| |
| text = re.sub(r'[—–]', '-', text) |
| |
| |
| text = re.sub(r'[“”]', '"', text) |
| text = re.sub(r'[‘’]', "'", text) |
| |
| |
| text = re.sub(r'[\u200B-\u200D\uFEFF]', '', text) |
| |
| |
| text = text.strip() |
| |
| return text |
|
|
| |
|
|
| |
| |
| |
| |
| |
|
|
| |
|
|
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
|
|
| def separate_punctuation(text): |
| """ |
| Separate punctuation marks from Hindi words using regex. |
| |
| This helps the tokenizer handle punctuation better by treating |
| it as separate tokens. |
| |
| Args: |
| text (str): Hindi text |
| |
| Returns: |
| str: Text with punctuation separated |
| """ |
| if not text: |
| return text |
| |
| |
| |
| text = re.sub(r'([\u0900-\u097F\uA8E0-\uA8FF]+)([।॥,;:!?\-()\[\]{}"\'])', r'\1 \2', text) |
| text = re.sub(r'([।॥,;:!?\-()\[\]{}"\'])([\u0900-\u097F\uA8E0-\uA8FF]+)', r'\1 \2', text) |
| |
| return text |
|
|
|
|
| def clean_hindi_text(text: str) -> str: |
| """ |
| Comprehensive cleaning of Hindi text using regex. |
| |
| Combines normalization and punctuation separation. |
| Optimized for large text processing. |
| |
| Args: |
| text (str): Raw Hindi text |
| |
| Returns: |
| str: Cleaned and normalized Hindi text |
| """ |
| if not text: |
| return text |
| |
| |
| text = normalize_hindi_text(text) |
| |
| |
| text = separate_punctuation(text) |
| |
| |
| text = _WHITESPACE_PATTERN.sub(' ', text).strip() |
| |
| return text |
|
|
|
|
| def clean_hindi_text_streaming(file_path: str, chunk_size: int = 1024 * 1024) -> Iterator[str]: |
| """ |
| Stream and clean large Hindi text files in chunks to avoid memory issues. |
| |
| This is optimized for very large files (>100MB) that don't fit in memory. |
| |
| Args: |
| file_path (str): Path to the text file |
| chunk_size (int): Size of chunks to read (default: 1MB) |
| |
| Yields: |
| str: Cleaned text chunks |
| """ |
| with open(file_path, 'r', encoding='utf-8') as f: |
| buffer = "" |
| while True: |
| chunk = f.read(chunk_size) |
| if not chunk: |
| if buffer: |
| yield clean_hindi_text(buffer) |
| break |
| |
| buffer += chunk |
| |
| |
| while '\n' in buffer: |
| line, buffer = buffer.split('\n', 1) |
| if line.strip(): |
| cleaned = clean_hindi_text(line) |
| if cleaned: |
| yield cleaned |
|
|
|
|
| def extract_hindi_words(text): |
| """ |
| Extract only Hindi words (Devanagari script) from text using regex. |
| |
| Useful for filtering out non-Hindi content. |
| |
| Args: |
| text (str): Mixed text |
| |
| Returns: |
| list: List of Hindi words found |
| """ |
| if not text: |
| return [] |
| |
| |
| hindi_words = re.findall(DEVANAGARI_PATTERN, text) |
| return hindi_words |
|
|
|
|
| def is_hindi_text(text): |
| """ |
| Check if text contains Hindi (Devanagari script) using regex. |
| |
| Args: |
| text (str): Text to check |
| |
| Returns: |
| bool: True if text contains Devanagari characters |
| """ |
| if not text: |
| return False |
| |
| return bool(re.search(DEVANAGARI_PATTERN, text)) |
|
|
|
|
| def filter_hindi_only(text: str, min_hindi_ratio: float = 0.7) -> str: |
| """ |
| Filter text to keep only lines/sentences with significant Hindi content. |
| |
| This ensures the tokenizer only learns from Hindi text, not mixed content. |
| Removes non-Hindi characters and keeps only Devanagari script with allowed punctuation. |
| |
| Args: |
| text (str): Input text (may contain mixed Hindi/English/other) |
| min_hindi_ratio (float): Minimum ratio of Devanagari chars to keep a line (0.0-1.0) |
| Default 0.7 means at least 70% Hindi characters |
| |
| Returns: |
| str: Filtered text containing only Hindi-dominant lines |
| """ |
| if not text: |
| return text |
| |
| lines = text.split('\n') |
| filtered_lines = [] |
| |
| for line in lines: |
| line = line.strip() |
| if not line: |
| continue |
| |
| |
| devanagari_chars = len(re.findall(DEVANAGARI_PATTERN, line)) |
| total_chars = len(re.sub(r'\s', '', line)) |
| |
| if total_chars == 0: |
| continue |
| |
| |
| hindi_ratio = devanagari_chars / total_chars if total_chars > 0 else 0 |
| |
| |
| if hindi_ratio >= min_hindi_ratio: |
| |
| |
| hindi_line = re.sub( |
| r'[^\u0900-\u097F\uA8E0-\uA8FF\s।॥,;:!?\-()\[\]{}"\']+', |
| ' ', |
| line |
| ) |
| |
| hindi_line = _WHITESPACE_PATTERN.sub(' ', hindi_line).strip() |
| if hindi_line: |
| filtered_lines.append(hindi_line) |
| |
| return '\n'.join(filtered_lines) |
|
|
|
|