用特定逻辑替换字符串中的字符
Replacing characters in Strings with specific logic
我目前正在 Java 中创建一个简单的 Lexer 生成器。我快完成了,解决了一些错误,但遇到了一个问题。
我希望能够在 Lexer 中包含注释检测,并希望以特定方式包含注释:
- 评论由任意一组字符分隔,存储在一个字符串中。
- 示例:
single = "//"
、multi_beg = "/*"
和 multi_end = "*/"
- 注释字符之间的任何内容(包括注释字符)都需要用空格替换,因为 Lexer 使用空格来检测标记之间的间隙。 (替换为 void 可能会导致两个令牌融合在一起)
包括这样的东西在技术上很容易,只需几个布尔值和字符串替换。但是,词法分析器还包含可定义的字符串和字符常量。由于可以在这些常量中定义注释字符,因此当当前未定义字符串或字符时,注释只能是 "executable"。
此操作的逻辑可能如下所示:
- 如果当前已封装,则不执行任何操作,直到找到未封装的字符。
- 如果没有封装,并且找到封装字符,则封装。
- 如果没有封装,并且发现 单 行注释,请用空格替换 之后的所有内容,包括注释字符。 (我们处理的是单个字符串,而不是数组,因此 之后的所有内容仅引用一行)
- 如果没有封装,并且开始了一个 multi 行注释,替换 之后的所有内容,包括注释字符,用空格,直到多行注释 ended.
我脑子里已经有了完整的想法,但不知道如何在 Java 中实现它。
注意:我正在处理一个字符串数组,我正在考虑通过增强的 for 循环来实现这一点,在扫描仪中一次处理每一行,处理评论在标记堆栈之前。
for (String s : data) {
// ???
}
关于如何在 Java 中实现此 的任何想法?
更新: 这就是我希望 in/out 的样子:
通常的做法是将这项工作留给分词器,并将评论作为一种空白标记。
这是一个未经测试的实现。测试是最难的部分,一定要认真做。
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();
}
}
初始测试:
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\""));
}
}
简约 array-based 方法因其效率而被选中。 Streaming API 使它变得臃肿或无效。您必须连接字符串才能使用它。
请注意,无法将转义引号放入字符串文字中。这不是你的问题指定的,所以我省略了这个处理。
考虑使用像 ANTLR 这样的解析库,而不是自己编写解析器。
我目前正在 Java 中创建一个简单的 Lexer 生成器。我快完成了,解决了一些错误,但遇到了一个问题。
我希望能够在 Lexer 中包含注释检测,并希望以特定方式包含注释:
- 评论由任意一组字符分隔,存储在一个字符串中。
- 示例:
single = "//"
、multi_beg = "/*"
和multi_end = "*/"
- 示例:
- 注释字符之间的任何内容(包括注释字符)都需要用空格替换,因为 Lexer 使用空格来检测标记之间的间隙。 (替换为 void 可能会导致两个令牌融合在一起)
包括这样的东西在技术上很容易,只需几个布尔值和字符串替换。但是,词法分析器还包含可定义的字符串和字符常量。由于可以在这些常量中定义注释字符,因此当当前未定义字符串或字符时,注释只能是 "executable"。
此操作的逻辑可能如下所示:
- 如果当前已封装,则不执行任何操作,直到找到未封装的字符。
- 如果没有封装,并且找到封装字符,则封装。
- 如果没有封装,并且发现 单 行注释,请用空格替换 之后的所有内容,包括注释字符。 (我们处理的是单个字符串,而不是数组,因此 之后的所有内容仅引用一行)
- 如果没有封装,并且开始了一个 multi 行注释,替换 之后的所有内容,包括注释字符,用空格,直到多行注释 ended.
我脑子里已经有了完整的想法,但不知道如何在 Java 中实现它。
注意:我正在处理一个字符串数组,我正在考虑通过增强的 for 循环来实现这一点,在扫描仪中一次处理每一行,处理评论在标记堆栈之前。
for (String s : data) {
// ???
}
关于如何在 Java 中实现此 的任何想法?
更新: 这就是我希望 in/out 的样子:
通常的做法是将这项工作留给分词器,并将评论作为一种空白标记。
这是一个未经测试的实现。测试是最难的部分,一定要认真做。
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();
}
}
初始测试:
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\""));
}
}
简约 array-based 方法因其效率而被选中。 Streaming API 使它变得臃肿或无效。您必须连接字符串才能使用它。
请注意,无法将转义引号放入字符串文字中。这不是你的问题指定的,所以我省略了这个处理。
考虑使用像 ANTLR 这样的解析库,而不是自己编写解析器。