Java流内容替换

业务上有需求,需要将日语外字替换成指定字符,利用FilterInputStream实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

public class App {
public static void main(String[] args) throws Exception {
String str = "abc";
InputStream stream = new ByteArrayInputStream(str.getBytes());
// abc -> acc
replaceString(stream).stream().forEach( elt -> System.out.println(elt));
}

private static List<String> replaceString(InputStream inputStream) throws UnsupportedEncodingException {
List<String> result = new ArrayList<>();
FilterInputStream filterInputStream = new FilterInputStream(inputStream) {
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytesRead = super.read(b, off, len);
if (bytesRead != -1) {
for (int i = off; i < off + bytesRead - 1; i++) {
// ab -> ac
if (b[i] == (byte) 0x61 && b[i + 1] == (byte) 0x62) {
b[i] = (byte) 0x61;
b[i + 1] = (byte) 0x63;
}
}
}
return bytesRead;
}
};

BufferedReader br = new BufferedReader(new InputStreamReader(filterInputStream, "UTF8"));
br.lines().forEach( elt -> {
result.add(elt);
});
return result;
}
}

java外字处理爬坑

背景

日语内外字不在常规Unicode编码集内,需要手动更换成编码集内的文字。

坑点

外字在本地文档/服务器文档编码集符合文档提供的规则,但是在Stream流内会自动解析成\\uFFFD\\uxxxx。例如SJIS编码下F141的文字,在UTF8下编码为E08D,但是进入Stream流后变成了\\uFFFD\\u0041,所以要在代码内实际确认一下对应的编码。同时因为外字转成了\\uFFFD\\uxxxx,所以位数变成了2位,而且外字变换常规字符后可能会由一个字符变成多个字符,对应Byte的切分要注意位数变更。

相关代码段

打印字符串unicode编码

1
2
3
4
5
6
7
public static void printUnicode(String str) {
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
System.out.printf(String.format("\\u%04X ", (int) c));
}
System.out.println();
}

变换外字

1
2
3
4
5
6
7
8
9
10
Map<String, String> convertMap = new HashMap<>(); 
convertMap.put("\\uFFFD\\u0041", "さい");
convertMap.put("\\uFFFD\\u0042", "そね");

for (Map.Entry<String, String> entry : convertMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// repaceAll支持直接变换Unicode
String info = info.replaceAll(key, value);
}

时间戳四舍五入

因为项目原因,api返回的结果是6位毫秒(2023-04-12 20:56:31.589183),而存储到db然后导出是3位数毫秒(2023-04-12 20:56:31.589)。

这里牵扯到四舍五入,所以不能单纯截取,需要加500毫秒。

1
2
3
4
5
6
from datetime import datetime, timedelta
time_str = "2023-04-07 18:14:45.589183"
dt = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S.%f")
new_dt = dt + timedelta(microseconds=500)
new_time_str = new_dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
print(new_time_str)

ConfigParser读取properties

ConfigParser读取properties文件时,properties文件必须有默认的头,例如[default],如果没有会报错。

因为ConfigParser默认是读取ini格式文件,ini文件必须有section header。properties虽然也是key=value格式,但是不强制section header。

解决方式是读取内容后手动加上header,然后交给ConfigParser解析。

1
2
3
4
content = "[default]\n" + open(bathPath + "\\" + file).read()
config = ConfigParser(allow_no_value=True)
config.read_string(content)
value = config.get('default', key)

SpringBoot解决跨域问题

跨域问题

前后端分离的时候出现了跨域问题。。。。虽然可以用Jsonp的方式解决,但是axios推荐利用CORS方式解决。

解决方法

添加一个拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.wordcard.filter;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author 19745
*/
@Component
public class CORSFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "accept,x-requested-with,Content-Type");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, res);
}

@Override
public void destroy() {

}

}

Python3-enum枚举类使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from enum import Enum

# 声明枚举类
class Branch(Enum):
MAIN = "main branch"
DEV = "develop branch"


print(Branch.MAIN.name) # MAIN
print(Branch.MAIN.value) # main branch
print(Branch["MAIN"]) # Branch.MAIN
print(Branch("main branch")) # Branch.MAIN
print(Branch.MAIN) # Branch.MAIN

print(Branch.MAIN == Branch.MAIN) # True
print(Branch.MAIN == Branch.DEV) # False

print(Branch.MAIN is Branch.MAIN) # True

for branch in Branch:
print(branch)
# Branch.MAIN
# Branch.DEV

for branch in Branch.__members__:
print(branch)
# MAIN
# DEV

词云图生成

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import jieba
from wordcloud import WordCloud
from matplotlib.pyplot import imread

text = open("2020政府报告.txt").read()
font = "simhei.ttf"

# 背景图片
mask = imread("background.jpg")

cut = jieba.cut(text) # text为你需要分词的字符串/句子
result = ' '.join(cut) # 将分开的词用空格连接

# 剔除掉的关键词
exclude = {'我们', '今年', '同志', '我国'}

wc = WordCloud(collocations=False, # 避免重复单词
font_path=font, # 设置字体
mask=mask, # 设置背景
background_color="white",
# width=1400, height=1400, margin=2, # 图像宽高,字间距
stopwords=exclude)
wc.generate(result)
wc.to_file('2020政府报告.png')

引用模块

import jieba:中文词解析。
from wordcloud import WordCloud:词云图模块,用于生成词云图。
from matplotlib.pyplot import imread:图片加载。

数据源

background
2020政府报告

运行结果

设置背景版本

默认版本