• 【174】Java解析文件名中的方括号表达式


    1. 使用场景

    在由不同编程语言构成的系统中,经常会出现下面的场景:一开始某个子系统(非Java编写)在硬盘上生成了某个二进制文件,下一步交给Java代码进行处理。并且文件的一些附加信息会保存在文件名中。

    比如【173】VS2022调试通过海康温度报警SDK的C++代码 中生成的文件,文件名就用英文方括号 [] 来显示不同的信息。

    例如

    FireAlarm[192.168.1.56][20220107153455][101.5][0].jpg
    
    • 1

    第一个方括号表示IP。
    第二个方括号表示日期时间。
    第三个方括号表示温度。
    第四个方括号表示测温单位: 0- 摄氏度(℃),1- 华氏度(℉),2- 开尔文(K)。
    其中如果数值为空,就用空的方括号表示。

    对应的图片文件生成后,就需要Java来对文件做进一步处理,并读取和文件相关的一系列参数加以处理。

    那么,怎么用Java来读取文件名中的数据呢?这就是下一节讨论的内容。

    2. 解析方括号

    我把文件名看作程序的输入参数,参数类型是字符串。我同时也把文件名看作表达式。下文用表达式来指代文件名。
    Java代码使用了 Parser 的方式来编写代码。 因为这个表达式逻辑比较简单,所以就没有使用巴科斯范式(BNF)。

    整个代码结构如下:

    在这里插入图片描述

    其中Main类包含main方法,做测试用。

    包 zhangchao.parser是用来解析表达式的包。其中只有 Parser 类的 getStrArr 方法是对外提供的公共方法。

    2.1 单词(token)

    表达式是由不同的单词(token)组成的。每个单词是一个或多个字符组成的集合。在这个例子中单词分成标识符单词(对应类IdToken)和标识符单词(对应类StrToken)。为了方便编程实现,我还加了个抽象类AbstractToken。继承关系如下图所示:

    在这里插入图片描述

    AbstractToken.java

    package zhangchao.parser;
    
    /**
     * 所有类型单词的抽象类
     * @author zhangchao
     */
    abstract class AbstractToken {
    
        /**
         * 判断单词类型是不是字符串。
         * @return true是字符串,false不是字符串。
         */
        boolean isString() {
            return false;
        }
    
        /**
         * 判断单词是不是标识符。
         * @return true是标识符,false不是标识符。
         */
        boolean isIdentifier() {
            return false;
        }
    
    
        /**
         * 以字符串类型,返回单词内容
         * @return 单词的字符串内容
         */
        abstract String show();
    }
    
    
    • 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

    IdToken.java

    package zhangchao.parser;
    
    /**
     * 标识符类型的单词
     *
     * @author zhangchao
     */
    class IdToken extends AbstractToken {
        // 单词的值
        String value;
    
        @Override
        boolean isIdentifier() {
            return true;
        }
    
        @Override
        String show() {
            return value;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    StrToken.java

    package zhangchao.parser;
    
    /**
     * 字符串类型的单词
     * @author zhangchao
     */
    class StrToken extends AbstractToken{
        // 单词的值
        String value;
    
        @Override
        boolean isString() {
            return true;
        }
    
        @Override
        String show() {
            return value;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.2 词法分析器Lexer

    词法分析器Lexer负责把表达式拆成多个单词。

    Lexer.java

    package zhangchao.parser;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 词法分析器
     * @author zhangchao
     */
    class Lexer {
    
        // 保存单词
        private ArrayList<AbstractToken> queue = new ArrayList<AbstractToken>();
    
    
        /**
         * 预读取表达式。
         * 从index给定的位置开始,计算字符串类型的单词的长度。
         *
         * @param arr  表达式转化成的字符数组。
         * @param index 从数组中index位置开始,计算字符串类型的单词的长度。
         * @return 字符串类型的单词的长度。
         */
        private int preRead(char[] arr, int index) {
            int result = 0;
            for (int i = index; i < arr.length; i++) {
                char c = arr[i];
                if (c == '[') {
                    break;
                } else if (c == ']') {
                    break;
                } else {
                    result ++;
                }
            }
            return result;
        }
    
        /**
         * 解析输入的表达式字符串,并且解析成【单词Token】
         * @param str 输入的表达式字符串。
         */
        void addQueue(String str) {
            char[] arr = str.toCharArray();
            int length = arr.length;
            for (int i = 0; i < length;) {
                char c = arr[i];
                if (c == '[') {
                    IdToken t = new IdToken();
                    t.value = String.valueOf(c);
                    queue.add(t);
                    i++;
                } else if (c == ']') {
                    IdToken t = new IdToken();
                    t.value = String.valueOf(c);
                    queue.add(t);
                    i++;
                } else {
                    int size = preRead(arr, i);
                    char[] tmp = Arrays.copyOfRange(arr, i, i + size);
                    StrToken strToken = new StrToken();
                    strToken.value = new String(tmp);
                    queue.add(strToken);
                    i = i + size;
                }
            }
        }
    
        /**
         * 因为只关心在 [] 中的字符串,所以不在 [] 中的 【单词】 都要删除
         */
        void delNonsenseToken() {
            int length = this.queue.size();
            if (0 == length) {
                return;
            }
            for (int i = length - 1; i >= 0; i--) {
                AbstractToken t = queue.get(i);
                if (t.isString()) {
                    // 开头或者结尾的字符串,肯定不在 [] 之中
                    if (queue.size() - 1 == i || 0 == i) {
                        queue.remove(i);
                    } else if (i - 1 >= 0) {
                        AbstractToken left = queue.get(i - 1);
                        if (left.isIdentifier() && left.show().equals("]")) {
                            queue.remove(i);
                        }
                    } else if (i + 1 < queue.size()) {
                        AbstractToken right = queue.get(i + 1);
                        if (right.isIdentifier() && right.show().equals("[")) {
                            queue.remove(i);
                        }
                    }
                } // end if (t.isString()) {
            }
        }
    
    
    
        List<AbstractToken> getQueue() {
            queue.trimToSize();
            return queue;
        }
    
    
    }
    
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    2.3 语法分析器Parser

    语法分析器Parser负责解析单词,构造抽象语法树(AST)。类 AstNode 是抽象语法树的节点。

    AstNode.java

    package zhangchao.parser;
    
    import java.util.List;
    import java.util.ArrayList;
    
    /**
     * 抽象语法树  AST 的节点
     * @author zhangchao
     */
    class AstNode {
    
        List<AbstractToken> children = new ArrayList<>();
    
        String eval() {
            return children.get(1).show();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Parser.java

    package zhangchao.parser;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 语法分析器
     * @author zhangchao
     */
    public class Parser {
    
        /**
         * 词法分析器
         */
        private Lexer lexer = new Lexer();
    
    
        /**
         * 对输入的字符串进行语法分析,生成抽象语法树AST。
         * @param str 输入的字符串
         * @return 抽象语法树节点AstNode列表。
         */
        private List<AstNode> parser(String str) {
            lexer.addQueue(str);
            lexer.delNonsenseToken();
            List<AbstractToken> queue = lexer.getQueue();
    
            ArrayList<AstNode> allNodes = new ArrayList<>();
    
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                AbstractToken current = queue.get(i);
                AbstractToken add1 = null;
                AbstractToken add2 = null;
                if (i + 1 < size) {
                    add1 = queue.get(i + 1);
                }
                if (i + 2 < size) {
                    add2 = queue.get(i + 2);
                }
    
                if (current.isIdentifier() && current.show().equals("[") &&
                        null != add1 && add1.isIdentifier() && add1.show().equals("]")) {
                    AstNode node = new AstNode();
                    node.children.add(current);
                    StrToken emptyToken = new StrToken();
                    emptyToken.value = "";
                    node.children.add(emptyToken);
                    node.children.add(add1);
                    allNodes.add(node);
                }
                if (current.isIdentifier() && current.show().equals("[") &&
                        null != add1 && add1.isString() &&
                        null != add2 && add2.isIdentifier() && add2.show().equals("]")) {
                    AstNode node = new AstNode();
                    node.children.add(current);
                    node.children.add(add1);
                    node.children.add(add2);
                    allNodes.add(node);
                }
            }
            allNodes.trimToSize();
            return allNodes;
        }
    
    
        /**
         * 输入形如  COMM_ALARM_ACS_CapPic[0][20220107153455][36.099][192.168.1.1][].jpg  字符串,
         * 此方法会把 [] 中的内容提取出来,
         * 并用列表形式返回。对比Lexer,增强了对空括号的处理。
         * @param str 形如  COMM_ALARM_ACS_CapPic[0][20220107153455][36.099][192.168.1.1].jpg  字符串
         * @return 从[]中提取的字符串,组成的字符串列表
         */
        public List<String> getStrArr(String str) {
            List<AstNode> allNodes = this.parser(str);
            ArrayList<String> result = new ArrayList<>();
            for (AstNode node : allNodes) {
                result.add(node.eval());
            }
            return result;
        }
    }
    
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    2.4 测试

    Main.java

    package zhangchao;
    
    import zhangchao.parser.Parser;
    
    import java.util.List;
    
    /**
     * 主类,保护main方法。
     * @author zhangchao
     */
    public class Main {
    
    
        public static void main(String[] args) {
    
    
            String name = "COMM_ALARM_ACS_CapPic[][20220107153455][36.099][37.69.15.243][].jpg";
            Parser p = new Parser();
            List<String> stringList = p.getStrArr(name);
            for (String str : stringList) {
                System.out.println("str=" + str);
            }
        }
    
    }
    
    
    • 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

    输出结果:

    str=
    str=20220107153455
    str=36.099
    str=37.69.15.243
    str=
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    node.js+校内废品回收管理 毕业设计-附源码140933
    音视频开发13 FFmpeg 音频 相关格式分析 -- AAC ADTS格式分析
    Linux中进程管理
    Android 12 蓝牙打开
    Python 导入Excel三维坐标数据 生成三维曲面地形图(体) 5-3、线条平滑曲面且可通过面观察柱体变化(三)
    Docker with IPV6
    入门力扣自学笔记155 C++ (题目编号698)
    Mac实现Gitlab CICD
    Spring Securit OAuth 2.0整合—核心的接口和类
    lru_cache python
  • 原文地址:https://blog.csdn.net/zhangchao19890805/article/details/125794696