在win10环境下安装RabbitMQ的步骤第一步：下载并安装erlang原因：RabbitMQ服务端代码是使用并发式语言Erlang编写的，安装Rabbit MQ的前提是安装Erlang。下载地址：http://www.erlang.org/downloads由于我的电脑是64位系统，所以我选择是的64位下载。第二步、下载完成后开始安装点击install开始安装，完成后点击close第三步、配置环境变量ERLANG_HOME变量名：ERLANG_HOME变量值就是刚才erlang的安装地址，点击确定。然后双击系统变量path，点击“新建”，将%ERLANG_HOME%\bin加入到path中。最后windows键+R键，输入cmd，再输入erl，看到版本号就说明erlang安装成功了。第四步、下载安装rabbitMq下载地址：http://www.rabbitmq.com/download.html下载完成后双击安装RabbitMQ安装好后接下来安装RabbitMQ-Plugins。打开命令行cd，输入RabbitMQ的sbin目录然后在后面输入rabbitmq-plugins enable rabbitmq_management命令进行安装出现上图代表安装成功，打开sbin目录，双击rabbitmq-server.bat访问 http://localhost:15672默认用户名和密码都是guest登陆即可。原文出处：https://www.cnblogs.com/count-mjb/p/10939117.html
Loguru是一个功能强大且易于使用的开源Python日志记录库。它建立在Python标准库中的logging模块之上，并提供了更加简洁直观、功能丰富的接口。Logging模块的使用见： Python日志记录库logging总结 。Loguru官方仓库见：loguru，loguru官方文档见： loguru-doc。Loguru的主要特点包括：简单易用：无需复杂的配置和定制即可实现基本的日志记录和输出。灵活的日志格式：支持自定义日志格式，并提供丰富的格式化选项。丰富的日志级别：支持多种日志级别，例如DEBUG、INFO、WARNING、ERROR和CRITICAL。多种日志目标：可以将日志输出到终端、文件、电子邮件、网络服务器等目标。强大的日志处理功能：支持日志过滤、格式化、压缩、旋转等功能。支持异步日志记录：能够极大地提升日志记录的性能。支持跨进程、跨线程的日志记录：可以安全地记录多进程、多线程应用程序的日志。Loguru与logging是Python中常用的两个日志记录库，但两者在功能和易用性方面存在一些差异，如下所示：特性Logurulogging易用性更简单易用相对复杂日志格式更灵活较简单日志级别更丰富较少日志目标更多种类较少日志处理功能更强大较弱异步日志记录支持不支持跨进程、跨线程支持支持支持总的来说，loguru在易用性、功能性和性能方面都优于logging。如果要一个简单、强大且易于使用的日志系统，loguru是一个很好的选择。而如果只是需要快速输出一些调试信息，print可能就足够了。不过，对于生产环境，使用loguru或其他日志系统通常会更加合适。Loguru安装命令如下：pip install loguru# 查看loguru版本 import loguru print(loguru.__version__) # 输出：0.7.21 使用说明1.1 基础用法简单使用 Loguru的核心概念是只有一个全局的日志记录器，也就是logger。这个设计使得日志记录变得非常简洁和一致。使用Loguru时，你不需要创建多个日志实例，而是直接使用这个全局的logger来记录信息。这不仅减少了配置的复杂性，也使得日志管理更加集中和高效。from loguru import logger logger.debug("这是一个调试信息")输出：2024-06-29 19:57:44.506 | DEBUG | __main__::3 - 这是一个调试信息Loguru日志输出默认格式如下：时间戳：表示日志记录的具体时间，格式通常为年-月-日 时:分:秒.毫秒。日志级别：表示这条日志的严重性级别。进程或线程标识：表示日志来自哪个模块或脚本。 main 表示日志来自主模块。如果是其他文件会显示文件名。文件名和行号：记录日志消息的函数名和行号。日志消息：实际的日志内容，此外loguru支持使用颜色来区分不同的日志级别，使得日志输出更加直观.日志等级Loguru可以通过简单的函数调用来记录不同级别的日志，并自动处理日志的格式化和输出。这一特点可以让使用者专注于记录重要的信息，而不必关心日志的具体实现细节。Loguru支持的日志级别，按照从最低到最高严重性排序：TRACE: 最详细的日志信息，用于追踪代码执行过程。Loguru默认情况下使用DEBUG级别作为最低日志记录级别，而不是TRACE级别。这是因为TRACE级别会产生大量的日志信息。DEBUG: 用于记录详细的调试信息，通常只在开发过程中使用，以帮助诊断问题。INFO: 用于记录常规信息，比如程序的正常运行状态或一些关键的操作。SUCCESS: 通常用于记录操作成功的消息，比如任务完成或数据成功保存。WARNING: 用于记录可能不是错误，但需要注意或可能在未来导致问题的事件。ERROR: 用于记录错误，这些错误可能会影响程序的某些功能，但通常不会导致程序完全停止。CRITICAL: 用于记录非常严重的错误，这些错误可能会导致程序完全停止或数据丢失。from loguru import logger logger.debug("这是一条跟踪消息") logger.debug("这是一条调试信息") logger.info("这是一条普通信息") logger.success("操作成功完成") logger.warning("这是一条警告信息") logger.error("这是一条错误信息") logger.critical("这是一条严重错误信息")输出：2024-06-29 19:58:11.535 | DEBUG | __main__:<module>:3 - 这是一条跟踪消息 2024-06-29 19:58:11.536 | DEBUG | __main__:<module>:4 - 这是一条调试信息 2024-06-29 19:58:11.536 | INFO | __main__:<module>:5 - 这是一条普通信息 2024-06-29 19:58:11.537 | SUCCESS | __main__:<module>:6 - 操作成功完成 2024-06-29 19:58:11.537 | WARNING | __main__:<module>:7 - 这是一条警告信息 2024-06-29 19:58:11.538 | ERROR | __main__:<module>:8 - 这是一条错误信息 2024-06-29 19:58:11.538 | CRITICAL | __main__:<module>:9 - 这是一条严重错误信息1.2 日志配置在loguru中，add函数用于添加日志处理器。这个函数用于指定日志消息应该被发送到何处，例如控制台、文件或其他自定义的目的地。add函数主要参数介绍如下：sink: 定义日志消息的输出位置，可以是文件路径、标准输出（stdout）、标准错误（stderr，默认）或其他自定义的输出位置。format: 指定日志消息的格式，可以是简单的字符串，也可以是格式化字符串，支持各种字段插值。level: 设置处理程序处理的日志消息的最低级别。比如设置为DEBUG，则处理程序将处理所有级别的日志消息。filter: 可选参数，用于添加过滤器，根据特定的条件过滤掉不需要的日志消息。colorize: 布尔值，指定是否对日志消息进行着色处理，使日志在控制台中更易于区分。serialize: 布尔值，指定是否对日志消息进行序列化处理，通常与enqueue=True一起使用，以确保多线程安全。enqueue: 布尔值，指定是否将日志消息放入队列中处理，用于多线程应用中避免阻塞。backtrace: 布尔值或字符串，指定是否记录回溯信息，默认为False。diagnose: 布尔值，启用后，会在处理程序内部出现错误时记录诊断信息。rotation: 日志文件轮换的配置，支持按大小或时间进行日志文件的轮换。retention: 用于设置日志文件的保留时间。compression: 布尔值，指定是否对轮换后的日志文件进行压缩处理。from loguru import logger import sys # 终端显示不受该段代码设置 # 添加一个日志处理器，输出到文件 # 设置日志最低显示级别为INFO，format将设置sink中的内容 # sink链接的本地文件，如不存在则新建。如果存在则追写 logger.add(sink="myapp.log", level="INFO", format="{time:HH:mm:ss} | {message}| {level}") # debug结果不被显示到本地文件 logger.debug("这是一条调试信息") logger.info("这是一条普通信息")输出：2024-06-29 19:58:56.159 | DEBUG | __main__:<module>:11 - 这是一条调试信息 2024-06-29 19:58:56.159 | INFO | __main__:<module>:12 - 这是一条普通信息当连续两次调用 add 函数时，loguru 会将新的日志处理器添加到处理器列表中，而不是覆盖之前的处理器。这意味着所有添加的处理器都会接收到日志消息，并且按照它们被添加的顺序来处理这些消息。from loguru import logger logger.add(sink="myapp1.log", level="INFO") logger.add(sink="myapp2.log", level="INFO") # 会同时存入所有add添加日志处理器 logger.info("这是一条普通信息，存入myapp2")如果想删除所有已添加的日志处理器，loguru运行使用 logger.remove()方法不带任何参数来移除所有日志处理器。from loguru import logger import sys # 移除所有日志处理器（包括终端输出） logger.remove() logger.add(sink="myapp3.log", level="INFO", format="{time:HH:mm:ss} | {message}| {level}") logger.debug("这是一条调试信息存入myapp3") logger.info("这是一条普通信息存入myapp3")注意调用logger.remove()之后的所有日志将不会被记录，因为没有处理器了。from loguru import logger # 移除所有日志处理器（包括终端输出） logger.remove() # 没有输出 logger.info("这是一条普通信息存入myapp3")如果希望移除某些日志处理器，而不是从所有日志器中移除，代码如下：from loguru import logger # 移除默认终端logger，如果终端存在。 # logger.remove(0) # 添加多个文件处理器，enqueu设置异步日志记录 handler1 = logger.add("myapp1.log", enqueue=True) print(handler1) # handler_id是移除的处理器的唯一标识符 handler2 = logger.add("myapp2.log") # 记录一些日志 logger.info("这些信息会被记录到两个文件中") # 移除特定的文件处理器 logger.remove(handler1) # 现在只有myapp2.log 会记录日志 logger.info("这条信息只会记录在myapp2.log 中")如果想将日志输出到日志台，代码如下：from loguru import logger import sys logger.remove() # 移除默认输出 # 添加一个日志处理器，输出到控制台，使用自定义格式 logger.add( sink=sys.stdout, level="DEBUG", # green表示颜色 format="<green>{time:HH:mm}</green> <level>{message}</level>" ) # 注意终端显示会同步显示 logger.debug("这是一条调试信息") logger.info("这是一条普通信息")时间自定义 可以使用datatime库来自定义日志时间格式。from datetime import datetime from loguru import logger # 自定义时间格式 # time_format = "%Y-%m-%d %H:%M:%S,%f" # 包括微秒 time_format = "%H:%M:%S,%f" # 包括微秒但不含年月日 # 定义日志格式，使用 datetime.now().strftime() 来格式化时间 log_format = "{time:" + time_format + "} - {level} - {message}" logger.add("myapp.log", format=log_format, level="DEBUG") # 记录一条日志 logger.debug("这是一个带有微秒的测试日志")日志轮换from loguru import logger # 当文件大小达到100MB时创建新的日志文件，旧文件保留并重命名，用于防止单个日志文件变得过大。 logger.add("file_1.log", rotation="100 MB") # 每天中午12时创建新的日志文件，旧文件保留并重命名 logger.add("file_2.log", rotation="12:00") # 当日志文件存在超过一周时创建新的日志文件，旧文件保留并重命名 logger.add("file_3.log", rotation="1 week") # 设置日志文件保留10天 logger.add("file_4.log", retention="10 days") # 当文件大小达到100MB时创建新的日志文件，旧文件保留压缩为zip文件 logger.add('file_{time}.log', rotation="100 MB", compression='zip')1.3 进阶使用异常捕获 @logger.catch装饰器可以用来装饰my_function函数，并将这些异常信息记录到日志中。from loguru import logger logger.add(sink='myapp.log') @logger.catch def my_function(x, y): return x / y res = my_function(0,0)过滤 使用loguru库进行Python日志记录时，可以通过自定义的filter函数来筛选并记录特定的日志信息。此函数接收一个记录对象作为参数，根据日志消息内容(message)、级别(level)或其他日志属性，返回布尔值以决定是否记录该条日志。如果函数返回True，则日志被记录；若返回False，则忽略该日志。from loguru import logger # 定义一个过滤器函数 def my_filter(record): # 只记录包含 "第一" 的日志 return "第一" in record["message"] # 使用过滤器 logger.add("myapp.log", filter=my_filter) # 记录一些日志 logger.info("第一个记录") logger.info("第二个记录")此外可以结合bind方法进行过滤，bind方法用于向日志记录器添加额外的上下文信息。这些信息将被包含在每条日志消息中，但不会改变日志消息本身。如下所示：from loguru import logger def filter_user(record): return record["extra"].get("user") =="A" logger.add("myapp.log", filter=filter_user) # 绑定user logger.bind(user="A").info("来自A") logger.bind(user="B").info("来自B")原文出处：https://www.cnblogs.com/luohenyueji/p/18276299
缘起今天在看一个bug，之前一个分支的版本是正常的，在新的分支上上加了很多日志没找到原因，希望回溯到之前的版本，确定下从哪个提交引入的问题，但是还不想把现在的修改提交，也不希望在Git上看到当前修改的版本（带有大量日志和调试信息）。因此呢，查查Git有没有提供类似功能，就找到了git stash的命令。综合下网上的介绍和资料，git stash（git储藏）可用于以下情形：发现有一个类是多余的，想删掉它又担心以后需要查看它的代码，想保存它但又不想增加一个脏的提交。这时就可以考虑git stash。使用git的时候，我们往往使用分支（branch）解决任务切换问题，例如，我们往往会建一个自己的分支去修改和调试代码, 如果别人或者自己发现原有的分支上有个不得不修改的bug，我们往往会把完成一半的代码commit提交到本地仓库，然后切换分支去修改bug，改好之后再切换回来。这样的话往往log上会有大量不必要的记录。其实如果我们不想提交完成一半或者不完善的代码，但是却不得不去修改一个紧急Bug，那么使用git stash就可以将你当前未提交到本地（和服务器）的代码推入到Git的栈中，这时候你的工作区间和上一次提交的内容是完全一样的，所以你可以放心的修Bug，等到修完Bug，提交到服务器上后，再使用git stash apply将以前一半的工作应用回来。经常有这样的事情发生，当你正在进行项目中某一部分的工作，里面的东西处于一个比较杂乱的状态，而你想转到其他分支上进行一些工作。问题是，你不想提交进行了一半的工作，否则以后你无法回到这个工作点。解决这个问题的办法就是git stash命令。储藏(stash)可以获取你工作目录的中间状态——也就是你修改过的被追踪的文件和暂存的变更——并将它保存到一个未完结变更的堆栈中，随时可以重新应用。git stash用法1. stash当前修改git stash会把所有未提交的修改（包括暂存的和非暂存的）都保存起来，用于后续恢复当前工作目录。比如下面的中间状态，通过git stash命令推送一个新的储藏，当前的工作目录就干净了。$ git status On branch master Changes to be committed: new file: style.css Changes not staged for commit: modified: index.html $ git stash Saved working directory and index state WIP on master: 5002d47 our new homepage HEAD is now at 5002d47 our new homepage $ git status On branch master nothing to commit, working tree clean 需要说明一点，stash是本地的，不会通过git push命令上传到git server上。实际应用中推荐给每个stash加一个message，用于记录版本，使用git stash save取代git stash命令。示例如下：$ git stash save "test-cmd-stash" Saved working directory and index state On autoswitch: test-cmd-stash HEAD 现在位于 296e8d4 remove unnecessary postion reset in onResume function $ git stash list stash@{0}: On autoswitch: test-cmd-stash2. 重新应用缓存的stash可以通过git stash pop命令恢复之前缓存的工作目录，输出如下：$ git status On branch master nothing to commit, working tree clean $ git stash pop On branch master Changes to be committed: new file: style.css Changes not staged for commit: modified: index.html Dropped refs/stash@{0} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)这个指令将缓存堆栈中的第一个stash删除，并将对应修改应用到当前的工作目录下。你也可以使用git stash apply命令，将缓存堆栈中的stash多次应用到工作目录中，但并不删除stash拷贝。命令输出如下：$ git stash apply On branch master Changes to be committed: new file: style.css Changes not staged for commit: modified: index.html3. 查看现有stash可以使用git stash list命令，一个典型的输出如下：$ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert "added file_size" stash@{2}: WIP on master: 21d80a5 added number to log在使用git stash apply命令时可以通过名字指定使用哪个stash，默认使用最近的stash（即stash@{0}）。4. 移除stash可以使用git stash drop命令，后面可以跟着stash名字。下面是一个示例：$ git stash list stash@{0}: WIP on master: 049d078 added the index file stash@{1}: WIP on master: c264051 Revert "added file_size" stash@{2}: WIP on master: 21d80a5 added number to log $ git stash drop stash@{0} Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)或者使用git stash clear命令，删除所有缓存的stash。5. 查看指定stash的diff可以使用git stash show命令，后面可以跟着stash名字。示例如下：$ git stash show index.html | 1 + style.css | 3 +++ 2 files changed, 4 insertions(+)在该命令后面添加-p或--patch可以查看特定stash的全部diff，如下：$ git stash show -p diff --git a/style.css b/style.css new file mode 100644 index 0000000..d92368b --- /dev/null +++ b/style.css @@ -0,0 +1,3 @@ +* { + text-decoration: blink; +} diff --git a/index.html b/index.html index 9daeafb..ebdcbd2 100644 --- a/index.html +++ b/index.html @@ -1 +1,2 @@ + 6. 从stash创建分支如果你储藏了一些工作，暂时不去理会，然后继续在你储藏工作的分支上工作，你在重新应用工作时可能会碰到一些问题。如果尝试应用的变更是针对一个你那之后修改过的文件，你会碰到一个归并冲突并且必须去化解它。如果你想用更方便的方法来重新检验你储藏的变更，你可以运行 git stash branch，这会创建一个新的分支，检出你储藏工作时的所处的提交，重新应用你的工作，如果成功，将会丢弃储藏。$ git stash branch testchanges Switched to a new branch "testchanges" # On branch testchanges # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: index.html # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # # modified: lib/simplegit.rb # Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)这是一个很棒的捷径来恢复储藏的工作然后在新的分支上继续当时的工作。7. 暂存未跟踪或忽略的文件默认情况下，git stash会缓存下列文件：添加到暂存区的修改（staged changes）Git跟踪的但并未添加到暂存区的修改（unstaged changes）但不会缓存一下文件：在工作目录中新的文件（untracked files）被忽略的文件（ignored files）git stash命令提供了参数用于缓存上面两种类型的文件。使用-u或者--include-untracked可以stash untracked文件。使用-a或者--all命令可以stash当前目录下的所有修改。至于git stash的其他命令建议参考Git manual。小结git提供的工具很多，恰好用到就可以深入了解下。更方便的开发与工作的。原文出处：https://www.cnblogs.com/tocy/p/git-stash-reference.html
名称git-stash - 将变化藏在一个脏工作区中概述git stash list [<日志选项>] git stash show [-u | --include-untracked | --only-untracked] [<差异选项>] [<暂存>] git stash drop [-q | --quiet] [<暂存>] git stash pop [--index] [-q | --quiet] [<暂存>] git stash apply [--index] [-q | --quiet] [<暂存>] git stash branch <分支名> [<暂存>] git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet] [-u | --include-untracked] [-a | --all] [(-m | --message) <信息>] [--pathspec-from-file=<文件> [--pathspec-file-nul]] [--] [<路径规范>…​]] git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet] [-u | --include-untracked] [-a | --all] [<信息>] git stash clear git stash create [<信息>] git stash store [(-m | --message) <信息>] [-q | --quiet] <提交>描述当你想记录工作目录和索引的当前状态，但又想回到一个干净的工作目录时，请使用git stash。 该命令将你的本地修改保存起来，并将工作目录还原为与HEAD提交相匹配。这个命令所存储的修改可以用git stash list列出，用git stash show检查，用git stash apply恢复（可能是在不同的提交之上）。 在没有任何参数的情况下调用git stash等同于git stash push。 默认情况下，储藏库被列为 "WIP on branchname …​"，但你可以在创建储藏库时在命令行中给出更多描述性信息。你最近创建的储藏库被保存在refs/stash中；旧的储藏库可以在这个引用的引用日志中找到，并且可以使用通常的引用日志语法来命名（例如，stash@{0}是最近创建的储藏库，stash@{1}是它之前的储藏库，stash@{2.hours.ago}也是可以的）。也可以通过指定储藏库的索引来引用储藏库（例如，整数n等同于stash@{n}）。命令push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [(-m|--message) <信息>] [--pathspec-from-file=<文件> [--pathspec-file-nul]] [--] [<路径规范>…​]将你的本地修改保存到一个新的 "存储条目 "中，并将它们回滚到 HEAD（在工作区和索引中）。 <信息>部分是可选的，它给出了描述和储藏的状态。为了快速制作快照，你可以省略 "push"。 在这种模式下，非选项参数是不允许的，以防止拼写错误的子命令产生不需要的储藏条目。 这方面的两个例外是stash -p，它作为stash push -p的别名，以及为消除歧义允许在双连字符--之后的路径规范元素。save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<信息>]该选项已被弃用，改为 "git stash push"。 它与 "stash push "的不同之处在于，它不能接受路径规范。 取而代之的是，所有非选项的参数都被串联起来，形成储藏消息。list [<日志选项>]列出你目前拥有的储藏目录。 每个 "储藏目录 "都列出了它的名字（例如，stash@{0}是最新的条目，stash@{1}是之前的条目，等等），条目产生时的分支名称，以及该条目所基于的提交的简短描述。stash@{0}: WIP on submit: 6ebd0e2...更新 git-stash 文档 stash@{1}: On master: 9cc0589... 添加 git-stash该命令采用适用于 git log 命令的选项来控制显示的内容和方式。参见 [git-log[1]](https://git-scm.com/docs/git-log/zh_HANS-CN) 。show [-u|--include-untracked|--only-untracked] [<diff选项>] [<储藏目录>]显示贮藏中记录的修改，作为贮藏内容与贮藏库条目首次创建时的提交之间的差异。 默认情况下，该命令显示diff统计，但它会接受 "git diff "已知的任何格式（例如，git stash show -p stash@{1}以补丁形式查看第二条最新条目）。 如果没有提供<diff选项>，默认行为将由stash.showStat和stash.showPatch配置变量给出。你也可以使用stash.showIncludeUntracked来设置是否默认启用--include-untracked`。pop [--index] [-q|--quiet] [<暂存>]从贮藏库列表中移除一个单一的贮藏状态，并将其应用于当前工作区状态之上，也就是做git stash push的逆向操作。工作目录必须与索引匹配。应用状态可能会因为冲突而失败；在这种情况下，它不会被从贮藏库列表中删除。你需要手动解决冲突，并在之后调用 git stash drop。apply [--index] [-q|--quiet] [<暂存>]和 pop 一样，但不从贮藏库列表中删除该状态。与pop不同，<贮藏项>可以是任何看起来像由stash push或stash create创建的提交。branch <分支名> [<贮藏项>]创建并检查一个名为 <分支名> 的新分支，从最初创建 <贮藏项> 的提交开始，将 <贮藏项> 中记录的修改应用到新的工作树和索引。 如果成功了，并且<贮藏项>是stash@{<版本>}形式的引用，那么它将删除<贮藏项>。如果你运行 git stash push 的分支发生了足够的变化，以至于 git stash apply 因冲突而失败，那么这就很有用。由于贮藏条目是在运行 git stash 时的 HEAD 提交之上应用的，它没有冲突地恢复了最初的贮藏状态。清除删除所有的贮藏条目。直接切断任何联系，而且可能无法恢复（可能的策略见下面的 "例子"）。Drop [-q|--quiet] [<贮藏项>]从贮藏条目列表中删除一个单一的贮藏条目。create创建一个贮藏条目（这是一个普通的提交对象），并返回其对象名称，而不将其存储在引用命名空间的任何地方。 这是为了对脚本有用。 你可能不想用这个命令；可以看看前面的 "push"。保存将通过’git stash create'（这是一个悬空的合并提交）创建的特定贮藏库存储在贮藏库引用中，更新贮藏库参考文件。 这是为了对脚本有用。 这条命令可能不是你想要的；见上文"push"。选项-a--all这个选项只对push和save命令有效。所有被忽略的和未被追踪的文件也被贮藏起来，然后用git clean来清理。-u--include-untracked--no-include-untracked当与push和save命令一起使用时，所有未被追踪的文件也被贮藏起来，然后用git clean来清理。当与show命令一起使用时，显示贮藏库条目中未被追踪的文件作为差异的一部分。--only-untracked这个选项只对show命令有效。只显示贮藏库条目中未被追踪的文件作为差异的一部分。--index这个选项只对pop和apply命令有效。不仅试图恢复工作区的变化，而且恢复索引的变化。然而，这可能会在出现冲突时失败（这些冲突被存储在索引中，因此你不能再按原来的方式应用这些变化）。-k--keep-index--no-keep-index这个选项只对push和save命令有效。所有已经添加到索引中的变化都保持原样。-p--patch这个选项只对push和save命令有效。交互式地从 HEAD 和工作区之间的差异中选择要存储的内容。 藏匿条目的构造是这样的：它的索引状态与你仓库的索引状态相同，它的工作区只包含你交互选择的变化。 被选中的修改会从你的工作区中回滚。参见 [git-add[1]](https://git-scm.com/docs/git-add/zh_HANS-CN) 中的 '互动模式' 一节，了解如何操作--patch模式。选项 --patch 意味着 --keep-index。 你可以使用 --no-keep-index 来覆盖它。-S--staged这个选项只对push和save命令有效。只存放当前分阶段的修改。这类似于基本的git commit，只不过是将状态提交到贮藏室而不是当前分支。--patch选项要优先于这个选项。--pathspec-from-file=这个选项只对push命令有效。Pathspec在 <文件> 中传递，而不是在命令行参数中传递。如果 <文件> 正好是 -，则使用标准输入。路径规范元素由 LF 或 CR/LF 分隔。可以引用配置变量 core.quotePath 的路径规范元素（请参见 git-config[1]）。另请参见 --pathspec-file-nul 和全局 --literal-pathspecs。--pathspec-file-nul这个选项只对push命令有效。只有在使用 --pathspec-from-file 选项时才有意义。指定路径元素用 NUL 字符分隔，所有其他字符都按字面意思（包括换行符和引号）表示。-q--quiet这个选项只对apply、drop、pop、push、save、store命令有效。静默，压制反馈信息。--这个选项只对push命令有效。为了消除歧义，将路径规范与选项分开。…​这个选项只对push命令有效。新的贮藏条目只记录了符合路径规范文件的修改状态。 然后索引条目和工作区文件也被回滚到 HEAD 中的状态，只留下不符合路径规范的文件。更多细节请参见 [gitglossary[7]](https://git-scm.com/docs/gitglossary/zh_HANS-CN) 中的 路径规范 条目。这个选项只对apply、branch、drop、pop、show命令有效。一个形式为stash@{<版本>}的引用。如果没有给出<贮藏项>，则假定是最新的储藏库（即stash@{0}）。讨论一个贮藏库条目被表示为一个提交，它的目录树记录了工作目录的状态，它的第一个父节点是创建该条目时在HEAD的提交。 第二个父节点的树记录了条目生成时索引的状态，它是HEAD提交的一个子节点。 祖先图看起来像这样： .----W / / -----H----I其中H是HEAD提交，I是记录索引状态的提交，W是记录工作区状态的提交。实例拉进一个脏目录树当你在做某件事的时候，你得知上游有一些变化可能与你正在做的事情有关。 当你的本地修改与上游的修改不冲突时，一个简单的`git pull’就可以让你继续前进。然而，在有些情况下，你的本地修改确实与上游修改有冲突，而git pull拒绝覆盖你的修改。 在这种情况下，你可以把你的进度保存起来，执行一次拉取，然后再解开，像这样：$ git pull ... 文件 foobar 不是最新的，无法合并。 $ git stash $ git pull $ git stash pop中断的工作流程当你正在做某件事的时候，你的老板来了，要求你立即修复某件事。 传统上，你会向一个临时分支提交，以储存你的修改，然后返回到你的原始分支进行紧急修复，就像这样：# ... 嗨骇害 ... $ git switch -c my_wip $ git commit -a -m "我待会还得写点东西" $ git switch master $ edit emergency fix $ git commit -a -m "速速修复BUG" $ git switch my_wip $ git reset --soft HEAD^ # ... 继续骇入 ...你可以用’git stash’来简化上述工作，像这样：# ... 嗨骇害 ... $ git stash $ edit emergency fix $ git commit -a -m "紧急修复" $ git stash pop # ... 继续骇入 ...部分测试提交当你想把工作区上的改动做两个或更多的提交，并且想在提交前测试每个改动时，你可以使用git stash push --keep-index：# ... 嗨骇害 ... $ git add --patch foo # 仅将第一部分添加到索引中 $ git stash push --keep-index # 将所有其他改动保存到储藏室中 $ edit/build/test first part $ git commit -m '第一个部分' # 提交完全测试过的改动 $ git stash pop # 准备处理所有其他改动 # ... 重复以上五个步骤，直到剩下一个提交... $ 编辑/构建/测试剩余部分 $ git commit foo -m '剩余部分'。保存不相关的变化供将来使用当你在进行大规模修改时，发现一些不相关的问题，你不想忘记修复，你可以进行修改，将其分阶段，然后使用 git stash push --staged 将其存放起来，以便将来使用。这类似于提交阶段性修改，只是提交的结果是在贮藏库而不是在当前分支。# ... 嗨骇害 ... $ git add --patch foo # 将不相关的改动添加到索引中 $ git stash push --staged # 将这些改动保存到储藏库中 # ... 嗨骇害， 完成当前改动 ... $ git commit -m '大规模测试' # 提交完全测试过的改动 $ git switch fixup-branch # 切换到另一个分支 $ git stash pop # 完成已保存更改的工作恢复被错误地清除/丢弃的贮藏库条目如果你错误地丢弃或清除了贮藏库条目，它们无法通过正常的安全机制恢复。 然而，你可以试试下面的咒语，以获得仍在你的版本库中，但无法到达的贮藏库条目列表：git fsck --unreachable | grep commit | cut -d -f3 | xargs git log --merges --no-walk --grep=WIP原文出处：https://git-scm.com/docs/git-stash/zh_HANS-CN
Git冲突：Please commit your changes or stash them before you merge
使用git pull来更新代码时，有时会遇到以下的问题：**error: Your local changes to the following files would be overwritten by merge:....phpPlease, commit your changes or stash them before you can merge.Aborting** 这个问题原因是其他人修改了某个文件并提交到版本库中去了，而你本地也修改了同一个，这时候你进行拉取就会出现冲突了，解决方法，原则是不要去更改别人已经提交的代码，如果确实要更改（不建议也不需要），请先跟当事人沟通方法一：放弃本地修改(此方法本地修改的代码会被丢弃，不可找回)git reset --hard git pullgit reset --hard：撤销工作区中所有未提交的修改内容，将暂存区与工作区都回到上一次版本，并删除之前的所有信息提交方法二：同样是放弃本地修改使用TortoiseGit（小乌龟），打开冲突文件所在目录，如下：方法三：使用git stashgit stash git pull git stash popgit stash：保存当前工作进度，能够将所有未提交的修改（工作区和暂存区）保存至堆栈中，用于后续恢复当前工作目录。也可以用git stash save，作用等同于git stash，区别是可以加一些注释git pull：这个应该不用说了吧！（把服务器仓库的更新拉到本地仓库中）git stash pop：可以把你刚才stash到本地栈中的代码pop到本地（也可以用git stash apply，区别：使用apply恢复，stash列表中的信息是会继续保留的，而使用pop恢复，会将stash列表中的信息进行删除。）git stash list：存储到本地栈顶以后，你可以使用git stash list 查看你本地存储的stash日志git stash clear: 清空Git栈，原来stash的节点都会被清除原文链接：https://blog.csdn.net/weixin_44545251/article/details/115366666
{callout color="#d1d1d1"} 《PHP获取客户端真实IP地址》PHP获取客户端真实IP地址,只要根据实际部署情况选择相对应的代码获取访客IP地址,是可以确保程序不被假IP欺骗的...{/callout} php获取客户端真实IP地址，需要根据具体的服务器环境来确定使用哪种方法。目前搜索到的方法，大多是直接贴代码，没有针对不同情况作出说明，有可能导致系统被假IP骗过（IP欺骗）。 很多文章都提到“无法保证获取到的访客IP地址100%准确”，是否意味着PHP获取访客IP一定有漏洞可钻呢？ 只要根据实际部署情况选择相对应的代码获取访客IP地址，是可以确保程序不被假IP欺骗的。PHP的运行方式 PHP支持非常多的运行方式，例如php-cgi、php-fpm、swoole、php-cli、php-mod等。其中，php-fpm是php的fast-cgi的进程管理器。php-mod通常配合Apache使用，而php-fpm通常配合Nginx使用。从PHP5.4开始，PHP甚至可以以内置PHP服务（Buid-in web server）方式运行。逐渐的，PHP形成了很多经典搭配，例如LAMP、LNMP、LNMPA、IIS+PHP。 无论采用哪种PHP运行方式，分清是谁传递IP给PHP程序即可。这里对几种常见环境进行分析。无代理层（PHP内置服务器/swoole） 由于客户端IP数据是从TCP/IP协议层传递过来的，因此在没有中间代理（客户端和服务器直连）的情况下，可以直接通过标准方法REMOTE_ADDR获得与PHP直接通讯的IP地址。 例如$_SERVER['REMOTE_ADDR']或getenv("REMOTE_ADDR")。 而在swoole中，可以通过$request->server['remote_addr']获得客户端IP地址。 在这种情况下，REMOTE_ADDR不可以显式的伪造，获取到的是实际与服务器连接的IP地址，是可靠的。Nginx代理（Nginx + php-fpm / Nginx + swoole） 常见的LNMP方案，属于Nginx反向代理。Nginx与php的通讯，无论Unix socket方式还是tcp socket方式，都跟Nginx的Header配置有关系。proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 可以在第一层nginx代理设置 proxy_set_header X-Forwarded-For $remote_addr,其他层nginx代理设置proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for,这样就可以通过X-Forwarded-For获取客户端真实IP地址。如果设置了X-Real-IP $remote_addr，则通过X-Real-IP获取客户端IP地址。PHP代码：// php-fpm等 $_SERVER['HTTP_X_FORWARDED_FOR']); // swoole $this->http_input->header('x-real-ip'); 总结来说，当有Nginx代理的情况下，需要根据具体配置来选择获取的Header，从而正确获取客户端IP地址，此时PHP中的的REMOTE_ADDR可能是最后一级代理的IP地址。Apache代理（Apache + php_mod / Apache + php-fpm） 通常在Apache + PHP方案中，获取IP地址取决于Apache配置信息。 绝大部分情况可以使用$_SERVER["REMOTE_ADDR"]获取到真实客户端IP地址。 如果Apache上级存在Nginx，那么可以在Apache中使用mod_rpaf模块将客户端IP地址传递到X-Forwarded-For头中。PAFheader X-Forwarded-For负载均衡/云虚拟机/Serverless 在负载均衡条件下，需要查阅对应负载均衡程序的文档，来决定使用哪种方法获取真实客户端IP地址。 在大部分虚拟主机、负载均衡及无服务器中，可以通过HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR或X-REAL-IP其一得知客户端IP地址。但是不建议对多种来源进行空值判断，这样容易被伪造者利用，从而实现IP欺骗。 目前并未有标准规定将客户端IP地址放入名为CLIENT_IP的环境变量，但是有不少老虚拟主机供应商这样做。新的提供商都不再使用该环境变量。网上大量的PHP文章中都是优先获取HTTP_CLIENT_IP的值，因此导致网上存在大量的服务器可以被伪造IP欺骗。 可能有该漏洞的代码：<?php if(!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) { $ip = $_SERVER['REMOTE_ADDR']; }IIS + PHP 这种情况下直接使用$_SERVER['REMOTE_ADDR']方法即可。总结 无论是哪种情况，如果用户使用匿名代理访问服务器，只能获取到代理服务器的IP地址，其IP地址仍然具有参考意义。 获取客户端真实IP地址，此事需要查阅相关资料根据具体情况选用某一个方法，而不是 复制粘贴 。 部分方法获取到的值可能是一个数组。 不建议对获取到的IP地址进行正则过滤，有可能你获取到的是一个IPv6地址。原文出处：http://www.cppcns.com/wangluo/php/646369.html
{callout color="#d1d1d1"}《PHP定界符php和html代码混编方法实例详解》：本文主要介绍PHP定界符php和html代码混编方法实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...{/callout}正文 PHP定界符的运用，极大地方便了PHP和html混编的结构，因为在定界符中可以包括变量函数和html代码，而且可以正常解析。不用对字符串进行转义，可以是多行字符串。 PHP定界符是一种用于在字符串中定义大段代码的语法结构，它有三种形式：一、 HEREDOC（<<<）定界符 HEREDOC被称为“长字符串常量”，用于包含多行字符串。格式如下：$string = <<<EOT 这是一段多行字符串 可以包含变量以及任何其他语法 EOT; 在这个例子中，HEREDOC开始于"<<<EOT"，结束于"EOT;"，三个EOT之间的所有内容都是字符串，可以包含变量以及其他语法。 在PHP中，HEREDOC（Here Document）是一种用于创建多行字符串的语法结构。它允许您通过指定一个标识符来引用字符串。<<<是HEREDOC的定界符，用于指示开始和结束标识符之间的字符串是HEREDOC类型的。下面为大家写几个示例代码：1. 使用字符串字面量替换<<<：$name = <<<EOT Hello, world! This is a sample string. EOT; 在这个示例代码中，使用双引号包围的字符串字面量替换<<<，并在末尾添加了换行符EOT;作为结束标识符。2. 使用自定义标识符：$greeting = <<<Greetings Hello, $name! Greetings; 上述代码中，使用自定义的标识符Greetings来引用字符串。标识符可以是任何有效的PHP标识符，但通常建议使用有意义的名称。3. 嵌套使用HEREDOC：$message = <<<EOT Hello, $name! This is a sample message. Here's a list of fruits: $fruits EOT; 上述代码中，在字符串中使用另一个HEREDOC来创建嵌套的字符串。这允许您在字符串中嵌入多行文本。二、 NOWDOC（<<<'标识符'） NOWDOC与HEREDOC非常相似，但不会解析变量和特殊字符。NOWDOC是PHP中的另一种字符串语法结构，类似于HEREDOC，但不会解析其中的变量。NOWDOC使用单引号来定界字符串，并在<<<后的标识符前加上单引号。格式如下：$string = <<<'EOT' 这是一段多行字符串 不会解析包含在字符串中的任何变量或特殊字符 EOT; 注意到这里的标识符是用单引号包裹的，它与下面的纯文本是完全相同的。与HEREDOC不同，NOWDOC不会替换其中的变量或转义任何特殊字符。1. 基本示例：$message = <<<'EOT' Hello, world! This is a sample string. EOT; 该示例代码，使用NOWDOC的语法结构来创建一个多行字符串。由于使用了单引号定界符，其中的变量不会被解析。2. 使用自定义标识符：$greeting = <<<'Greetings' Hello, $name! Greetings; 在这个示例代码中，使用自定义的标识符Greetings来引用字符串。与NOWDOC一样，其中的变量不会被解析。 值得注意的是，由于NOWDOC不会解析字符串中的变量，因此您无法在其中引用或嵌入变量。如果需要在字符串中使用变量值，应该使用HEREDOC。所以NOWDOC适用于那些不需要解析变量的情况。使用单引号定界符的NOWDOC语法可以确保字符串中的文本保持原样，不会被解析或替换为变量值。三、短标签写法（<?= ?>） 短标签写法允许PHP代码直接嵌入HTML中，使用"<?="输出变量的值。例如：<?= $variable; ?> 在这个例子中，"$variable"的值将被输出到HTML页面中。请注意，您必须启用短标签才能使用它，通过在php.ini文件中将short_open_tag选项设置为"on"，或者在代码中使用"<?php"标记。四、使用<?php"标记在html代码中嵌入php代码，这种方法比较好用，但是代码看起来会比较乱！<?php $nus=rand(1,10);//赋值变量为0到10的随意数字 ?> html中嵌入php代码 随机数值是：<?php echo $nus;?> 注意，php代码不会在html文件中被解释，但是html代码可以在php文件中使用！五、把html代码赋值给php变量在php代码中，把html代码赋值给php变量也可以实现混编。示例1：<?php $html = ' ' ; $html .= ' ' ; $html .= ' 你好html ' ; $html .= ' ' ; $html .= ' ' ; $html .= ' 这就是输出php变量$html的效果! ' ;//这里的$html在单引号内，不会被解释，会被视为字符串直接输出！ $html .= ' ' ; $html .= ' ' ; echo $html; ?>PHP定界符php和html代码混编方法实例详解示例2：<?php $html = '//注意这里下一行起 你好html 这就是输出php变量$html的效果! ';//html代码到这里结束，这样的效果和使用HEREDOC（<<<）定界符的效果是一样的！ echo $html;这里的输出内容与示例1是一样的！ ?>六、总结 在使用PHP开发项目的时候，使用php与html代码混编是一件非常常见的事情，体现出php语言的灵活性。你可能会好奇，为什么我们在使用dz、zblog、织梦建站的时候，php文件里没有看到html代码。那是因为他们采用了模板分离的方法，把php文件和html分开出来编写代码，然后再通过php把html文件转换成php与html代码混编的逻辑代码，最终都是以html的形式呈现网站内容！如果你也希望php代码和html分开编写使用，你可以去找一款合适自己的模板引擎来帮助你达到该目的！原文出处：http://www.cppcns.com/wangluo/php/649944.html
《Python利用psutil库进行监控进程和资源》psutil是Python系统和进程工具库,它提供了一种跨平台的方式来获取系统信息、管理系统进程、监控系统性能、操作系统资源等,下面就跟随小编一起来... python作为一门强大的编程语言，拥有丰富的标准库和第三方库，使得开发者可以轻松完成各种任务。在系统监控和管理方面，psutil库无疑是一个强大的工具，它可以轻松地获取系统信息、管理进程、监控系统性能等。本文将深入探讨psutil库，介绍它的基本概念、用法和示例代码，帮助大家更好地理解和利用这个强大的库。什么是psutil库psutil是Python系统和进程工具库（Python System and Process Utilities）。它提供了一种跨平台的方式来获取系统信息、管理系统进程、监控系统性能、操作系统资源等。无论是在Windows、linux还是MACOS，psutil都能提供一致的API，使得开发者可以轻松地编写跨平台的代码。安装psutil库要开始使用psutil库，首先需要安装它。可以使用pip来进行安装：pip install psutil安装完成后，就可以导入并开始使用psutil库了。import psutil获取系统信息获取CPU信息import psutil # 获取CPU核心数 cpu_count = psutil.cpu_count(logical=False) print(f"Physical CPU Count: {cpu_count}") # 获取逻辑CPU核心数（包括超线程） logical_cpu_count = psutil.cpu_count(logical=True) print(f"Logical CPU Count: {logical_cpu_count}") # 获取CPU使用率 cpu_usage = psutil.cpu_percent(interval=1, percpu=True) for i, usage in enumerate(cpu_usage, 1): print(f"CPU {i} Usage: {usage}%")获取内存信息import psutil # 获取内存总量 total_memory = psutil.virtual_memory().total print(f"Total Memory: {total_memory / (1024 ** 3):.2f} GB") # 获取可用内存 available_memory = psutil.virtual_memory().available print(f"Available Memory: {available_memory / (1024 ** 3):.2f} GB") # 获取内存使用率 memory_usage = psutil.virtual_memory().percent print(f"Memory Usage: 1.15MB%")获取磁盘信息import psutil # 获取所有磁盘分区信息 disk_partitions = psutil.disk_partitions() for partition in disk_partitions: print(f"Device: {partition.device}") print(f"Mountpoint: {partition.mountpoint}") print(f"Fstype: {partition.fstype}") print("") # 获取指定分区的使用情况 disk_usage = psutil.disk_usage("/") print(f"Total: {disk_usage.total / (1024 ** 3):.2f} GB") print(f"Used: {disk_usage.used / (1024 ** 3):.2f} GB") print(f"Free: {disk_usage.free / (1024 ** 3):.2f} GB") print(f"Usage: {disk_usage.percent}%")获取网络信息import psutil # 获取网络接口列表 network_interfaces = psutil.net_if_addrs() for interface, addresses in network_interfaces.items(): print(f"Interface: {interface}") for address in addresses: print(f" Family: {address.family}") print(f" Address: {address.address}") print(f" Netmask: {address.netmask}") print(f" Broadcast: {address.broadcast}") print("")管理进程查询进程信息import psutil # 获取所有进程列表 all_processes = psutil.process_iter(attrs=['pid', 'name']) for process in all_processes: print(f"Process ID: {process.info['pid']}, Name: {process.info['name']}") # 根据进程名称查询进程 process_name = "python3" process_list = [p.info for p in all_processes if p.info['name'] == process_name] for process in process_list: print(f"Process ID: {process['pid']}, Name: {process['name']}")创建新进程import psutil import subprocess # 创建新进程 subprocess.Popen(["notepad.exe"]) # 检查进程是否存在 process_name = "notepad.exe" is_running = any(process.info['name'] == process_name for process in psutil.process_iter(attrs=['name'])) print(f"{process_name} is running: {is_running}")终止进程import psutil # 根据进程名称终止进程 process_name = "notepad.exe" for process in psutil.process_iter(attrs=['name']): if process.info['name'] == process_name: process.terminate() print(f"Terminated {process_name}")监控系统性能监控CPU利用率import psutil import time # 监控CPU利用率 for _ in range(5): cpu_percent = psutil.cpu_percent(interval=1) print(f"CPU Usage: {cpu_percent}%") time.sleep(1)监控内存使用量import psutil import time # 监控内存使用量 for _ in range(5): memory_usage = psutil.virtual_memory().percent print(f"Memory Usage: 1.15MB%") time.sleep(1)监控磁盘IOimport psutil import time # 监控磁盘IO for _ in range(5): disk_io = psutil.disk_io_counters() print(f"Read Count: {disk_io.read_count}") print(f"Write Count: {disk_io.write_count}") time.sleep(1)监控网络流量import psutil import time # 监控网络流量 for _ in range(5): network_io = psutil.net_io_counters() print(f"Bytes Sent: {network_io.bytes_sent} bytes") print(f"Bytes Received: {network_io.bytes_recv} bytes") time.sleep(1)操作系统资源获取用户信息import psutil # 获取当前用户信息 user_info = psutil.users() for user in user_info: print(f"User: {user.name}") print(f"Terminal: {user.terminal}") print(f"Host: {user.host}") print("")获取虚拟内存信息import psutil # 获取虚拟内存信息 virtual_memory = psutil.virtual_memory() print(f"Total: {virtual_memory.total / (1024 ** 3):.2f} GB") print(f"Used: {virtual_memory.used / (1024 ** 3):.2f} GB") print(f"Free: {virtual_memory.free / (1024 ** 3):.2f} GB") print(f"Usage: {virtual_memory.percent}%")安全性和权限考虑 在使用psutil库时，需要注意安全性和权限问题。某些功能可能需要管理员权限或超级用户权限才能访问，例如终止进程、查询其他用户的进程等。确保你的程序以合适的权限运行，以避免潜在的问题。总结psutil库是Python中用于系统监控和管理的强大工具，它提供了丰富的功能来获取系统信息、管理进程、监控系统性能等。本文介绍了psutil库的基本用法和示例代码，帮助大家更好地理解和利用这个库。无论是用于系统管理、性能监控、自动化任务还是系统信息收集，psutil都是一个不可或缺的Python神器。
简介PhpSpreadsheet要求PHP 7.3及以上版本，是PHPExcel库的扩展版本，可用来读写xls、xlsx、CSV、HTML等格式文件。FormatReadingWritingOpen Document Format/OASIS (.ods)✓✓Office Open XML (.xlsx) Excel 2007 and above✓✓BIFF 8 (.xls) Excel 97 and above✓✓BIFF 5 (.xls) Excel 95✓ SpreadsheetML (.xml) Excel 2003✓ Gnumeric✓ HTML✓✓SYLK✓ CSV✓✓PDF (using either the TCPDF, Dompdf or mPDF libraries, which need to be installed separately) ✓PhpSpreadsheet安装方法可参考 禅道二次开发（四）：集成PhpSpreadsheet解析Excel文件 下面来介绍PhpSpreadsheet读写excel格式文件方法写入excel文件以写入Xlsx格式文件为例，<?php require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; /* 写入excel */ $fileName = './test.xlsx'; $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setCellValue('A1', 'Hello World !'); $sheet->setCellValue('C3', '你好 !'); $writer = new Xlsx($spreadsheet); $writer->save($fileName); ?>工作表默认样式可以设置工作表默认样式，比如$spreadsheet->getDefaultStyle()->getFont()->setName('宋体'); // 字体 $spreadsheet->getDefaultStyle()->getFont()->setSize(8); // 字体大小 $spreadsheet->getDefaultStyle()->getAlignment()->setWrapText(true); // 自动换行 $spreadsheet->getDefaultStyle()->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); //垂直居中单元格样式use PhpOffice\PhpSpreadsheet\Style\Alignment; $spreadsheet->getActiveSheet()->getStyle('A1:D4')->getAlignment()->setWrapText(true); // 自动换行 $caseSpreadsheet->getActiveSheet()->getStyle('A1:D4')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); //垂直居中单元格对齐方式包括水平对齐和垂直对齐：// 水平对齐样式 HORIZONTAL_GENERA HORIZONTAL_LEFT HORIZONTAL_RIGHT HORIZONTAL_CENTER HORIZONTAL_CENTER_CONTINUOUS HORIZONTAL_JUSTIFY HORIZONTAL_FILL HORIZONTAL_DISTRIBUTED // Excel2007 only // 垂直对齐样式 VERTICAL_BOTTOM VERTICAL_TOP VERTICAL_CENTER VERTICAL_JUSTIFY VERTICAL_DISTRIBUTED // Excel2007 only除了设置单元格的字体，还可以设置边框，填充颜色等样式信息。比如设置单元格背景颜色：$spreadsheet->getActiveSheet()->getStyle('E2')->getFill() ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) ->getStartColor()->setARGB('FFFF0000'); // 多个单元格，如果设置多个单元格推荐此方法，性能更优。 $spreadsheet->getActiveSheet()->getStyle('B3:B7')->getFill() ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) ->getStartColor()->setARGB('FFFF0000');注意：颜色代码为ARGB，带了Alpha通道。设置单元格高度，某一行高度：// 设置列宽 $spreadsheet->getActiveSheet() ->getColumnDimension('A') ->setWidth(30); // 自动列宽 $spreadsheet->getActiveSheet() ->getColumnDimension('A') ->setAutoSize(true); // 默认列宽 $spreadsheet->getActiveSheet() ->getDefaultColumnDimension() ->setWidth(12); 单元格数据类型use PhpOffice\PhpSpreadsheet\Cell\DataType; $spreadsheet->getActiveSheet()->setCellValueExplicit("A1", "123", DataType::TYPE_STRING); /* TYPE_STRING2 TYPE_STRING TYPE_FORMULA TYPE_NUMERIC TYPE_BOO TYPE_NULL TYPE_INLINE TYPE_ERROR */数字添加引号前缀：$spreadsheet->getActiveSheet()->setCellValueExplicit("A1", "123", DataType::TYPE_STRING); $spreadsheet->getActiveSheet()->getStyle("A1")->setQuotePrefix(true); // $spreadsheet->getActiveSheet()->getStyle("A1")->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT);设置数据有效性：use PhpOffice\PhpSpreadsheet\Cell\DataValidation; $objValidation = $spreadsheet->getActiveSheet()->getCell('C1')->getDataValidation(); // 设置数据有效性的单元格 $objValidation -> setType(DataValidation::TYPE_LIST) -> setErrorStyle(DataValidation::STYLE_INFORMATION) -> setAllowBlank(false) -> setShowInputMessage(true) -> setShowErrorMessage(true) -> setShowDropDown(true) -> setErrorTitle('错误提示') -> setError('您输入的值有误') -> setPromptTitle('结果') -> setFormula1('"成功,失败"');冻结单元格$sheet = $spreadsheet->getActiveSheet(); $sheet->freezePane('A2'); // 冻结第一行 $sheet->freezePane('B1'); // 冻结第一列 $sheet->freezePane('B3'); // 冻结B3单元格单元格条件格式可以设置单元格的条件格式，可以对满足某个条件的单元格设置样式，比如设置大于80的单元格：$conditional = new \PhpOffice\PhpSpreadsheet\Style\Conditional(); $conditional->setConditionType(\PhpOffice\PhpSpreadsheet\Style\Conditional::CONDITION_CELLIS); $conditional->setOperatorType(\PhpOffice\PhpSpreadsheet\Style\Conditional::OPERATOR_GREATERTHAN); $conditional->addCondition(80); $conditional->getStyle()->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_DARKGREEN); $conditional->getStyle()->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID); $conditional->getStyle()->getFill()->getStartColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); $conditionalStyles = $spreadsheet->getActiveSheet()->getStyle('A1:A10')->getConditionalStyles(); $conditionalStyles[] = $conditional; $spreadsheet->getActiveSheet()->getStyle('A1:A10')->setConditionalStyles($conditionalStyles);可以使用的条件及操作符：// Condition types CONDITION_NONE CONDITION_CELLIS CONDITION_CONTAINSTEXT CONDITION_EXPRESSION CONDITION_CONTAINSBLANKS CONDITION_NOTCONTAINSBLANKS CONDITION_DATABAR CONDITION_NOTCONTAINSTEXT // Operator types OPERATOR_NONE OPERATOR_BEGINSWITH OPERATOR_ENDSWITH OPERATOR_EQUA OPERATOR_GREATERTHAN OPERATOR_GREATERTHANOREQUAL OPERATOR_LESSTHAN OPERATOR_LESSTHANOREQUAL OPERATOR_NOTEQUAL OPERATOR_CONTAINSTEXT OPERATOR_NOTCONTAINS OPERATOR_BETWEEN OPERATOR_NOTBETWEEN写入图片将图片写入某个单元格中：$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); $drawing->setName('Logo'); $drawing->setDescription('Logo'); $drawing->setPath($diagramPath); $drawing->setHeight(120); $drawing->setCoordinates("D2"); $drawing->setOffsetX(0); $drawing->setRotation(0); $drawing->getShadow()->setVisible(true); $drawing->getShadow()->setDirection(0); $drawing->setWorksheet($spreadsheet->getActiveSheet());设置超链接给单元格设置超链接：$spreadsheet->getActiveSheet()->getCell('B2')->getHyperlink()->setUrl("sheet://'Sheetname'!A1"); // 当前文档位置 $spreadsheet->getActiveSheet()->getCell('B2')->getHyperlink()->setUrl("https://www.baidu.com/"); // 外链地址读取excel文件下面介绍读取excel文件方法。读取文本数据<?php require 'vendor/autoload.php'; use PhpOffice\ PhpSpreadsheet\ Spreadsheet; use PhpOffice\ PhpSpreadsheet\ Writer\ Xlsx; /* 读取excel */ // $reader = new Xlsx(); // $spreadsheet = $reader->load($fileName); $reader = IOFactory::createReader('Xlsx'); $spreadsheet = $reader - > load($fileName); // $reader->setReadDataOnly(true); // 设置后无法获取excel中的图片 $worksheet = $spreadsheet - > getActiveSheet(); // $worksheet = $spreadsheet->getSheetByName('testcase'); // $rawCasedata = $worksheet->toArray(); $highestRow = $worksheet - > getHighestRow(); // 取得总行数 $highestColumn = $worksheet - > getHighestColumn(); // 取得总列数 $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn); // 取得总列数 $excelData = []; for ($row = 1; $row <= $highestRow; $row++) { for ($col = 1; $col <= $highestColumnIndex; $col++) { $excelData[$row][] = (string) $worksheet - > getCellByColumnAndRow($col, $row) - > getValue(); } } echo " "; print_r($excelData); echo " "; ?>结果：Array ( [1] => Array ( [0] => Hello World ! [1] => [2] => ) [2] => Array ( [0] => [1] => [2] => ) [3] => Array ( [0] => [1] => [2] => 你好 ! ) )读取图片读取Excel文件中的图片，支持png、gif和jpg格式图片：<?php require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; $fileName = './test.xlsx'; $reader = IOFactory::createReader('Xlsx'); $spreadsheet = $reader->load($fileName); $worksheet = $spreadsheet->getActiveSheet(); /* 读取excel中的图片 */ $imgpath = './'; $imgArray = array(); foreach ($worksheet->getDrawingCollection() as $drawing) { list($startColumn, $startRow) = Coordinate::coordinateFromString($drawing->getCoordinates()); print_r($startColumn); print_r($startRow); switch ($drawing->getExtension()) { case 'jpeg': $source = imagecreatefromjpeg($drawing->getPath()); $imgname = $imgpath . $drawing->getCoordinates() . '.jpg'; imagejpeg($source, $imgname); break; case 'png': $source = imagecreatefrompng($drawing->getPath()); $imgname = $imgpath . $drawing->getCoordinates() . '.png'; imagepng($source, $imgname); break; default: echo "Unsupported file type: " . $drawing->getExtension() . "\n";; } }读取超链接读取超链接：$spreadsheet = $reader->load($fileName); $worksheet = $spreadsheet->getActiveSheet(); $spreadsheet->getActiveSheet()->getCell('A1')->hasHyperlink(); // 判断是否有超链接 $url = $spreadsheet->getActiveSheet()->getCell('A1')->getHyperlink()->getUrl(); // 读取超链接下载文件在服务器上创建了excel文件后，可以将它下载到客户端。// redirect output to client browser header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header("Content-Disposition: attachment;filename=$downloadFileName"); header('Cache-Control: max-age=0'); $writer = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); $writer->save('php://output');当然也可以下载已经生成的xlsx格式文件：$FileName = basename($FilePath); header("Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); header("Content-Disposition: attachment; filename=$FileName"); header("Content-length: " . filesize($FilePath)); header("Pragma: no-cache"); header("Expires: 0"); readfile("$FilePath");xls格式文件类型为：application/vnd.ms-excel。这里就介绍这么多了，PhpSpreadsheet的更多使用方法可参考官方文档：https://phpspreadsheet.readthedocs.io/en/latest/。版权声明：本文内容由互联网用户自发贡献，该文观点仅代表作者本人。本站仅提供信息存储空间服务，不拥有所有权，不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容， 请发送邮件至 举报，一经查实，本站将立刻删除。 原文出处：https://javaforall.cn/192523.html
Git 每次提交代码，都要写 Commit message（提交说明），否则就不允许提交。$ git commit -m "hello world"上面代码的 -m 参数，就是用来指定 commit mesage 的。如果一行不够，可以只执行 git commit ，就会跳出文本编辑器，让你写多行。$ git commit基本上，你写什么都行（这里，这里和这里）。但是，一般来说，commit message 应该清晰明了，说明本次提交的目的。目前，社区有多种 Commit message 的写法规范。本文介绍Angular 规范 ，这是目前使用最广的写法，比较合理和系统化，并且有配套的工具。一、Commit message 的作用格式化的Commit message，有几个好处。 （1）提供更多的历史信息，方便快速浏览。 比如，下面的命令显示上次发布后的变动，每个commit占据一行。你只看行首，就知道某次 commit 的目的。$ git log <last tag> HEAD --pretty=format:%s（2）可以过滤某些commit（比如文档改动），便于快速查找信息。 比如，下面的命令仅仅显示本次发布新增加的功能。$ git log <last release> HEAD --grep feature（3）可以直接从commit生成Change log。 Change Log 是发布新版本时，用来说明与上一个版本差异的文档，详见后文。二、Commit message 的格式每次提交，Commit message 都包括三个部分：Header，Body 和 Footer。<type>(<scope>): <subject> // 空一行 // 空一行 其中，Header 是必需的，Body 和 Footer 可以省略。不管是哪一个部分，任何一行都不得超过72个字符（或100个字符）。这是为了避免自动换行影响美观。2.1 HeaderHeader部分只有一行，包括三个字段： type （必需）、 scope （可选）和 subject （必需）。（1）type type 用于说明 commit 的类别，只允许使用下面7个标识。feat：新功能（feature） fix：修补bug docs：修改文档，比如README.md、CHANGELOG.md等；文档（documentation） style： 格式（不影响代码运行的变动）修改代码的格式，不影响代码运行的变动，比如空格、格式化代码、补齐句末分号等等； refactor：重构（没有新功能的添加以及bug修复的代码改动） perf：优化代码以提高性能 test：增加测试 build：修改影响项目构建文件或外部依赖项，比如npm、gulp、webpack、broccoli等 ci：修改CI配置文件和脚本； chore：构建过程或辅助工具的变动 revert：代码回退；如果 type 为 feat 和 fix ，则该 commit 将肯定出现在 Change log 之中。其他情况（ docs 、 chore 、 style 、 refactor 、 test ）由你决定，要不要放入 Change log，建议是不要。（2）scope scope 用于说明 commit 影响的范围，比如数据层、控制层、视图层等等，视项目不同而不同。（3）subject subject 是 commit 目的的简短描述，不超过50个字符。以动词开头，使用第一人称现在时，比如change，而不是changed或changes 第一个字母小写 结尾不加句号（.）2.2 BodyBody 部分是对本次 commit 的详细描述，可以分成多行。下面是一个范例。More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. Further paragraphs come after blank lines. - Bullet points are okay, too - Use a hanging indent有两个注意点。（1）使用第一人称现在时，比如使用 change 而不是 changed 或 changes 。（2）应该说明代码变动的动机，以及与以前行为的对比。2.3 FooterFooter 部分只用于两种情况。 （1）不兼容变动 如果当前代码与上一个版本不兼容，则 Footer 部分以 BREAKING CHANGE 开头，后面是对变动的描述、以及变动理由和迁移方法。BREAKING CHANGE: isolate scope bindings definition has changed. To migrate the code follow the example below: Before: scope: { myAttr: 'attribute', } After: scope: { myAttr: '@', } The removed `inject` wasn't generaly useful for directives so there should be no code using it.（2）关闭 Issue 如果当前 commit 针对某个issue，那么可以在 Footer 部分关闭这个 issue 。Closes #234也可以一次关闭多个 issue 。Closes #123, #245, #992 2.4 Revert还有一种特殊情况，如果当前 commit 用于撤销以前的 commit，则必须以 revert: 开头，后面跟着被撤销 Commit 的 Header。revert: feat(pencil): add 'graphiteWidth' option This reverts commit 667ecc1654a317a13331b17617d973392f415f02.Body部分的格式是固定的，必须写成 This reverts commit <hash>. ，其中的 hash 是被撤销 commit 的 SHA 标识符。如果当前 commit 与被撤销的 commit，在同一个发布（release）里面，那么它们都不会出现在 Change log 里面。如果两者在不同的发布，那么当前 commit，会出现在 Change log 的 Reverts 小标题下面。三、CommitizenCommitizen 是一个撰写合格 Commit message 的工具。安装命令如下。$ npm install -g commitizen然后，在项目目录里，运行下面的命令，使其支持 Angular 的 Commit message 格式。$ commitizen init cz-conventional-changelog --save --save-exact 以后，凡是用到 git commit 命令，一律改为使用 git c z。这时，就会出现选项，用来生成符合格式的 Commit message。四、validate-commit-msgvalidate-commit-msg 用于检查 Node 项目的 Commit message 是否符合格式。它的安装是手动的。首先，拷贝下面这个 JS 文件 ，放入你的代码库。文件名可以取为 validate-commit-msg.js 。接着，把这个脚本加入 Git 的 hook。下面是在 package.json 里面使用 ghooks ，把这个脚本加为commit-msg时运行。 "config": { "ghooks": { "commit-msg": "./validate-commit-msg.js" } }然后，每次 git commit 的时候，这个脚本就会自动检查 Commit message 是否合格。如果不合格，就会报错。$ git add -A $ git commit -m "edit markdown" INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" ! was: edit markdown五、生成 Change log如果你的所有 Commit 都符合 Angular 格式，那么发布新版本时， Change log 就可以用脚本自动生成。生成的文档包括以下三个部分。New features Bug fixes Breaking changes.每个部分都会罗列相关的 commit ，并且有指向这些 commit 的链接。当然，生成的文档允许手动修改，所以发布前，你还可以添加其他内容。conventional-changelog 就是生成 Change log 的工具，运行下面的命令即可。$ npm install -g conventional-changelog $ cd my-project $ conventional-changelog -p angular -i CHANGELOG.md -w上面命令不会覆盖以前的 Change log，只会在 CHANGELOG.m d的头部加上自从上次发布以来的变动。如果你想生成所有发布的 Change log，要改为运行下面的命令。$ conventional-changelog -p angular -i CHANGELOG.md -w -r 0为了方便使用，可以将其写入 package.json 的 script s字段。{ "scripts": { "changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0" } }以后，直接运行下面的命令即可。$ npm run changelog原文出处：https://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html