404E Blog

第 5 页

UTC时间转北京时间

编程笔记
kotlin
fun main() {
    val formatPattern = "yyyy-MM-dd HH:mm:ss"
    val utc = "2019-07-10T16:00:00.000Z"
    val time = parse(utc, formatPattern)
    println(time)
}

fun parse(utc: String, formatPattern: String): String {
    val zdt = ZonedDateTime.parse(utc)
    val ldt = zdt.toLocalDateTime()
    val formatter = DateTimeFormatter.ofPattern(formatPattern)
    return formatter.format(ldt.plusHours(8))
}

参考 https://www.jianshu.com/p/62fa934ad2bc

如何查找Maven/Gradle依赖

开发工具

阿里云(推荐)

如何使用阿里云镜像

搜索依赖 https://developer.aliyun.com/mvn/search

搜索依赖有两种模式,一种是使用关键词的搜索,另一种是知道依赖的group和artifact的情况下精准搜索

关键词

精准搜索

精准搜索时可以只填一个参数模糊匹配

这里点击蓝色的依赖名字,可以打开如下界面

这里也可以直接生成maven的依赖(虽然没有生成gradle格式的依赖,但是在IDEA中直接复制到build.gradle中是可以自动转换格式的)

中央仓库(不翻墙需要过人机验证)

搜索依赖 https://mvnrepository.com/

首先搜索,找到自己要的依赖并打开

找到自己需要的版本打开

可以看到此处显示了依赖的详细信息,下方还会自动生成各种格式的依赖,可以直接复制到项目里

Gradle设置阿里云镜像源

开发工具

Gradle设置

首先找到Gradle的用户目录,默认情况下在 C:\Users\你的用户名\.gradle

如果设置了 GRADLE_HOME 环境变量的话就是你设置的目录

找到家目录中的 init.gradle 文件,如果没有就新建一个

在其中添加如下代码

纯文本
buildscript {
    repositories {
        maven {
            url 'https://maven.aliyun.com/repository/public'
        }
        maven {
            url 'https://maven.aliyun.com/repositories/jcenter'
        }
    }
}

allprojects {
    repositories {
        maven {
            url 'https://maven.aliyun.com/repository/public'
        }
        maven {
            url 'https://maven.aliyun.com/repositories/jcenter'
        }
    }
}

仓库名

简介

实际地址

使用地址

jcenter

JFrog公司提供的仓库

http://jcenter.bintray.com

https://maven.aliyun.com/repository/jcenter
https://maven.aliyun.com/nexus/content/repositories/jcenter

mavenLocal

本台电脑上的仓库

{USER_HOME}/.m2/repository

C:/Users/{USER_NAME}/.m2/repository (Windows)
/home/{USER_NAME}/.m2/repository (Linux)

mavenCentral

Sonatype公司提供的中央库

http://central.maven.org/maven2

https://maven.aliyun.com/repository/central
https://maven.aliyun.com/nexus/content/repositories/central

google

Google公司提供的仓库

https://maven.google.com

https://maven.aliyun.com/repository/google
https://maven.aliyun.com/nexus/content/repositories/google
https://dl.google.com/dl/android/maven2

jitpack

JitPack提供的仓库

https://jitpack.io

https://jitpack.io

public

jcenter和mavenCentral的聚合仓库

https://maven.aliyun.com/repository/public
https://maven.aliyun.com/nexus/content/groups/public

gradle-plugin

Gradle插件仓库

https://plugins.gradle.org/m2

https://maven.aliyun.com/repository/gradle-plugin
https://maven.aliyun.com/nexus/content/repositories/gradle-plugin

Ubuntu20.04安装&配置下载源/中文

运维部署

下载ISO & 安装

官网下载,这里选LTS(长期支持版)

这里我用的vmware虚拟机安装的ubuntu,如果要在实体机上安装ubuntu的话,可以使用ubuntu官网推荐的rufus

安装过程比较长并且没有什么需要操作的地方,跳过

配置下载源

