Censor the middle of words of length N from a text file LENGTHS_TO_CENSOR = {4, 5}
CENSOR_CHAR       = '*'
CENSOR_EXT        = &quot;-censored&quot;

def replace_inner(word, char):
    if len(word) &lt; 3:
        return word
    return word[0] + char * len(word[1:-1]) + word[-1]

def create_censor_file(filename):
    output_file = open(filename + CENSOR_EXT, &quot;w+&quot;)
    with open(filename) as source:
        for line in source:
            idx = 0
           
            while idx &lt; len(line):

                # If the character isn't a letter, write it to the output file.
                if not line[idx].isalpha():
                    output_file.write(line[idx])
                    idx += 1

                else:
                    word = &quot;&quot;

                    while idx &lt; len(line) and line[idx].isalpha():
                        word += line[idx]
                        idx += 1

                    if len(word) in LENGTHS_TO_CENSOR:
                        word = replace_inner(word, CENSOR_CHAR)
                    
                    output_file.write(word)

    output_file.close()

def main():
    filename = input(&quot;File to be censored: &quot;)
    create_censor_file(filename)

if __name__ == &quot;__main__&quot;:
    main()

I was assigned a task to censor words that are length n in a file. This file can potentially contain punctuation and numbers.
I originally tackled the problem by splitting the line into a list of words (using .split(' ')) and checking the length to determine if the program should censor the word or not. This failed for inputs such as:

does not work.for.this.input
or.this

The output file must be exactly like the input but with words of length in LENGTHS_TO_CENSOR censored with CENSOR_CHAR.
I decided to abandon trying to make it Pythonic and ended up with this result. I want to know if there is a way to take this method and make it more Pythonic.
create_censor_file should really be called create_censored_file.
I'd rename source to source_file for consistency and clarity.
You should use with for both files.
Why not use just w instead of w+?
This is probably one of the few things that regexes are actually useful for. You can just use re.sub(r'(?&lt;=\b\w)\w{' + ','.join(map(lambda x: str(x-2), LENGTHS_TO_CENSOR)) + '}(?=\w\b)', lambda match: CENCOR_CHAR * len(match.group(0)), source.read())


A couple other things:


Good job with the main function and if __name__ == '__main__' check!
I have not yet tested this code.


Result:

LENGTHS_TO_CENSOR = {4, 5}
CENSOR_CHAR       = '*'
CENSOR_EXT        = "-censored"

def create_censor_file(filename):       
    with open(filename + CENSOR_EXT, "w") as output_file, open(filename) as source_file:
        output_file.write(
            re.sub(
                r'(?&lt;=\b\w)\w{'
                    + ','.join(map(lambda x: str(x-2), LENGTHS_TO_CENSOR))
                    + '}(?=\w\b)',
                lambda match: CENSOR_CHAR * len(match.group(0)),
                source_file.read()))

def main():
    filename = input("File to be censored: ")
    create_censor_file(filename)

if __name__ == "__main__":
    main()