Java 用特定逻辑替换字符串中的字符

Java 用特定逻辑替换字符串中的字符,java,string,logic,character,Java,String,Logic,Character,我目前正在用Java创建一个简单的Lexer生成器。我差不多完成了,在这里和那里熨掉了一些虫子,但是遇到了一个问题 我希望能够在Lexer中包含注释检测,并希望以特定方式包含注释: 注释由存储在字符串中的任何字符集分隔。 示例:single=“/”,multi\u beg=“/*”和multi\u end=“*/” 任何介于和(包括)注释字符之间的内容都需要用空格替换,因为Lexer使用空格来检测标记之间的间隙。(替换为void可能会导致两个令牌融合在一起) 包含这样一个东西在技术上很容

我目前正在用Java创建一个简单的Lexer生成器。我差不多完成了,在这里和那里熨掉了一些虫子,但是遇到了一个问题

我希望能够在Lexer中包含注释检测,并希望以特定方式包含注释:

  • 注释由存储在字符串中的任何字符集分隔。
    • 示例:
      single=“/”
      multi\u beg=“/*”
      multi\u end=“*/”
  • 任何介于和(包括)注释字符之间的内容都需要用空格替换,因为Lexer使用空格来检测标记之间的间隙。(替换为void可能会导致两个令牌融合在一起)
包含这样一个东西在技术上很容易,只需要几个布尔值和字符串替换。但是,Lexer还包含可定义的字符串和字符常量。由于注释字符可以在这些常量中定义,因此注释只能在当前未定义字符串或字符时“可执行”


其逻辑可能如下所示:

  • 如果当前已装箱,则不执行任何操作,直到找到未装箱字符
  • 如果未封装,并且找到了一个封装字符,则封装
  • 如果未封装,并且找到了一条单行注释,则将之后的所有内容(包括注释字符)替换为空白。(我们处理的是单个字符串,而不是数组,因此后面的所有内容仅引用一行)
  • 如果未封装,并且开始了多行注释,则将之后的所有内容(包括注释字符)替换为空格,直到多行注释结束

我脑子里已经完全记下了这个想法,但不知道如何在Java中实现它

注意:我正在处理一个字符串数组,我正在考虑通过增强for循环实现这一点,在扫描程序中一次处理每一行,在标记堆栈之前处理注释

for (String s : data) {
    // ???
}
关于如何在Java中实现这个有什么想法吗

更新:这就是我想要的输入/输出外观:


通常的方法是将此作业留给标记器,并将注释作为一种空白标记。

这里是一个未经测试的实现。测试是最难的部分,要非常小心

public class CommentStripper {
    private enum State {
        CODE,
        LINE_COMMENT,
        COMMENT,
        STRING
    }
    public static String strip(String input) {
        return strip(input.toCharArray());
    }

    public static String strip(char[] input) {
        State currentState = State.CODE;
        StringBuilder rv = new StringBuilder();
        char[] lineSeparator = System.lineSeparator().toCharArray();
        for (int i = 0; i < input.length; i++) {
            STATE_SWITCH: switch (currentState) {
            case CODE: 
                if (input[i] == '"') {
                    currentState = State.STRING;
                    rv.append(input[i]);
                    break;
                }
                if (input[i] == '/') {
                    if (i + 1 >= input.length) {
                        rv.append(input[i]);
                        break;
                    }
                    if (input[i+1] == '*') {
                        i++;
                        currentState = State.COMMENT;
                        break;
                    } else if (input[i+1] == '/') {
                        i++;
                        currentState = State.LINE_COMMENT;
                        break;
                    }
                }
                rv.append(input[i]);
            break;
            case STRING:
                if (input[i] == '"') {
                    currentState = State.CODE;
                    rv.append(input[i]);
                    break;
                }
                rv.append(input[i]);
                break;
            case COMMENT:
                if (input[i] == '*') {
                    if (i + 1 >= input.length) {
                        break;
                    }
                    if (input[i + 1] == '/') {
                        i++;
                        currentState = State.CODE;
                        break;
                    }
                }
                break;
            case LINE_COMMENT:
                for (int sepIndex = 0; sepIndex < lineSeparator.length; sepIndex++) {
                    if (input[i+sepIndex] != lineSeparator[sepIndex]) {
                        break STATE_SWITCH;
                    }
                }
                i+=lineSeparator.length-1;
                rv.append(lineSeparator);
                currentState = State.CODE;
                break;
            } 
        }
        return rv.toString();
    }
}
基于最小阵列的方法因其效率而被选择。流式API使其要么臃肿,要么无效。您必须连接字符串才能使用它

请注意,无法将转义引号放入字符串文字中。你的问题没有具体说明这一点,所以我省略了这一点


考虑使用像ANTLR这样的解析库,而不是自己编写解析器。

您打算对输入数组执行操作吗?通常lexer在不修改输入的情况下,将输入拆分为令牌(一种新结构)。为什么下一个标记不能是一个完整的字符串文字?@Basilevs我已经将lexer调制成了段。第一个是扫描器,第二个是标记器。在将数据显示到Tokeniser之前,我只想完全“擦除”字符串中的注释,就像用white out完成一样。@Basilevs这将是目前为止唯一执行的操作。现在您有两个标记器,没有明显的好处,不是吗?@Basilevs否。我想从字符串数组中删除数据。一旦删除了字符串数组中所有可能出现的内容,请发送数组进行标记化。我理解,但我根本不想要标记。我希望在将任何数据推送到tokeniser之前将其删除,回到Scanner部分。为什么要这样做?我编写的Lexer具有扫描令牌的结构,仅当它是符号、数字或字符时才停止标记。如果它是一个符号,将其映射到相应的字符,并吐出一个标记。数字,吐出数字或实数。字母/常量字符,吐出标识符或字符串/字符。已跳过空格。很抱歉答复太晚。最近一直在工作,真的很忙。我修改了我的发电机,工作得很好。非常感谢。至于ANTLR,也许我会把发电机挂在上面?:)
import static commentStrip.CommentStripper.strip;
import static org.junit.Assert.*;

import org.junit.Test;

public class CommentStripperTest {

    @Test
    public void test() {
        assertEquals("\"test\"", strip("\"test\"//hello\"test\""));
        assertEquals("\"test\"\"test\"", strip("\"test\"/*hello*/\"test\""));
        assertEquals("test"+System.lineSeparator()+"test", strip("test//linecomment"+System.lineSeparator()+"test"));
        assertEquals("test", strip("test/*test"));
        assertEquals("\"test//hellotest\"", strip("\"test//hellotest\""));
        assertEquals("\"test/*hello*/test\"", strip("\"test/*hello*/test\""));
    }

}