安装好系统后,默认是英文,系统默认没有附带中文的语言文件,要更换语言就得下载。

下载之前首先要设置下载源,国内不配置下载源下载会非常慢(如果有弹窗让你更新的话先放着,等配置好下载源之后再更新)

关闭并且重载

等到进度条走完就完成了换源(如果有报错跳过即可)

更换系统语言

安装完成后

设置格式

设置完成后重启就是中文得了

不要忘了设置输入法 不要忘了设置输入法

其他设置

标准文件夹改名

将语言设置为中文后系统会询问是否改名标准文件夹,这里我选择不改

原因是之前用centos7(gnome桌面),中文输入法有时会失灵,无法输入中文,没办法进入中文名字的文件夹

自动息屏

时区

SU & ROOT

ubuntu默认不允许直接使用root账户登陆,需要使用时请使用 su 指令切换到 root 账户,使用完毕之后用 exit 指令退出

第一次使用 su 切换toot账户之前需要先为root账户设置密码

输入 sudo passwd 为root账户设置密码,输入指令后需要输入3此密码,第一次是当前账户的密码,后两次是设置root账户的密码,输入后不显示是正常情况

代理

wget的时候是不走系统代理的,解决办法

export https_proxy=192.168.48.1:7890 export http_proxy=192.168.48.1:7890 export ftp_proxy=192.168.48.1:7890

这里的 192.168.48.1:7890 是我本机的代理,使用时修改成你的代理地址

远程连接/SSH

Ubuntu默认带了openssh-client,但是没有openssh-server

直接 apt install -y openssh-server 会提示

这里我们直接卸载已有的openssh-clileht

apt remove -y openssh-client

然后再安装openssh-server

apt install -y openssh-server

ps: 这里我使用的root账户安装,若是普通账户请在指令前添加 sudo

sudo apt remove -y openssh-client

sudo apt install -y openssh-server

使用Gson序列化和反序列化

编程笔记

引入依赖

Gradle

纯文本
implementation 'com.google.code.gson:gson:2.8.8'

Maven

xml
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.8</version>
</dependency>

示例代码

java
package com.e404.test

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.io.FileInputStream
import java.io.FileOutputStream

fun main() {
    // 反序列化
    val stu: Student = FileInputStream("in.json").use {
        it.bufferedReader().use { br ->
            Gson().fromJson(br, Student::class.java)
        }
    }
    println("stu.name: ${stu.name}")
    println("stu.age: ${stu.age}")
    // 序列化
    FileOutputStream("out.json").use {
        it.bufferedWriter().use { bw ->
            // 要写入的json字符串
            // setPrettyPrinting 是生成带缩进的字符串
            val str: String = GsonBuilder().setPrettyPrinting().create().toJson(stu)
            bw.write(str)
        }
    }
}

/**
 * 必须要有无参的构造方法和每个参数的getter和setter
 *
 * (idea下setter可以用alt+insert快速生成)
 *
 * 否则会报错
 */
class Student(var name: String = "", var age: Int = 0)

使用SnakeYaml序列化和反序列化

编程笔记

SnakeYaml官网:http://www.snakeyaml.org/

引入依赖

Gradle

纯文本
implementation 'org.yaml:snakeyaml:1.29'

Maven

xml
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.29</version>
</dependency>

使用示例

java
package com.e404.test

import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.nodes.Tag
import java.io.FileInputStream
import java.io.FileOutputStream

fun main() {
    // 反序列化
    val stu: Student = FileInputStream("in.yml").use {
        Yaml().loadAs(it, Student::class.java)
    }
    println("stu.name: ${stu.name}")
    println("stu.age: ${stu.age}")
    // 序列化
    FileOutputStream("out.yml").use {
        it.bufferedWriter().use { bw ->
            // 字符串 (使用dumpAs以移除bean标签)
            val str: String = Yaml().dumpAs(stu, Tag.MAP, DumperOptions.FlowStyle.BLOCK)
            bw.write(str)
        }
    }
}

