遇到乱码问题

在使用百度语音识别 JAVA Rest API 的时候,把应用部署到外部Tomcat,
发现返回的语音识别结果是乱码,而在IDEA上测试API,返回的结果正常。


百度语音识别 Rest API

短语音识别 简介
GitHub地址

处理、上传文件,获取识别结果。过程:

com/baidu/speech/restapi/asrdemo/ AsrMain.java

public static void main(String[] args) throws IOException, DemoException {
        AsrMain demo = new AsrMain();
        String result = demo.run();
        // 打印识别结果result
        log.info("识别结束:结果是:");
        log.info(result);
    }

public String run() throws IOException, DemoException {
		...
       	// 以Json方式上传文件,获取返回的字符串
            result = runJsonPostMethod(token);
        ...
        return result;
    }

// 默认以Json方式上传文件
// 注:本文作者添加:传入了参数 WAV文件路径+文件名
private String runJsonPostMethod(String token, String fileName) throws IOException, DemoException {         
	...
    byte[] content = getFileContent(fileName);
    String speech = base64Encode(content);
    ...
    // 从HttpURLConnection 获取返回的字符串
	String result = ConnUtil.getResponseString(conn);
	...
	return result;
}

// 将文件读取到FileInputStream,作为bytes返回
private byte[] getFileContent(String fileName) throws IOException{
    File file = new File(fileName);
    if (!file.canRead()) {
        log.error("文件不存在或者不可读: " + file.getAbsolutePath());
    }
    try(FileInputStream fileInputStream = new FileInputStream(file)) {
        // 将InputStream内的内容全部读取,作为bytes返回
        return ConnUtil.getInputStreamContent(fileInputStream);
    } catch (Exception e) {
        log.error("读取文件到输入流过程错误:" + e.getMessage(), e);
        return null;
    }
}

com/baidu/speech/restapi/common/ ConnUtil.java:

    // 从HttpURLConnection 获取返回的字符串
    public static String getResponseString(HttpURLConnection conn) throws IOException {
        // 从连接信息返回的内容
        return new String(getResponseBytes(conn));
    }

	// 从HttpURLConnection 获取返回的 bytes
    public static byte[] getResponseBytes(HttpURLConnection conn) throws IOException, DemoException {
        int responseCode = conn.getResponseCode();
        InputStream inputStream = conn.getInputStream();
        ...
        byte[] result = getInputStreamContent(inputStream);
        return result;
    }

    // 将InputStream内的内容全部读取,作为bytes返回
    public static byte[] getInputStreamContent(InputStream is) throws IOException {
        byte[] b = new byte[1024];
        // 定义一个输出流存储接收到的数据
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 开始接收数据
        int len = 0;
        while (true) {
            len = is.read(b);
            if (len == -1) {
                // 数据读完
                break;
            }
            byteArrayOutputStream.write(b, 0, len);
        }
        return byteArrayOutputStream.toByteArray();
    }

Rest API 的使用

String myApiKey = "myApiKey";
String mySecretKey = "mySecretKey";
String fileName = "D:/test.wav";
// 获得token: TokenHolder为API的 common 包中的类
TokenHolder holder =
        new TokenHolder(myApiKey, mySecretKey, "audio_voice_assistant_get");
holder.resfresh();
String token = holder.getToken();
// 默认以json方式上传音频文件
String result = runJsonPostMethod(token, fileName);

// 解析返回的json字符串
JsonParser jsonParser = new JsonParser();
// 将json字符串转化成json对象
JsonObject jsonObject = jsonParser.parse(result).getAsJsonObject();

// 返回结果中的错误码。若错误码为0,则识别成功。
String stringErrorNumber = jsonObject.get("err_no").getAsString();
// 返回结果中的语音识别文字段落
String stringErrorMessage = jsonObject.get("err_msg").getAsString();

int errorNumber;
if(stringErrorNumber != null) {
	errorNumber = Integer.valueOf(stringErrorNumber);
} else {
	errorNumber = 0;
}
// 根据错误码,进行简单分类
switch (errorNumber) {
	case 0:     // 识别成功
	    log.info("识别结束,结果是:" + jsonObject.get("result").getAsString());
	    break;
	case 3307:  // recognition error 识别出错,无法识别
		// TO-DO
	    break;
	case 3308:  // speech too long 音频文件时长过
	    // TO-DO 
	    break;
	default:	// 识别不成功
	    log.error("百度ASR结果错误:" + wavFullName
	        + ",错误代码:" + errorNumber
	        + ",错误信息:" + stringErrorMessage);
}

乱码现象和解决过程

1. 乱码现象

用1个时长不到60s的,符合百度短语音识别 REST API 格式的,WAV文件,用IDEA测试,结果:IDEA测试
可见,result 里边显示的是正常的中文识别结果。

打包应用,部署到外部Tomcat,运行测试,也识别成功,日志显示却是乱码:
识别结果乱码

2. 解决过程

尝试修改Tomcat编码,改 UTF-8,或者改GBK,都无效;
解决各种tomcat中文乱码问题
修复tomcat9.0中文乱码问题
tomcat配置及中文乱码问题的解决方案

在JAVA代码中,用 String(str.getBytes(“GBK”), “UTF-8”); 也无效:
知乎:java中GBK编码格式转成UTF8,用一段方法实现怎么做?

public static String recover(String str) throws Throwable {
	return new String(str.getBytes("GBK"), "UTF-8");
}

使用后,依旧乱码:

String jsonBack = new String(jsonBack.getBytes("GBK"), StandardCharsets.UTF_8);
log.info("ASR结果: " + jsonBack);

然后,某天在网上看到这两个图:
常见乱码问题和产生原因
聊天记录
搜了一下,发现相关文章:
[转载]字符乱码说明

看上去,本文遇上的是第一种情况:以GBK方式读取UTF-8编码的中文,百度API返回的结果就是UTF-8格式,这应该不用怀疑的,在IDEA内部Tomcat的UTF-8环境下测试都显示正常。但尝试过修改了外部Tomcat的编码,没用。干脆从JAVA代码切入,看看能不能修改一点代码就能解决。

在获取识别的返回结果中,寻找能够设置字符串String编码格式的地方。从最底层的
getResponseBytes(HttpURLConnection conn) 和 getInputStreamContent(InputStream is) 看起,两个函数中没发现字符串String。

继续往上,发现 getResponseString(HttpURLConnection conn) 里边有 new String:

// 从HttpURLConnection 获取返回的字符串
public static String getResponseString(HttpURLConnection conn) throws IOException {
    // 从连接信息返回的内容
    return new String(getResponseBytes(conn));
}

于是,直接尝试加入UTF-8编码格式:

public static String getResponseString(HttpURLConnection conn) throws IOException {
    // 从连接信息返回的内容,先用UTF-8解码
    return new String(getResponseBytes(conn), StandardCharsets.UTF_8);
}

部署到外部Tomcat测试,结果显示正常!!!

成功

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