Tugas utama pengaturcara Python ialah menulis atur cara baris arahan, iaitu skrip yang dijalankan terus dalam terminal. Apabila projek itu berkembang dalam saiz, kami berharap dapat mencipta antara muka baris arahan yang berkesan yang boleh menyelesaikan masalah yang berbeza dengan menyediakan parameter yang berbeza dan bukannya mengubah suai kod sumber setiap kali.
Untuk mencapai matlamat ini, saya telah meringkaskan empat prinsip, saya harap ia akan membantu semua orang:
Mari kita gunakan peraturan ini pada kes konkrit: skrip yang menggunakan sifir Caesar untuk menyulitkan dan menyahsulit mesej.
Andaikan kita menulis fungsi penyulitan seperti yang ditunjukkan di bawah. Sekarang mari kita buat skrip untuk menyulitkan dan menyahsulit mesej.
Skrip membenarkan pengguna memilih: mod (penyulitan atau penyahsulitan), kunci. Nilai lalai untuk yang pertama ialah penyulitan, dan nilai lalai untuk yang terakhir ialah 1. Ini semua dicapai melalui parameter baris arahan.
def encrypt(plaintext, key): cyphertext = '' for character in plaintext: if character.isalpha(): number = ord(character) number += key if character.isupper(): if number > ord('Z'): number -= 26 elif number < ord('A'): number += 26 elif character.islower(): if number > ord('z'): number -= 26 elif number < ord('a'): number += 26 character = chr(number) cyphertext += character return cyphertext
Skrip perlu mendapatkan nilai parameter baris arahan terlebih dahulu, mari gunakan sys.argv yang paling mudah untuk dilaksanakan itu dahulu.
sys.argv ialah senarai yang mengandungi semua parameter yang dimasukkan oleh pengguna semasa menjalankan skrip (termasuk nama skrip itu sendiri).
Masukkan arahan berikut dalam terminal:
> python caesar_script.py --key 23 --decrypt my secret message pb vhfuhw phvvdjh
Senarai sys.argv termasuk:
['caesar_script.py', '--key', '23', '--decrypt', 'my', 'secret', 'message']
Untuk mendapatkan nilai Parameter, anda perlu melintasi senarai parameter, mencari '--key' (atau '-k') untuk mendapatkan nilai kunci, dan mencari '--decrypt' untuk mendapatkan mod .
import sys from caesar_encryption import encryp def caesar(): key = 1 is_error = False for index, arg in enumerate(sys.argv): if arg in ['--key', '-k'] and len(sys.argv) > index + 1: key = int(sys.argv[index + 1]) del sys.argv[index] del sys.argv[index] break for index, arg in enumerate(sys.argv): if arg in ['--encrypt', '-e']: del sys.argv[index] break if arg in ['--decrypt', '-d']: key = -key del sys.argv[index] break if len(sys.argv) == 1: is_error = True else: for arg in sys.argv: if arg.startswith('-'): is_error = True if is_error: print(f'Usage: python {sys.argv[0]} [ --key <key> ] [ --encrypt|decrypt ] <text>') else: print(encrypt(' '.join(sys.argv[1:]), key)) if __name__ == '__main__': caesar()
Kod mengikut prinsip yang kami bentangkan pada mulanya:
Mempunyai nilai kunci lalai dan mod lalai
Mengendalikan ralat asas (tiada teks input disediakan atau parameter tidak diketahui )
Cetak mesej gesaan ringkas apabila parameter salah atau skrip dipanggil tanpa parameter
> python caesar_script_using_sys_argv.py Usage: python caesar.py [ --key <key> ] [ --encrypt|decrypt ] <text>
Tetapi versi skrip ini agak panjang ( 39 baris, tidak termasuk fungsi penyulitan), dan kod itu sangat hodoh.
Adakah terdapat cara yang lebih baik untuk menghuraikan hujah baris arahan?
argparse ialah modul perpustakaan standard Python yang digunakan untuk menghuraikan argumen baris arahan.
Ubah suai skrip untuk menggunakan argparse untuk menghuraikan argumen baris arahan:
import argparse from caesar_encryption import encrypt def caesar(): parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() group.add_argument('-e', '--encrypt', action='store_true') group.add_argument('-d', '--decrypt', action='store_true') parser.add_argument('text', nargs='*') parser.add_argument('-k', '--key', type=int, default=1) args = parser.parse_args() text_string = ' '.join(args.text) key = args.key if args.decrypt: key = -key cyphertext = encrypt(text_string, key) print(cyphertext) if __name__ == '__main__': caesar()
Kod masih mematuhi prinsip yang kami cadangkan dan memberikan lebih ketepatan daripada arahan menghurai secara manual argumen baris. Dokumentasi dan pengendalian ralat yang lebih interaktif.
> python caesar_script_using_argparse.py --encode My message usage: caesar_script_using_argparse.py [-h] [-e | -d] [-k KEY] [text [text ...]] caesar_script_using_argparse.py: error: unrecognized arguments: --encode > python caesar_script_using_argparse.py --help usage: caesar_script_using_argparse.py [-h] [-e | -d] [-k KEY] [text [text ...]]positional arguments: text optional arguments: -h, --help show this help message and exit -e, --encrypt -d, --decrypt -k KEY, --key KEY
Baris 7 hingga 13 skrip mentakrifkan argumen baris arahan, tetapi ia tidak begitu elegan: terlalu bertele-tele dan prosedural, kita boleh menggunakan sesuatu yang lebih padat dan deklaratif Dilengkapkan dalam cara seksual.
Nasib baik, terdapat klik perpustakaan pihak ketiga untuk mencipta antara muka baris arahan Ia bukan sahaja menyediakan lebih banyak fungsi daripada argparse, tetapi juga mempunyai a gaya kod yang lebih baik. Gantikan argparse dengan klik dan teruskan mengoptimumkan skrip.
import click from caesar_encryption import encrypt @click.command() @click.argument('text', nargs=-1) @click.option('--decrypt/--encrypt', '-d/-e') @click.option('--key', '-k', default=1) def caesar(text, decrypt, key): text_string = ' '.join(text) if decrypt: key = -key cyphertext = encrypt(text_string, key) click.echo(cyphertext) if __name__ == '__main__': caesar()
Perhatikan bahawa parameter dan pilihan baris arahan diisytiharkan dalam penghias, yang menjadikannya boleh diakses terus sebagai argumen kepada fungsi.
Mari kita analisa kod di atas dengan teliti:
nargs mentakrifkan bilangan nilai yang diterima oleh argumen baris arahan Nilai lalai ialah 1, nargs=-1 membenarkan sebarang bilangan perkataan disediakan.
--encrypt/--decrypt mentakrifkan pilihan yang saling eksklusif, yang akhirnya diserahkan kepada program sebagai nilai Boolean.
click.echo ialah fungsi asas yang disediakan oleh pustaka klik Fungsinya serupa dengan cetakan, tetapi menyediakan fungsi yang lebih berkuasa, seperti melaraskan warna teks yang dicetak ke konsol.
Nilai yang diterima oleh parameter baris arahan ialah mesej rahsia utama yang akan disulitkan, jadi ia mungkin menimbulkan kebimbangan keselamatan jika pengguna diminta memasukkan teks biasa terus ke terminal.
Pendekatan yang lebih selamat ialah menggunakan pembayang tersembunyi, atau membaca teks daripada fail setempat, yang lebih praktikal untuk teks yang panjang.
Idea yang sama digunakan untuk output: pengguna boleh menyimpannya ke fail atau mencetaknya dalam terminal. Mari teruskan mengoptimumkan skrip.
import click from caesar_encryption import encrypt @click.command() @click.option( '--input_file', type=click.File('r'), help='File in which there is the text you want to encrypt/decrypt.' 'If not provided, a prompt will allow you to type the input text.', ) @click.option( '--output_file', type=click.File('w'), help='File in which the encrypted / decrypted text will be written.' 'If not provided, the output text will just be printed.', ) @click.option( '--decrypt/--encrypt', '-d/-e', help='Whether you want to encrypt the input text or decrypt it.' ) @click.option( '--key', '-k', default=1, help='The numeric key to use for the caesar encryption / decryption.' ) def caesar(input_file, output_file, decrypt, key): if input_file: text = input_file.read() else: text = click.prompt('Enter a text', hide_input=not decrypt) if decrypt: key = -key cyphertext = encrypt(text, key) if output_file: output_file.write(cyphertext) else: click.echo(cyphertext) if __name__ == '__main__': caesar()
Apabila skrip menjadi lebih kompleks, kami mencipta dokumen parameter (dilaksanakan dengan mentakrifkan parameter bantuan penghias click.option) untuk menerangkan fungsi parameter secara terperinci Kesannya adalah seperti berikut.
> python caesar_script_v2.py --help Usage: caesar_script_v2.py [OPTIONS] Options: --input_file FILENAMEFile in which there is the text you want to encrypt/decrypt. If not provided, a prompt will allow you to type the input text. --output_file FILENAME File in which the encrypted/decrypted text will be written. If not provided, the output text will just be printed. -d, --decrypt / -e, --encryptWhether you want to encrypt the input text or decrypt it. -k, --key INTEGERThe numeric key to use for the caesar encryption / decryption. --help Show this message and exit.
Kami mempunyai dua parameter baharu input_file dan output_file, jenisnya ialah klik. Fail, klik akan membuka fail dalam mod yang betul dan mengendalikan kemungkinan ralat. Sebagai contoh, fail tidak boleh ditemui:
> python caesar_script_v2.py --decrypt --input_file wrong_file.txt Usage: caesar_script_v2.py [OPTIONS] Error: Invalid value for "--input_file": Could not open file: wrong_file.txt: No such file or directory
Jika input_file tidak disediakan, kami menggunakan click.prompt untuk mencipta tetingkap gesaan pada baris arahan untuk membenarkan pengguna terus masukkan teks. Gesaan ini berguna untuk penyulitan Mod akan disembunyikan. Kesannya adalah seperti berikut:
> python caesar_script_v2.py --encrypt --key 2 Enter a text: ************** yyy.ukectc.eqo
假设你是一名黑客:想要解密一个用凯撒加密过的密文,但你不知道秘钥是什么。最简单的策略就是用所有可能的秘钥调用解密函数 25 次,阅读解密结果,看看哪个是合理的。但你很聪明,而且也很懒,所以你想让整个过程自动化。确定解密后的 25 个文本哪个最可能是原始文本的方法之一,就是统计所有这些文本中的英文单词的个数。这可以使用 PyEnchant 模块实现:
import click import enchant from caesar_encryption import encrypt @click.command() @click.option( '--input_file', type=click.File('r'), required=True, ) @click.option( '--output_file', type=click.File('w'), required=True, ) def caesar_breaker(input_file, output_file): cyphertext = input_file.read() english_dictionnary = enchant.Dict("en_US") max_number_of_english_words = 0 for key in range(26): plaintext = encrypt(cyphertext, -key) number_of_english_words = 0 for word in plaintext.split(' '): if word and english_dictionnary.check(word): number_of_english_words += 1 if number_of_english_words > max_number_of_english_words: max_number_of_english_words = number_of_english_words best_plaintext = plaintext best_key = key click.echo(f'The most likely encryption key is {best_key}. It gives the following plaintext:nn{best_plaintext[:1000]}...') output_file.write(best_plaintext) if __name__ == '__main__': caesar_breaker()
示例中的文本包含10^4个单词,因此该脚本需要大约5秒才能解密。这很正常,因为它需要检查所有25个秘钥,每个秘钥都要检查10^4个单词是否出现在英文字典中。
假设你要解密的文本包括10^5个单词,那么就要花费50秒才能输出结果,用户可能会非常着急。因此我建议这种任务一定要显示进度条。特别是,显示进度条还非常容易实现。下面是个显示进度条的例子:
import click import enchant from tqdm import tqdm from caesar_encryption import encrypt @click.command() @click.option( '--input_file', type=click.File('r'), required=True, ) @click.option( '--output_file', type=click.File('w'), required=True, ) def caesar_breaker(input_file, output_file): cyphertext = input_file.read() english_dictionnary = enchant.Dict("en_US") best_number_of_english_words = 0 for key in tqdm(range(26)): plaintext = encrypt(cyphertext, -key) number_of_english_words = 0 for word in plaintext.split(' '): if word and english_dictionnary.check(word): number_of_english_words += 1 if number_of_english_words > best_number_of_english_words: best_number_of_english_words = number_of_english_words best_plaintext = plaintext best_key = key click.echo(f'The most likely encryption key is {best_key}. It gives the following plaintext:nn{best_plaintext[:1000]}...') output_file.write(best_plaintext) if __name__ == '__main__': caesar_breaker()
这里使用了tqdm库,tqdm.tqdm类可以将任何可迭代对象转化为一个进度条。click也提供了类似的接口来创建进度条(click.progress_bar),但我觉得它不如tqdm好用。
Atas ialah kandungan terperinci Gunakan klik untuk mencipta program baris arahan Python yang sempurna. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!