Still playing with text files (Jose Portilla on Udemy) - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Still playing with text files (Jose Portilla on Udemy) (/thread-21553.html) |
Still playing with text files (Jose Portilla on Udemy) - Drone4four - Oct-04-2019 In a previous thread titled, “Counting words in the last line of a file” that I was working on where I explored how to analyze text files, @DeaD_EyE graciously stepped in to help rewrite the script from scratch to make it more pythonic. @DeaD_EyE’s post can be found on page #2 (post #12) of my thread. See the bottom for the current script that I am working with now. It runs well and as intended. The script:
Now I am trying to add this feature to that list:
To achieve that, here are the changes that I made so far. I have:
When I run the script it turns out that it is print the selected line number twice. What I really want is for Python to print the chosen line once and the line number once. I partially understand that the request variable when asked for input (inside validate_and_shoose() ), it’s picking the integer value but it’s not clear to me how I can extract the actual line contents chosen by the user. What would you ppl recommend?Here is the latest iteration of my script: #!/usr/bin/env python3 """ Description This is my tenth iteration of this text reading script. This iteration is based on DeaD_EyE's. DeaD_EyE's script runs well. It prompts the user to choose a line, counts the number of words and characters but I noticed that it doesn't actually print the selected line when the instructions say it will. here I attempt to add this missing functionality """ import sys BOOKS = {1: 'Tolstoy.txt', 2: 'Alice.txt', 3: 'Chesterton.txt'} request = None def read_all_books(): """ Read all books from global variable BOOKS The keys are the digits """ result = {} for number, filename in BOOKS.items(): with open(filename) as fd: book_lines = fd.read().splitlines() result[number] = book_lines # maybe adding some metadata to the book return result def which_book(): """ This function presents the user with a selection of 3 potential books to examine. """ print("\nChoose from this list of books: \n 1. Tolstoy \n 2. Alice \n 3. Chesterton") while True: try: pick = int(input("What is your pick (1, 2, 3)? ")) except ValueError: print(f'{pick} is not in the list. Enter a valid number in the range of available books.') return pick def showcase(book): """ This function essentially prints the entire book, line by line (but also prints the associated line numbers) """ max_num = len(str(len(book))) + 1 # i know it's silly # just want to know how long the last linenumber is for num, line in enumerate(book): print(f'{num:>{max_num}}: {line.rstrip()}') # we don't return anything # the books are already loaded def validate_and_choose(allowed_range): """ This function ensures the user input is an integer and within the range of number of lines. """ global request request = input('\nWhich line do you want to count and print? >> ') range_text = f'integer in range {min(allowed_range)} - {max(allowed_range)}' while True: answer = input(f'Enter {range_text}: ') try: answer = int(answer) if answer in allowed_range: return answer if answer not in allowed_range: raise ValueError except ValueError: print(f'Expected {range_text} but input was "{answer}". Try again! ') def again(): """ This function gives the user the ability to (1) start from the very beginning at the top, (2) restart half way through (3) exit """ replay = input( "\n------------------------------------------------------\n" "\nWould you like to choose a new line in the same book?\n" "Or would you prefer to pick a line from a different book?\n" "\n'A' for the same book, \n'B' for a different book, " "or \n'C' to exit this program \n Make your selection: ") return replay.lower() def main_menu(): """ Main menu + Loop for this game """ print('Welcome to my game.') print('Maybe some help..') print(f'All books together take {utf8_in_memory / 1024**2:.2f} MiB in memory') # it's not right. It consumes more memory because of # the overhead of the dict itself and the list as holder for # the lines of the books print() book_key = which_book() # just a number, which is the key of the dict book_data while True: book = book_data[book_key] print() showcase(book) print() line_index = validate_and_choose(range(0, len(book))) words = len(book[line_index].split()) # characters = len(book[line_index]) characters = len(book[line_index].replace(' ', '')) # only characters, no whitespaces print(f'Here is the line # that you picked: "{line_index}"\n' f'Here is the content of the line that you picked:"{request}"\n' f'The number of words: {words}\n' f'The number of characters: {characters}') replay = again() if replay == 'a': continue elif replay == 'b': book_key = which_book() elif replay == 'c': print("Goodbye!") return 0 else: print( "I'll take that answer as a request" "to exit this program. Goodbye for now!" ) return 1 if __name__ == "__main__": book_data = read_all_books() # book_data on module leve. # it could be in main() # but then you must pass this around utf8_in_memory = sum(sys.getsizeof(line) for book in book_data.values() for line in book) try: retval = main_menu() except KeyboardInterrupt: retval = 10 print('\nGoodbye!') sys.exit(retval) # maybe as information for other shell citizensHere is my script on GitHub on the master branch. For my future reference, this exercise is part of Jose Portilla's Udemy course at this specific module: Python-Narrative-Journey/02-Field-Readiness-Exam-1/01-Field-Readiness-Exam-One.ipynb RE: Still playing with text files (Jose Portilla on Udemy) - knackwurstbagel - Oct-10-2019 Howdy. I have made a few changes and observations regarding your code.
Explanations: I removed the global request, in favour of returning the local answer from validate_and_choose(). The main application loop was very close, it had the correct line number. The key here is to realize that book is an array containing each line of your file. That is how you counted the number of words. It is also what showcase() iterates over. I also changed the variable name from words to word_count to make it more clear. I would suggest learning to use PDB so that you can watch your program run step by step, this enables you to see exactly what the variable values are and will lead to ah-ha moments. One good resource for this How to Use Pdb to Debug Your Code though there are many around the internet. In my experience pudb is more visual and easier to use but look around. You could even use the Community version of PyCharms just for debugging purposes and continue to use whatever editor is your preference. #!/usr/bin/env python3 """ Description This is my tenth iteration of this text reading script. This iteration is based on DeaD_EyE's. DeaD_EyE's script runs well. It prompts the user to choose a line, counts the number of words and characters but I noticed that it doesn't actually print the selected line when the instructions say it will. here I attempt to add this missing functionality """ import sys BOOKS = {1: 'Tolstoy.txt', 2: 'Alice.txt', 3: 'Chesterton.txt'} def read_all_books(): """ Read all books from global variable BOOKS The keys are the digits """ result = {} for number, filename in BOOKS.items(): with open(filename) as fd: book_lines = fd.read().splitlines() result[number] = book_lines # maybe adding some metadata to the book return result def which_book(): """ This function presents the user with a selection of 3 potential books to examine. """ print( "\nChoose from this list of books: \n 1. Tolstoy \n 2. Alice \n 3. Chesterton" ) while True: try: pick = int(input("What is your pick (1, 2, 3)? ")) except ValueError: print( f'{pick} is not in the list. Enter a valid number in the range of available books.' ) return pick def showcase(book): """ This function essentially prints the entire book, line by line (but also prints the associated line numbers) """ max_num = len(str(len(book))) + 1 # i know it's silly # just want to know how long the last linenumber is for num, line in enumerate(book): print(f'{num:>{max_num}}: {line.rstrip()}') # we don't return anything # the books are already loaded def validate_and_choose(allowed_range): """ This function ensures the user input is an integer and within the range of number of lines. """ print('\nWhich line do you want to count and print? >> \n') range_text = f'integer in range {min(allowed_range)} - {max(allowed_range)}' while True: answer = input(f'Enter {range_text}: ') try: answer = int(answer) if answer in allowed_range: return answer if answer not in allowed_range: raise ValueError except ValueError: print( f'Expected {range_text} but input was "{answer}". Try again! ') def again(): """ This function gives the user the ability to (1) start from the very beginning at the top, (2) restart half way through (3) exit """ replay = input( "\n------------------------------------------------------\n" "\nWould you like to choose a new line in the same book?\n" "Or would you prefer to pick a line from a different book?\n" "\n'A' for the same book, \n'B' for a different book, " "or \n'C' to exit this program \n Make your selection: ") return replay.lower() def main_menu(): """ Main menu + Loop for this game """ print('Welcome to my game.') print('Maybe some help..') print( f'All books together take {utf8_in_memory / 1024**2:.2f} MiB in memory' ) # it's not right. It consumes more memory because of # the overhead of the dict itself and the list as holder for # the lines of the books print() book_key = which_book() # just a number, which is the key of the dict book_data while True: book = book_data[book_key] print() showcase(book) print() line_index = validate_and_choose(range(0, len(book))) word_count = len(book[line_index].split()) # characters = len(book[line_index]) characters = len(book[line_index].replace(' ', '')) # only characters, no whitespaces print(f'Here is the line # that you picked: "{line_index}"\n' f'Here is the content of the line that you picked:' f'"{book[line_index]}"\n' f'The number of words: {word_count}\n' f'The number of characters: {characters}') replay = again() if replay == 'a': continue elif replay == 'b': book_key = which_book() elif replay == 'c': print("Goodbye!") return 0 else: print("I'll take that answer as a request" "to exit this program. Goodbye for now!") return 1 if __name__ == "__main__": book_data = read_all_books() # book_data on module leve. # it could be in main() # but then you must pass this around utf8_in_memory = sum( sys.getsizeof(line) for book in book_data.values() for line in book) try: retval = main_menu() except KeyboardInterrupt: retval = 10 print('\nGoodbye!') sys.exit(retval) # maybe as information for other shell citizensHere is the diff output of your file vs mine. I did a bunch of formating so there will be many changes but only three relate to functionality
RE: Still playing with text files (Jose Portilla on Udemy) - Drone4four - Oct-17-2019 Hi @knackwurstbagel! Thank you for reading and experimenting with my script. Your reply contains the solution that I needed. I know my script at this point is large so I appreciate your time, my friend. I don’t understand the diff output you shared. So instead I just copied your new script into a new text file and then compared yours with mine using my favourite difftool, p4merge. With p4merge I was able to see the formatting and cosmetic changes where you indented some of my strings and such to make it easier to read. I had realized that global variables should be used sparingly but I wasn’t sure how else to process the line number. However now that you have made the trivial change (at line 120), it is clear how straight forward the solution was. Instead of passing in the request (as I had it), you just passed in book[line_index] . That was easy enough! =)I’ve taken a look at your link to PyBites’ blog post on how to use the Python debugger. I’m not sure I understand it fully yet. I already have alotta new questions. I’m gonna jump in now and I'll write a new forum thread here with some more questions if need be. Thanks again! |