```python import re import sys def replace_relative_substring( file_path, target_word, replace_word, position_offset ): """ Reads a file, finds the first case-insensitive exact match of target_word, and replaces a relative value. The original whitespace and punctuation formatting are preserved. 'position_offset' can be: Numeric offsets: -1 -> previous word, +1 -> next word -2 -> two words before, +2 -> two words after Row offsets: +1d -> same word/column index on the next row (down) +1u -> same word/column index on the previous row (up) Usage: replace_relative_substring("input.txt", "WindSpeed", "12.5", "+1d") """ # ------------------------------------------------------------ # Read file # ------------------------------------------------------------ with open(file_path, "r", encoding="utf-8") as f: content = f.read() # ------------------------------------------------------------ # Handle special row-based offsets: +1d and +1u # ------------------------------------------------------------ if position_offset in ("+1d", "+1u"): # Split into rows while preserving line endings. # splitlines(keepends=True) keeps the original formatting. rows = content.splitlines(keepends=True) # Search each row for target_word for row_index, row in enumerate(rows): # Split row into words and whitespace. # Whitespace is preserved. tokens = re.split(r"(\s+)", row) tokens = [t for t in tokens if t] # Indices of actual non-whitespace tokens word_indices = [ i for i, token in enumerate(tokens) if not token.isspace() ] # Search for target word in this row for column_index, token_idx in enumerate(word_indices): token = tokens[token_idx] # Remove leading/trailing punctuation clean_word = re.sub( r'^\W+|\W+$', '', token ) # Case-insensitive exact match if clean_word.lower() == target_word.lower(): # ------------------------------------------------ # Determine destination row # ------------------------------------------------ if position_offset == "+1d": destination_row = row_index + 1 else: # +1u destination_row = row_index - 1 # Check row bounds if not (0 <= destination_row < len(rows)): sys.exit( "\n---Specified row offset is outside " "the file---\n" ) # ------------------------------------------------ # Split destination row # ------------------------------------------------ destination_tokens = re.split( r"(\s+)", rows[destination_row] ) destination_tokens = [ t for t in destination_tokens if t ] destination_word_indices = [ i for i, t in enumerate(destination_tokens) if not t.isspace() ] # ------------------------------------------------ # Check that same column/index exists # ------------------------------------------------ if column_index >= len(destination_word_indices): sys.exit( "\n---The same column/index does not " "exist in the specified row---\n" ) replace_token_idx = ( destination_word_indices[column_index] ) original_token = ( destination_tokens[replace_token_idx] ) # Preserve punctuation leading_punct = re.match( r'^\W*', original_token ).group() trailing_punct = re.search( r'\W*$', original_token ).group() destination_tokens[replace_token_idx] = ( f"{leading_punct}" f"{replace_word}" f"{trailing_punct}" ) # Reconstruct destination row rows[destination_row] = "".join( destination_tokens ) # Reconstruct entire file updated_content = "".join(rows) with open( file_path, "w", encoding="utf-8" ) as f: f.write(updated_content) msg = ( f"File {file_path} updated + overwritten " f"for variable {target_word} " f"with position_offset={position_offset}\n" ) print(f"\n---{msg}") return True sys.exit( "\n---Specified string not found---\n" ) # ------------------------------------------------------------ # Existing word-based offset behaviour # ------------------------------------------------------------ # Split content into words and whitespace. # This preserves the original spacing. tokens = re.split(r'(\s+)', content) # Remove empty strings tokens = [t for t in tokens if t] # Find indices of actual words/non-whitespace tokens word_indices = [ i for i, token in enumerate(tokens) if not token.isspace() ] # Convert numeric offset try: numeric_offset = int(position_offset) except (ValueError, TypeError): sys.exit( "\n---Invalid position_offset. Use an integer, " "'+1d', or '+1u'---\n" ) # ------------------------------------------------------------ # Search for target word # ------------------------------------------------------------ for rank, token_idx in enumerate(word_indices): token = tokens[token_idx] # Strip punctuation clean_word = re.sub( r'^\W+|\W+$', '', token ) # Case-insensitive exact match if clean_word.lower() == target_word.lower(): # Calculate target word rank target_rank = rank + numeric_offset # Check bounds if not (0 <= target_rank < len(word_indices)): sys.exit( "\n---Either specified string not found " "or invalid offset---\n" ) replace_token_idx = word_indices[target_rank] original_token = tokens[replace_token_idx] # Preserve punctuation leading_punct = re.match( r'^\W*', original_token ).group() trailing_punct = re.search( r'\W*$', original_token ).group() # Replace only the core word tokens[replace_token_idx] = ( f"{leading_punct}" f"{replace_word}" f"{trailing_punct}" ) # Reconstruct content updated_content = "".join(tokens) with open( file_path, "w", encoding="utf-8" ) as f: f.write(updated_content) msg = ( f"File {file_path} updated + overwritten " f"for variable {target_word} " f"with position_offset={position_offset}\n" ) print(f"\n---{msg}") return True sys.exit( "\n---Either specified string not found " "or invalid offset---\n" ) # ------------------------------------------------------------ # Example usage # ------------------------------------------------------------ if __name__ == "__main__": replace_relative_substring( file_path, target_word, replace_word, position_offset ) ```