/**
 * 必须要有无参的构造方法和每个参数的getter和setter
 *
 * (idea下setter可以用alt+insert快速生成)
 *
 * 否则会报错
 */
class Student(var name: String = "", var age: Int = 0)

使用zxing生成&识别二维码

编程笔记

GitHub

zxing/zxing

引入依赖

Gradle

纯文本
implementation 'com.google.zxing:core:3.4.1'

Maven

xml
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.4.1</version>
</dependency>

示例代码

kotlin
import com.google.zxing.*
import com.google.zxing.common.HybridBinarizer
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import java.awt.geom.AffineTransform
import java.awt.image.BufferedImage


object QrUtil {
    private val mfw = MultiFormatWriter()
    private val encodeHints = object : HashMap<EncodeHintType, Any>() {
        init {
            put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H)
            put(EncodeHintType.CHARACTER_SET, Charsets.UTF_8)
            put(EncodeHintType.MARGIN, 1)
        }
    }
    
    private val decodeHints = object : HashMap<DecodeHintType, Any>() {
        init {
            put(DecodeHintType.CHARACTER_SET, Charsets.UTF_8)
            //优化精度
            put(DecodeHintType.TRY_HARDER, true)
            //复杂模式,开启PURE_BARCODE模式
            put(DecodeHintType.PURE_BARCODE, true)
        }
    }

    /**
     * 生成二维码
     *
     * 若宽/高小于生成的二维码的最小宽度则会忽略此宽/高度, 否则会生成此宽/高度的二维码图片
     *
     * @param content 正文
     * @param width 目标图片宽度
     * @param height 目标图片高度
     * @param color1 二维码颜色
     * @param color2 二维码背景
     * @return 二维码图片
     */
    @Throws(WriterException::class)
    fun getQrImage(
        content: String,
        width: Int,
        height: Int,
        color1: Int,
        color2: Int,
    ): BufferedImage {
        val bitMatrix = mfw.encode(content, BarcodeFormat.QR_CODE, width, height, encodeHints)
        val w = bitMatrix.width
        val h = bitMatrix.height
        val image = BufferedImage(w, h, 1)
        for (x in 0 until w) for (y in 0 until h) image.setRGB(x, y, if (bitMatrix[x, y]) color1 else color2)
        return image
    }

    /**
     * 识别二维码
     *
     * @param image 要识别的图片
     * @return 识别的结果
     */
    @Throws(NotFoundException::class)
    fun decode(image: BufferedImage): String {
        val source = BufferedImageLuminanceSource(image)
        val bitmap = BinaryBitmap(HybridBinarizer(source))
        return MultiFormatReader().decode(bitmap, decodeHints).text
    }

    class BufferedImageLuminanceSource(
        image: BufferedImage,
        left: Int = 0,
        top: Int = 0,
        width: Int = image.width,
        height: Int = image.height,
    ) :
        LuminanceSource(width, height) {
        private val image: BufferedImage
        private val left: Int
        private val top: Int
        override fun getRow(y: Int, rowBytes: ByteArray): ByteArray {
            var row = rowBytes
            require(!(y < 0 || y >= height)) { "Requested row is outside the image: $y" }
            if (row.size < width) row = ByteArray(width)
            image.raster.getDataElements(left, top + y, width, 1, row)
            return row
        }

        override fun getMatrix(): ByteArray {
            val width = width
            val height = height
            val area = width * height
            val matrix = ByteArray(area)
            image.raster.getDataElements(left, top, width, height, matrix)
            return matrix
        }

        override fun isCropSupported() = true
        override fun crop(left: Int, top: Int, width: Int, height: Int) =
            BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height)

        override fun isRotateSupported() = true

        override fun rotateCounterClockwise(): LuminanceSource {
            val sourceWidth = image.width
            val sourceHeight = image.height
            val transform = AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth.toDouble())
            val rotatedImage = BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY)
            val g = rotatedImage.createGraphics()
            g.drawImage(image, transform, null)
            g.dispose()
            val width = width
            return BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), height, width)
        }

        init {
            val sourceWidth = image.width
            val sourceHeight = image.height
            require(!(left + width > sourceWidth || top + height > sourceHeight)) { "Crop rectangle does not fit within image data." }
            for (y in top until top + height) {
                for (x in left until left + width) {
                    if (image.getRGB(x, y) and -0x1000000 == 0) {
                        image.setRGB(x, y, -0x1) // = white
                    }
                }
            }
            this.image = BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY)
            this.image.graphics.drawImage(image, 0, 0, null)
            this.left = left
            this.top = top
        }
    }
}

