利用VSCode构建简单的>$\LaTeX$<编辑器


起因

起因是因为要开始论文初稿的撰写, 然后需要使用>$\LaTeX$<. 虽然老板想让我在OverLeaf上写, 但是作为一个Vim党, 可能习惯了Vim的操作习惯以后就再也回不到纯文本的编辑上去了吧[笑]. 抛弃掉TexLive自带的TexWorks也是同理, VSCode的Vim插件总的来说还是挺好用的(虽然由于VSCode本身机制的问题会和一些插件不兼容, 比如acejump.

经过

现在是8102年了, 所以你需要的安装的是TexLive而不是CTex. 事实上, 对于中文现在TexLive也可以正常处理, 只要配置好字体就可以了1, 而且CTex也已经很久都没有更新过. 至于其他的发行版, 我只要求能用而且足够好用就可以了所以懒得圣战.

所以你需要的工具有:

安装方法非常简单:

  1. 安装VScode
  2. 安装TexLive2
  3. 安装Perl
  4. 打开终端运行tex看是否找到命令
  5. 安装VSCode的LaTeX Workshop插件
  6. 输入下列测试代码1保存:

    \documentclass[UTF8]{ctexart}
    \begin{document}
    你好,world!
    \end{document}
  7. 尝试运行左侧边栏的Build LaTeX Project - Recipe: latexmk/Recipe: pdflatex -> bibtex -> pdflatex*2应该都能够输出pdf
  8. 尝试运行左侧边栏的View LaTeX PDF - View in VSCode Tab/View in web browser就可以分别在VSCode的新标签页和浏览器打开了, 而且是会根据你编辑的内容实时刷新哟

其他的一些可能设置:

  • 输出到指定目录(比如./output)而不是根目录

    • 修改latex-workshop.latex.tools

          {
              "name": "latexmk",
              "command": "latexmk",
              "args": [
                  "-synctex=1",
                  "-interaction=nonstopmode",
                  "-file-line-error",
                  "-pdf",
                  "--outdir=output",
                  "%DOC%"
              ]
          },
    • 修改latex-workshop.latex.outputDir./output即可
  • 编译后清理掉中间文件

    • 修改latex-workshop.latex.tools

          {
              "name": "latexmk_build",
              "command": "latexmk",
              "args": [
                  "-synctex=1",
                  "-interaction=nonstopmode",
                  "-file-line-error",
                  "-pdf",
                  "--outdir=output",
                  "%DOC%"
              ]
          },
          {
              "name": "latexmk_clean",
              "command": "latexmk",
              "args": [
                  "-c",
                  "--outdir=output",
                  "%DOC%"
              ]
          },
    • 修改latex-workshop.latex.recipes

          {
              "name": "latexmk",
              "tools": [
                  "latexmk_build",
                  "latexmk_clean"
              ]
          },
  • 比如想要使用xelatex来排版

    • 打开VSCode的配置文件
    • latex-workshop.latex.recipes中加入如下内容:

      {
          "name": "xelatex",
          "tools": [
              "xelatex"
          ]
      },
    • latex-workshop.latex.tools中加入如下内容:

      {
          "name": "xelatex -> bibtex -> xelatex*2",
          "tools": [
              "xelatex",
              "bibtex",
              "xelatex",
              "xelatex"
          ]
      },
      {
          "name": "xelatex",
          "tools": [
              "xelatex",
          ]
      },
    • 重启VScode, 你就可以在Build LaTeX Project下看到新添加的Recipe了~

      xelatex -> bibtex -> xelatex*2用于处理需要bbl的情况, xelatex则用于纯>$\LaTeX$<环境下使用.
  • 在工作区排除掉LaTeX编译中间文件的显示

    • 加入如下配置到配置文件即可:

      "files.exclude": {
          "**/*.aux": true,
          "**/*.fdb_latexmk": true,
          "**/*.fls": true,
          "**/*.log": true,
          "**/*.synctex.gz": true,
          "**/*.bbl": true,
          "**/*.blg": true
      },
  • 补充一下自动clean的文件

    • latex-workshop.latex.clean.fileTypes中加入如下内容:

      "latex-workshop.latex.clean.fileTypes": [
          "*.aux",
          "*.bbl",
          "*.blg",
          "*.idx",
          "*.ind",
          "*.lof",
          "*.lot",
          "*.out",
          "*.toc",
          "*.acn",
          "*.acr",
          "*.alg",
          "*.glg",
          "*.glo",
          "*.gls",
          "*.ist",
          "*.fls",
          "*.log",
          "*.fdb_latexmk",
          "*.synctex.gz"
      ],
  • 其他的一些额外的有用的提示

    • 在VSCode的输出面板有LaTex WorkshopLaTeX Compiler两个输出选项, LaTex Workshop是LaTeX输出插件的日志, LaTeX Compiler是LaTeX编译器的输出日志
    • 如果没有pdf文件输出请检查编译器的日志, 查看VSCode是否找到了编译器是否正确编译
    • 如果有pdf文件, 但是pdf文件没有被打开请检查插件日志, 默认是打开主tex文件下同名pdf文件, 如果配置了输出目录需要修改配置latex-workshop.latex.outputDir

总结

VSCode不愧是一个全能型轻量级IDE, 通过各种插件的组合能够实现IDE的基本功能, 当然缺点主要是打开反应慢(和Sublime相比), 而且有些操作逻辑很迷(比如拖出标签页是保存文件???).

不过通过这一系列配置之后基本能实现实时预览和编写, 而且支持VScode完善的现代编辑器操作, 还是非常好评的w


如何通过反射从HttpServletRequest获取到HttpServletResponse对象(Tomcat限定


起因

项目会记录每次请求的进出便于调试和跟踪, 具体的实现是使用ServletRequestListenser的requestInitialized()和requestDestroyed()方法. 然后我们现在希望能够在请求出的时候同时记录到返回的状态码等信息, 这就需要获取到HttpServletResponse对象.

当然这个需求可能从一开始就是伪需求, 因为从Tomcat7开始就有提供AccessLog的记录, 使用Ngnix的访问记录也比在代码中去记录要好, 不过因为历史原因暂且就这样了╮( ̄▽ ̄)╭

发展

ServletRequestListener中的两个方法只提供了ServletRequestEvent参数, 然而你只能从这个类中获取到ServletRequest和ServletContext两个接口, 而在两个接口中是没有提供任何访问到Response的方法的, 因为这是由javax.serlvet包提供的接口.

然而事实上, 如果你使用的是Tomcat容器的话, 会发现这个接口在运行时的实现类是org.apache.catalina.connector.RequestFacade, 直接通过这个类我们依然获取不到Response, 但是它有一个类型为org.apache.catalina.connector.Request的属性, 而这个类他有一个getResponse的方法, 因此我们能通过这个类获取到Response对象.

1.png

事实上, org.apache.catalina.connector是位于tomcat根目录下lib目录下的catalina.jar, 出于部署方便的考虑, 我并不希望直接在工程中引入这个包, 所以就只能用万能的反射辣(x

Talk is Cheap. Show me the code.

    /**
     * 从Request中获取对应的Response的StatusCode
     * 采用了反射的方法, 可能只在限定版本的Tomcat中可用
     *
     * @param req Tomcat中的HTTPServletRequest对象
     * @return 提取出来的statusCode, 如果为-1, 说明提取出错
     */
    private Integer getStatusCode(HttpServletRequest req) {
        try{
            Field reqField = req.getClass().getDeclaredField("request");
            reqField.setAccessible(true);
            // org.apache.catalina.connector.Request
            Object reqObj = reqField.get(req);
            // org.apache.catalina.connector.Response
            Object resObj = reqObj.getClass().getDeclaredMethod("getResponse").invoke(reqObj);
            return (Integer)resObj.getClass().getDeclaredMethod("getStatus").invoke(resObj);
        }catch(NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e){
            e.printStackTrace();
        }
        return -1;
    }

总结

其实这是一个很尬而且很难受的操作, 但是有的时候出于各种考虑又需要进行这样的操作. 这个时候反射就是我们的好朋友啦.

事实上, 从调用栈可以看到Tomcat在connector中的service()方法中就会创建该次请求的request与response, 并互相关联. 只不过javax.servlet.http包中的接口没有给出相应的方法也是很尴尬→_→

    public void service(Request req, Response res) throws Exception {
        org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request)req.getNote(1);
        org.apache.catalina.connector.Response response = (org.apache.catalina.connector.Response)res.getNote(1);
        if (request == null) {
            request = this.connector.createRequest();
            request.setCoyoteRequest(req);
            response = this.connector.createResponse();
            response.setCoyoteResponse(res);
            request.setResponse(response);
            response.setRequest(request);
            req.setNote(1, request);
            res.setNote(1, response);
            req.getParameters().setQueryStringEncoding(this.connector.getURIEncoding());
        }
        //...
    }

记一次神奇的Manjaro文件丢失修复


事件起因

上次关闭了虚拟机之后, 手贱在Vmware里使用了"压缩磁盘以回收未使用的空间", 然后下次再启动的时候, 启动就报错了...
QQ图片20180808090659.jpg

故障现象

/sbin/init提示找不到libz.so.1文件找不到.
QQ图片20180808090624.png

故障分析

libz.so.1是zlib的库文件, 在系统中肯定是安装了的.

  • 联想到上次关机前, 我滚了一次系统, 首先想到是不是Arch又日常滚挂了, 于是打开了Arch官网...可是没有任何关于此的通知, 相比应该不是滚系统的原因.
  • 联想到我关机之后压缩了一下磁盘, 第二反应是不是文件系统挂了, 于是启动了livecd, 跑起了brtfsck, 然而它告诉我其实什么问题也没有.

恢复过程

于是这就很尴尬了...
既然文件系统本身没问题, 那就只好先恢复系统再说, 于是在livecd里把磁盘挂载, 然后find -name "libz.so.1"...
QQ图片20180808090634.png
诶...怎么只在/usr/lib32/下有libz.so.1, 看来应该是/usr/lib/下的文件丢失了导致的.
嗯 复制livecd的libz.so.1到/usr/lib/, 成功启动.

然后我需要重装一下zlib, 于是pacman -S zlib启动!
QQ图片20180808090641.png
emmmm 我的pacman发生了什么!
看来不仅仅是丢了一个libz.so.1的问题...好像zlib的软件包元数据也丢了Orz
更神奇的是, 我安装其他软件都没问题, 唯独zlib...emmmmm

群里有大佬建议说, 你不如用pacstrap安装一下试试看, 于是...
QQ图片20180808090648.png
emmmmm 还是不行啊Orz

思考了一下, 你不是说desc文件找不到么, 于是我去那个目录看了一下
emmmm 好像只剩下一个mtree文件了, desc和files都丢掉了...
于是我想要不干脆把这个文件夹删掉, 是不是就当作我从来没装过zlib了.
立刻mv /var/lib/pacman/local/zlib-1:1.2.11-3 ~/ && pacman -S zlib
QQ截图20180808093307.png
emmmm 好像是真的 虽然提示文件系统中已存在 这当然是因为我们已经安装过的原因

不过可以这样 pacman -S --dbonly zlib~
QQ截图20180808093429.png
成功! 这个时候再去pacman -S zlib已经一点问题也没有了yeah

总结与后记

  • pacman的软件包数据库真简单啊, 除了mtree是未知格式以外, desc和files都是纯文本文件
  • pacman的单个软件包数据库挂了完全一点都不影响到整个软件包仓库
  • 恢复完之后, 我发现上次关机前我滚过的包, 它又提醒我再滚一遍, 仿佛是一段时间内的磁盘写入完全丢失, 甚至还丢了别的文件
  • 如果真的丢了别的文件, 其实我也看不出来Orz
  • 所以既然用了brtfs, 那么就要常做snapshot啊_(:3」∠)_
  • 所以下次再也不手贱压缩磁盘了!!!

以上


使用Matplotlib画图时如何修改字体及显示汉字


前情提要

Matplotlib是python下一个非常常用的绘图库, 可是如果你用pip install matplotlib默认安装后, 却会发现它会把你输入的中文变成框框.
原因其实很简单, 只是因为默认的Matplotlib安装不带中文字体而已, 所以我们需要将添加中文字体进去.
当然还有可能的原因是你用的是python2, 对于utf-8的中文文本要进行一些别的非常恶心的操作, 当然这个不是本文的讨论范围.
以下文字的操作环境是Windows SubSystem for Linux, 但是原理在各个平台应该是通用的.

如何添加中文字体到Matplotlib中

Matplotlib的字体无论是在Linux还是在Windows中, 都和系统字体是完全独立的, 因此你需要手动将中文复制到其字体路径中去.
具体的路径位置和你当前使用python的site-packages路径有关, 在Linux中这个路径有可能在/usr/lib/python*下也有可能在~/.local/lib/python*下, 而在Windows中这个路径应该在你的python安装路径下.
那么具体的字体文件夹路径在哪里呢?假设$site-packages是你的site-packages路径, 那么字体目录在$site-packages/matplotlib/mpl-data/font下. 当然这个目录下一般会有afm pdfcorefonts ttf三个文件夹, 一般我们添加的都是ttf字体, 因此一般是添加到该目录下. 注意ttc字体放在这个目录下并不能被识别
比较推荐的代码字体有: 更纱黑体, 思源黑体.
更纱黑体是某个在微软工作的大佬正在开发而且不断在维护的一个字体, 效果很棒.
思源黑体是Adode开源的一个字体, 对于汉字和字幕都有着很好的效果.
总体来说, 这两个字体的显示效果差不多, 细节上略有差别, 具体的细节可以戳这里.

清空字体缓存

rm -rf ~/.cache/matplotlib

只有正确清空了字体缓存, 你的字体才会被Matplotlib再次识别. 对于Windows, 这个路径可能在C:\Users\$username\.matplotlib下.

查看已经添加在Matplotlib中的字体名

from matplotlib.font_manager import FontManager

fm = FontManager()
mat_fonts = set(f.name for f in fm.ttflist)
print(mat_fonts)

在Matplotlib中是使用字体名来配置字体的, 而不是字体的文件名, 因此你需要是从这里读到的字体名来进行下面的配置, 同时也可以查看之前的添加字体操作是否成功.

修改Matplotlib的字体配置

修改Matplotlib的字体配置, 有两种方法:
一种是我们可以在代码中修改配置;
另外一种是我们可以去修改Matplotlib的配置文件;

在代码中修改配置

通过plt.rcParams['$config_item']就可以修改Matplotlib的字体配置选项.
Matplotlib中一共有五种类型的字体, 分别是:

plt.rcParams['font.serif']=['Sarasa Mono SC'] # 衬线字体
plt.rcParams['font.sans-serif']=['Sarasa Mono SC'] # 无衬线字体
plt.rcParams['font.cursive']=['Sarasa Mono SC'] # 手写字体
plt.rcParams['font.fantasy']=['Sarasa Mono SC'] # 梦幻字体
plt.rcParams['font.monospace']=['Sarasa Mono SC'] # 等宽字体

直接修改配置文件

通过下面的代码可以读取到matplotlib的配置文件, 当然这个配置文件一般在$site-packages/matplotlib/mpl-data/matplotlibrc下:

import matplotlib

print(matplotlib.matplotlib_fname())

在这个文件搜索font.serif就会看到之前说的五大字体族了, 去掉前面的#号注释, 再在:前加上你的字体名就可以添加字体了的说.

最后的效果如图所示

QQ截图20180511151753.png


在WSL运行32位应用程序的神奇操作


在WSL运行32位应用程序的神奇操作

原理

主要的操作是通过qemu-i386-static模拟来运行x32程序, 然后通过binfmt来指定使用qemu来运行给定x32二进制文件

首先需要开启debian的x32基础支持

dpkg --add-architecture i386
apt-get update
apt-get install libstdc++6:i386

这会在dpkg中添加i386架构, 这样就可以通过apt工具安装i386的包了, 然后安装libstdc++会自动安装libc6/libgcc_s, 这样就可以运行基本的x32程序了.
一般的Linux到这一步就已经可以运行x32的程序了, 但是对于WSL来说还是不行, 使用ldd exec依然会告诉你这不是一个可执行文件, 于是说我们需要安装qemu.

安装qemu

apt-get update
apt-get install qemu-user-static

这样我们就可以通过qemu-i386-static exec来实际的运行我们想要的x32程序了, 但是这样很麻烦, 于是我们可以配置binfmt来自动使用qemu来运行qemu.

配置binfmt

update-binfmts --install i386 /usr/bin/qemu-i386-static --magic '\x7fELF\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x03\x00\x01\x00\x00\x00' --mask '\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xf8\xff\xff\xff\xff\xff\xff\xff'
service binfmt-support start

这样, 我们就可以像在正常Linux里面一样运行x32的程序了. 真是非常神奇的操作_(:3」∠)_

后记

当然每次启动要记得启动一下binfmt服务才行.

service binfmt-support start

Reference: https://github.com/Microsoft/WSL/issues/2468#issuecomment-374904520