3
\$\begingroup\$
LENGTHS_TO_CENSOR = {4, 5}
CENSOR_CHAR       = '*'
CENSOR_EXT        = "-censored"

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

def create_censor_file(filename):
    output_file = open(filename + CENSOR_EXT, "w+")
    with open(filename) as source:
        for line in source:
            idx = 0

            while idx < 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 = ""

                    while idx < 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("File to be censored: ")
    create_censor_file(filename)

if __name__ == "__main__":
    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.

\$\endgroup\$
5
\$\begingroup\$
  1. create_censor_file should really be called create_censored_file.
  2. I'd rename source to source_file for consistency and clarity.
  3. You should use with for both files.
  4. Why not use just w instead of w+?
  5. This is probably one of the few things that regexes are actually useful for. You can just use re.sub(r'(?<=\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:

  1. Good job with the main function and if __name__ == '__main__' check!
  2. 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'(?<=\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()
\$\endgroup\$
  • 1
    \$\begingroup\$ This is good! One small addidtion you can do multiple with statements on one line saving a line of indentation. with open(a) as a, open(b) as b \$\endgroup\$ – Ludisposed Sep 14 '18 at 7:25
  • \$\begingroup\$ @Ludisposed Good idea! Feel free to edit it in. \$\endgroup\$ – Solomon Ucko Sep 14 '18 at 10:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.