Win文件右键菜单添加自定义打开方式

系统笔记

目标

给所有文件的右键菜单添加"通过 Photoshop 打开"选项

步骤

打开注册表编辑器

打开开始菜单输入regedit或者win+r打开运行并输入regedit回车

找到对应路径

路径为 计算机\HKEY_CLASSES_ROOT\*\shell

在上方的搜索栏直接复制粘贴然后回车跳转

添加

在shell项上右键,点击 新建 -> 项

给新建的项目起一个名字,我这里起的是Photoshop(可以起任意名字,此名字就是修改完成后右键菜单上看到字样)

然后在这个新建的项目上再右键新建一个项(操作同上),重命名为command(这里的名字必须是command

点击选中command,可以看到右侧显示是数值未设置

这里我们双击左侧图标或者 (默认) ,打开如下界面

在数值数据中写入

"D:\app\Adobe Photoshop 2021\Photoshop.exe" "%1"

这里我写的是自己的ps安装路径,使用时修改为自己的ps路径,格式同上,引号不可省略

到这里我们就已经可以在所有文件的右键菜单上看到之前设置的选项了

设置图标和显示字样

上图我们可以看到上下两个打开都是有图标的,我们这个显得很low而且没有辨识度,格式也与其他的不一致

这里我们需要设置图标和显示字样

首先是设置图标

新建一个字符串值,并且命名为 Icon

设置其值为ps路径(这里也是一样,设置为你的ps路径)

修改完成后我们就可以看到,文件右键菜单上的Photoshop上已经有了图标

下一项是修改显示的字样

在注册表的Photoshop项上,有一个 默认

将其修改为想要显示的字样

到此为止,在右键新增打开方式的步骤就已经结束了,下面放一张效果图

给某种文件单独添加右键项目

在注册表下的 计算机\HKEY_CLASSES_ROOT 里面,还有很多 . 开头的项目,每个项目代表一个文件后缀名(也就是右键那个后缀名的文件时显示的右键菜单项目)

找到你要修改的文件后缀名,这里我用 .txt 做演示

点击 .txt ,复制 (默认) 项中的数据

ctrl+f打开搜索,在查找目标中输入上面复制的数据,并且在下面的查看中设置为只勾选项,然后搜索

找到之后大概是这样的

这里的awa项就是新加的,此时在txt文件上右键时显示

具体修改方法按上面的对应所有文件右键菜单的方式修改,这里就不再赘述

Java中获取到的颜色是负数

编程笔记

这几天写图片处理相关的代码的时候遇到个问题,BufferedImage.getRGB()的返回值有时会是负数

百度一番之后找到了原因:

颜色的取值范围0x00000000-0xffffffff,会超过int类型的最大值

为了能让其正常取值,超过int最大值的会变成 他本身 - 1 - 0xffffffff

这里我写了一段转化的代码供参考

kotlin
/**
 * 处理颜色补码(- -> +)
 * 将可能为负的颜色转成正数的Long
 *
 * @param color 可能为负数的颜色
 * @return 经过转换的颜色
 */
fun fromRgb(color: Int): Long {
    return (if (color < 0) 0xffffffff + color + 1 else color).toLong()
}

/**
 * 处理颜色补码(+ -> -)
 * 将长度超过Int的颜色转成可能为负的Int颜色
 *
 * @param color 正数颜色, Long
 * @return 经过转换的颜色
 */
fun toRgb(color: Long): Int {
    return (if (color > Int.MAX_VALUE) (color - 1 - 0xffffffff) else color).toInt()
}