[Java/HexStringReader] 核心源码精讲:java.io.StringReader类(JDK1.1-)
概述:java.io.StringReader
java.io包的StringReader类可用于从字符串读取数据(以字符为单位)。
- 它继承了抽象类
java.io.Reader。- 自 JDK 1.1 起便支持

注意:在
StringReader中,指定的字符串充当源,从其中分别读取字符。
创建StringReader对象
- 为了创建一个
StringReader,我们必须首先导入java.io.StringReader包。
导入包后,就可以创建字符串读取器了。
//创建 StringReader
StringReader input = new StringReader(String data);
在这里,我们创建了一个
StringReader,它从指定的名为data的字符串中读取字符。
StringReader的方法
StringReader类为Reader类中的不同方法提供了实现。
read(...)方法
read(...)
read()- 从字符串读取器读取单个字符read(char[] array)- 从阅读器读取字符并将其存储在指定的数组中read(char[] array, int start, int length)- 从阅读器读取等于length字符的数量,并从start位置开始存储在指定的数组中
示例:Java StringReader
import java.io.StringReader;
public class Main {
public static void main(String[] args) {
String data = "This is the text read from StringReader.";
//创建一个字符数组
char[] array = new char[100];
try {
//创建一个StringReader
StringReader input = new StringReader(data);
//使用read方法
input.read(array);
System.out.println("从字符串读取数据:");
System.out.println(array);
input.close();
} catch(Exception e) {
e.getStackTrace();
}
}
}
out
从字符串读取数据:
This is the text read from StringReader.
skip(...)方法
skip()方法
要丢弃和跳过指定数量的字符,可以使用
skip()方法。
例如
import java.io.StringReader;
public class Main {
public static void main(String[] args) {
String data = "This is the text read from StringReader";
System.out.println("原始数据: " + data);
//创建一个字符数组
char[] array = new char[100];
try {
//创建 StringReader
StringReader input = new StringReader(data);
//使用 skip() 方法
input.skip(5);
//使用 read 方法
input.read(array);
System.out.println("跳过5个字符后的数据:");
System.out.println(array);
input.close();
}
catch(Exception e) {
e.getStackTrace();
}
}
}
out
原始数据: This is the text read from the StringReader
跳过5个字符后的数据:
is the text read from the StringReader
在上面的示例中,我们使用
skip()方法从字符串读取器中跳过5个字符。因此,字符'T'、'h'、'i'、's'和' '从原始字符串读取器中被跳过。
close()方法
- 要关闭字符串阅读器,我们可以使用该
close()方法。
调用
close()方法后,我们将无法使用读取器从字符串读取数据。
其他方法
| 方法 | 描述 |
|---|---|
| ready() | 检查字符串读取器是否准备好被读取 |
| mark() | 标记读取器中已读取数据的位置 |
| reset() | 重置标记,返回到阅读器中设置标记的位置 |
最佳实践
基于StringReader自定义的HexStringReader
基础依赖: BytesUtils
DataTypeEnum
package com.xxx.sdk.utils.bytes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 数据类型枚举类
* @updateTime 2025.7.15 11:19
*/
public enum DataTypeEnum {
//字符串
STRING_TYPE("STRING_TYPE", "S", String.class, "string type"),
//整型
LONG_TYPE("LONG_TYPE", "L", Long.class, "long type"),
//IEEE 754 单精度浮点型
FLOAT_TYPE("FLOAT_TYPE", "F", Float.class, "single float type"),
//IEEE 754 双精度浮点型
DOUBLE_TYPE("DOUBLE_TYPE", "D", Double.class, "double float type"),
//整型数组
//INT_ARRAY_TYPE("INT_ARRAY_TYPE", "IntArray", Integer.class, "int array type"),
//原始类型 (Origin Raw Data Type, 原来是什么,就保留成什么,不需要解析)
RAW_TYPE("RAW_TYPE", "OR", null, "Origin Raw Data Type");
//public static final String STRING_TYPE = "S";
//public static final String LONG_TYPE = "L";
//public static final String FLOAT_TYPE = "F";
//public static final String DOUBLE_TYPE = "D";
////public static final String INT_ARRAY_TYPE = "IntArray";
//public static final String RAW_DATA_TYPE = "OR";
private String name;
private String code;
private Class javaClass;
private String description;
public final static String CODE_PARAM = "code";
public final static String NAME_PARAM = "name";
DataTypeEnum(String name, String code, Class javaClass, String description) {
this.name = name;
this.code = code;
this.javaClass = javaClass;
this.description = description;
}
public static DataTypeEnum findByCode(String code) {
for (DataTypeEnum type : values()) {
if (type.getCode().equals(code)) {
return type;
}
}
return null;
}
public static DataTypeEnum findByName(String name) {
for (DataTypeEnum type : values()) {
if (type.getName().equals(name)) {
return type;
}
}
return null;
}
public static List<Map<String, DataTypeEnum>> toList() {
List<Map<String, DataTypeEnum>> list = new ArrayList();//Lists.newArrayList()其实和new ArrayList()几乎一模
for (DataTypeEnum item : DataTypeEnum.values()) {
Map<String, DataTypeEnum> map = new HashMap<String, DataTypeEnum>();
map.put(DataTypeEnum.CODE_PARAM, item);
list.add(map);
}
return list;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
public Class getJavaClass() {
return javaClass;
}
public String getDescription() {
return description;
}
}
ISourceReader
package com.xxx.sdk.utils.bytes;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* 解析字节数据的抽象阅读器接口
* @updateTime 2025.7.15 11:19
* @param <T>
*/
public interface IBytesReader<T> {
public final static Charset CHARSET_DEFAULT = StandardCharsets.UTF_8; //Charset.forName("UTF-8");
/**
* 解析字节数据
* @note
* 1. 原方法名: getParserStr
* 2. 现方法名: parseBytes | change at 2025.4.9. 09:45 AM by johnny
* @param readBytesLength 待读取的数据所占用字节数
* @param dataType 数据类型 (S:string、L:long、OR:不处理)
* @param comment 备注说明 便于定位问题
* @return
* @throws IOException
*/
Object parseBytes(int readBytesLength, DataTypeEnum dataType, Charset charset, String comment) throws IOException;
public static Object parseBytes(ISourceReader reader, String readBytesLength, String dataType, String comment) throws IOException {
return parseBytes(reader, readBytesLength, dataType, reader.getCharset(), comment);
}
public static Object parseBytes(ISourceReader reader, String readBytesLength, String dataType, Charset charset, String comment) throws IOException {
Integer readBytesLengthInt = Integer.parseInt(readBytesLength);
DataTypeEnum dataTypeEnum = DataTypeEnum.findByCode(dataType);
Object result = reader.parseBytes(readBytesLengthInt, dataTypeEnum, charset, comment);
return result;
}
int next();
Boolean hasNext();
public T getSource();
Charset getCharset();
void setCharset(Charset charset);
T getLatestReadData();
}
HexStringReader
package com.xxx.sdk.utils.bytes;
import lombok.NonNull;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Unsafe;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* 基于 StringReader(字符(串)流阅读器) 的 十六进制字符串读取器
* @updateTime 2025.7.15 10:47
* @note 原名: SourceStringReader
* @note 所谓16进制字符串(HexString),形如: "0123A1B",而非 "0x0123A1B"
*/
public class HexStringReader extends StringReader implements IBytesReader<String> {
private final static Logger log = LoggerFactory.getLogger(HexStringReader.class);
public static final String SPLIT = IBytesReader.SPLIT;
private String string;
/**
* 已读取的字符数量
*/
private int hasReadSize = 0;
/**
* 字符集
* @note 解析 String 类型的字节数据时使用
*/
private Charset charset = CHARSET_DEFAULT;
/**
* 最近一次操作读取到的字节数据
*/
private String latestReadData = null;
public HexStringReader(@NonNull String string, Charset charset) {
super((String) Objects.requireNonNull(string));
this.string = string;
this.charset = charset==null?CHARSET_DEFAULT:charset;
}
public HexStringReader(@NonNull String string) {
this(string, CHARSET_DEFAULT);
}
@NonNull
@Override
public String getSource() {
return this.string;
}
public void close() {
super.close();
string = null;
}
@Override
public int read() throws IOException {
int readSize = super.read();
//hasReadSize += readSize;
return readSize;
}
@Override
public int read(CharBuffer var1) throws IOException {
int readSize = super.read(var1);
//hasReadSize += readSize;
return readSize;
}
@Override
public int read(char[] var1) throws IOException {
int readSize = super.read(var1);
//hasReadSize += readSize;
return readSize;
}
@Override
public int read(char[] var1, int var2, int var3) throws IOException {
int readSize = super.read(var1, var2, var3);
hasReadSize += readSize;
return readSize;
}
@Override
public long skip(long var1) throws IOException {
long readSize = super.skip(var1);
hasReadSize += readSize;
return readSize;
}
// public static String parseBytes(SourceStringReader reader, String readByteSize, String dataType) throws IOException {
// StringBuilder s = new StringBuilder();
// if (Integer.parseInt(readByteSize) * 2L > reader.getSource().length()) {
// //报文长度异常
// logger.error("message length exception!data={},len={}", reader.getSource(), readByteSize);
// throw new IOException();
// }
//
// int charSize = Integer.parseInt(readByteSize) * 2;
// char[] data = new char[ charSize ];
// reader.read(data);
// String s1 = new String(data);
// //System.out.println(s1);
//
// switch (dataType) {
// //Long 类型解析
// case LONG_TYPE:
// s.append(Long.parseLong(s1, 16));
// break;
// //String 类型解析
// case STRING_TYPE:
// s.append(BytesUtils.hexStringToTextString(s1));
// break;
// //IntArray 类型解析
// //case INT_ARRAY_TYPE:
// //s.append( BytesUtils.hexStringToIntegerArrayString( s1 , BytesUtils.INTEGER_ARRAY_STRING_SEPARATOR ) );
// //break;
// //原始类型,不需要解析
// case RAW_DATA_TYPE:
// s.append(s1);
// break;
// default:
// break;
// }
// String str = s.toString();
// s.setLength(0);
// return str;
// }
// public static String parseBytes(SourceStringReader reader, String length, String dataType) throws IOException {
// return parseBytes( reader, length, dataType, null);
// }
public static String parseBytes(HexStringReader reader, String readBytesLength, String dataType, String comment) throws IOException {
return parseBytes(reader, readBytesLength, dataType, reader.getCharset(), comment);
}
public static String parseBytes(HexStringReader reader, String readBytesLength, String dataType, Charset charset, String comment) throws IOException {
Integer readBytesLengthInt = Integer.parseInt(readBytesLength);
DataTypeEnum dataTypeEnum = DataTypeEnum.findByCode(dataType);
String result = reader.parseBytes(readBytesLengthInt, dataTypeEnum, charset, comment);
return result;
}
@Override
public String parseBytes(int readBytesLength, DataTypeEnum dataType, Charset charset, String comment) throws IOException {
comment = isEmpty(comment)?"":comment;
//step1 判断长度
if (readBytesLength * 2L > this.getSource().length()) {
log.error("{} | raw data's bytes length is abnormal!readBytesLength={},dataType={}, data={}", comment, readBytesLength,dataType, this.getSource() );//报文长度异常
throw new IOException();
}
//step2 从 reader 读取数据,存入 data:char []
char[] rawData = new char[readBytesLength * 2];
this.read(rawData);
//step3 data:char [] 转 String
// [demo] length = 2, dataType = "L", hexString = "0014", next = 4( StringReader 已读取 4 个 char,待读取第 1+4 个(即 下标为 4) 的 char )
// [demo] length = 4, dataType = "L", hexString = "64E85504", next = xx
String rawDataHex = new String(rawData);
this.latestReadData = rawDataHex;
if(log.isDebugEnabled()){//原则上不建议打开 | 注: next() 是基于反射机制获取的
log.debug("{}| rawDataHex:{}, dataType:{}, next:{}", comment, rawDataHex, dataType, this.next());
}
//step4 按照不同类型解析数据
String result = null;
try {
result = hexStringConvert( rawDataHex, dataType, charset );
} catch (Exception exception) {//先捕获异常,打印具体的日志信息(便于事后定位);再上抛异常
log.error("{}| Fail to convert the hex string({}) to data type({})!next:{}, exception.message:{}", comment, rawDataHex, dataType, this.next(), exception.getMessage() , this.next(), exception);
if( log.isDebugEnabled() ) {//仅调试模式下,打印该内容
log.error( "{}| hex-string:{} | data-type:{} | next:{} | source-string:{}", comment, rawDataHex, dataType, this.next(), this.getSource());
}
throw new RuntimeException(exception);
}
return result;
}
@Deprecated
private static String hexStringConvert(String hexString , String dataType, Charset charset){
DataTypeEnum dataTypeEnum = DataTypeEnum.findByCode( dataType );
return hexStringConvert( hexString, dataType, charset );
}
/** * 16进制字符串 转 Long / String / OR 的数据类型
* @return
*/
private static String hexStringConvert(String hexString , DataTypeEnum dataType, Charset charset){
StringBuilder builder = new StringBuilder();
byte [] bytes = null;
String errorMessage = null;
String result;
try {
switch (dataType) {
//Long 类型解析
case LONG_TYPE:
builder.append(Long.parseLong(hexString, 16));
break;
//Float 类型解析
case FLOAT_TYPE:
bytes = BytesUtils.hexStringToBytes( hexString );
builder.append( BytesUtils.bytesToFloat( bytes ) );//eg: "1.23"
break;
//Double 类型解析
case DOUBLE_TYPE:
bytes = BytesUtils.hexStringToBytes( hexString );
builder.append( BytesUtils.bytesToDouble( bytes ) );
break;
//String类型解析
case STRING_TYPE:
//builder.append(hexStringToString(hexString));
builder.append( BytesUtils.hexStringToTextString(hexString, charset) );
break;
//IntArray 类型解析
//case INT_ARRAY_TYPE:
//s.append( BytesUtils.hexStringToIntegerArrayString( s1 , BytesUtils.INTEGER_ARRAY_STRING_SEPARATOR ) );
//break;
//原始类型,不需要解析
case RAW_TYPE:
builder.append(hexString);
break;
default:
break;
}
result = builder.toString();
builder.setLength(0);//置空
} catch (Exception exception) {
errorMessage = String.format("Fail to convert the hex string(%s) to data type(%s)!", hexString, dataType);
log.error(errorMessage + ", exception:", exception);
throw new RuntimeException(errorMessage, exception);
}
if(log.isDebugEnabled()){
log.debug("Convert the hex string({}) to data type({}) success!result:{}", hexString, dataType, result);
}
return result;
}
private static String hexStringConvert(String hexString , DataTypeEnum dataType){
return hexStringConvert(hexString, dataType, CHARSET_DEFAULT);
}
// public String getParserStrV1(SourceStringReader reader, int a, String b) throws IOException {
// StringBuilder s = new StringBuilder();
// if (a * 2L > reader.getSource().length()) {
// log.error("报文长度异常,data={},len={}", reader.getSource(), a);
// throw new IOException();
// }
//
// char[] data = new char[a * 2];
// reader.read(data);
// String s1 = new String(data);
//
// switch (b) {
// //Long类型解析
// case "L":
// s.append(Long.parseLong(s1, 16));
// break;
// //String类型解析
// case "S":
// s.append(BytesUtils.hexStringToTextString(s1));
// break;
// //原始类型,不需要解析
// case "OR":
// s.append(s1);
// break;
// default:
// break;
// }
// return s.toString();
// }
//
// public String getParserStrOR(SourceStringReader reader, long a) throws IOException {
// if (a * 2L > reader.getSource().length()) {
// log.error("报文长度异常,data={},len={}", reader.getSource(), a);
// throw new IOException();
// }
//
// char[] data = new char[(int)a * 2];
// reader.read(data);
// return new String(data);
// }
/**
* 获取 StringReader 的 next(游标位置/指针)
* @description
* 1. 利用反射原理,将 java.io.StringReader 的 private 属性 next 读取出来
* 2. 不建议高频调用 (影响调用程序的性能)
* @return
*/
@SneakyThrows
public int next(){
int next = Integer.MIN_VALUE; //读取失败时,以此值为标志
//反射方法1 : Java 17 中需结合 VM Option 参数 : `--add-opens java.base/java.io=ALL-UNNAMED`
//java.lang.reflect.Field field = java.io.StringReader.class.getDeclaredField("next");
//field.setAccessible(true);
//next = field.getInt( this );//读取 next 的值
////field.set(this, Integer.MIN_VALUE);//设置字段的值
//反射方法2: 基于 Unsafe
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
// 获取私有字段的偏移量
Field nextField = StringReader.class.getDeclaredField("next");
long offset = unsafe.objectFieldOffset(nextField);
next = unsafe.getInt(this, offset);
//unsafe.putInt(stringReader, offset, 10);// 设置字段值
return next;//next : 下标从 0 开始; 即将读取的下一个 char 的下标位置
}
public Boolean hasNext(){
// string.length() | eg: string = "00000001" => string.length() = 8
return hasReadSize != string.length();
}
public int getHasReadSize() {
return hasReadSize;
}
@Override
public Charset getCharset() {
return charset;
}
@Override
public void setCharset(Charset charset) {
this.charset = charset;
}
@Override
public String getLatestReadData() {
return latestReadData;
}
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
}
Y 推荐文献
X 参考文献
本文作者:
千千寰宇
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!

浙公网安备 33010602011771号