提问人:amaidment 提问时间:11/9/2023 最后编辑:amaidment 更新时间:11/11/2023 访问量:78
多行字符串的 Java 子字符串,最多第 n 行和该行上的字符索引
Java substring of multi-line String up to nth line and character index on that line
问:
在 Java 中,给定一个多行,我想获取从开头到第 n 行的子字符串以及该行上的字符索引。(行索引和字符索引都是从零开始的。String
例如,如果我们要实现这样的方法:
/**
* Returns the substring of the given string up to the given character index on the given line index.
*
* @param text input text
* @param line line index
* @param character character index
* @return substring
*/
public static String substring(String text, int line, int character);
然后,考虑以下多行字符串:
你好,
世界,
你好
吗
?
对于给定的输入,上述方法应返回
- 子字符串(文本, 0, 2);
他
- 子字符串(文本,1,3);
你好
沃尔
- 子字符串(文本,3,0);
你好
,世界
如何
我考虑了几种方法:
- 通过对第 n 行进行操作来构造子字符串:
一种方法是使用string.lines()
并构建子字符串。 像这样:
更新:根据厄立特里亚的回答,更新了改进和更整洁的实现:String
public static String buildSubstring(String text, int line, int character) {
long textLines = text.lines().limit(line + 1).count();
if (line > textLines) {
return text;
} else {
String[] rows = text.lines().toArray(String[]::new);
return IntStream.range(0, line + 1)
.mapToObj(i -> {
String lineText = rows[i];
return i == line ? lineText.substring(0, Math.min(character, lineText.length())) : lineText;
})
.collect(Collectors.joining(System.lineSeparator()));
}
}
但是,我主要担心的是过度创建字符串对性能的影响。
- 获取子字符串到原始字符串中的字符索引:
更直观的方法可能是使用string.substring(0, x),其中 x
是第 n 行和该行中位置的字符索引 - 在原始多行中。
但是,对于在原始字符串中查找该字符索引的最佳方法是什么,我不清楚。
一种方法是迭代使用string.indexOf(System.lineSeparator(),lineIndex)
来标识该行在原始 String 中的位置,并在该行上添加字符索引。像这样的东西:String
public static String indexSubstring(String text, int line, int character) {
String separator = System.lineSeparator();
int separatorLength = separator.length();
int lineIndex = 0;
if (line > 0) {
lineIndex = text.indexOf(separator) + separatorLength;
for (int i = 1; i < line; i++) {
lineIndex = text.indexOf(separator, lineIndex) + separatorLength;
}
}
return text.substring(0, lineIndex + character);
}
但是,如果文本中的行分隔符与 ;在我的情况下就是这种情况 - 也就是说,原始文本可能来自 unix 或 Windows 环境和/或此功能可能在 unix 或 Windows 环境中执行,并且它们需要可互操作。
当然,可以做一个 ,但这会比第一种方法使用 做更多的创造。System.lineSeparator()
string.replaceAll("\\r?\\n, System.lineSeparator())
String
string.lines()
注意:就此问题而言,我不处理错误情况 - 例如,行/字符索引超出了原始长度,或者字符索引超出了行的长度。一旦我决定了基本方法,这些将被考虑在内;或者,为了简单起见,我们可以假设它将返回行或输入文本中的所有内容。String
问题:
- 如何获取第 n 行的多行中的字符位置以及该行上的字符索引?
即用于 string.substring(0, x)。String
- 有没有比我上面列出的任何一种方法更好的方法来获取子字符串?
答:
使用现有的 System 类和方法总是会让你走得更远,它们更有效率,让你更精确地得到你的结果。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
String text = """
hello
world
how
are
you?""";
System.out.println(substring(text, 0, 2)); // he
System.out.println(substring(text, 1, 3)); // hello\nwor
System.out.println(substring(text, 3, 0)); // hello\nworld\nhow\n
try {
System.out.println(substring(text, 6, 0)); // Line index out of bounds
} catch (IndexOutOfBoundsException e) {
System.out.println(e.getMessage());
}
try {
System.out.println(substring(text, 0, 6)); // Range [0, 6) out of bounds for length 5
} catch (IndexOutOfBoundsException e) {
System.out.println(e.getMessage());
}
}
/**
* Returns the substring of the given string up to the given character index on the given line index.
*
* @param text input text
* @param line line index (starting at 0 for the first line)
* @param character character index (starting at 0 for the first character)
* @return substring
*/
public static String substring(String text, int line, int character) throws IndexOutOfBoundsException {
Scanner scanner = new Scanner(text);
int lineCount = 0;
StringBuilder sb = new StringBuilder();
while (scanner.hasNextLine()) {
String lineText = scanner.nextLine();
if (lineCount == line) {
sb.append(lineText, 0, character);
break;
} else {
sb.append(lineText);
sb.append(System.lineSeparator());
}
lineCount++;
}
if (lineCount < line) {
throw new IndexOutOfBoundsException("Line index out of bounds");
}
return sb.toString();
}
}
评论
假设您没有很大的输入,我将输入拆分为行并存储在数组中,并使用 IntStream 并将每行索引映射到整行,除非该行等于参数,然后映射到子字符串。像这样:line
public static String buildSubstring(String text, int line, int character){
String[] rows = text.lines().toArray(String[]::new);
return IntStream.range(0, line + 1)
.mapToObj(i -> i == line ? rows[i].substring(0,character) : rows[i])
.collect(Collectors.joining(System.lineSeparator()));
}
评论
我认为这个解决方案几乎适用于所有 Java 版本。
public static String indexSubstring(String text, int line, int character) {
String result = "";
try {
String[] lines = text.split("\n");
for (int i = 0; i < line; i++) {
result += lines[i] + "\n";
}
result += lines[line].substring(0, character);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
我用 java 15 测试了它,它适用于所有形式的多行字符串 “”“你的多行字符串""";
如果您可以取回带有原始换行符 (*) 的子字符串,则可以遍历字符,并仅在最后执行一次实际调用:substring()
public static void main(String[] args) {
String n = "hello\nworld\nhow\nare\nyou?";
String r = "hello\rworld\rhow\rare\ryou?";
String rn = "hello\r\nworld\r\nhow\r\nare\r\nyou?";
System.out.println(substring(n, 0, 2));
System.out.println(substring(r, 0, 2));
System.out.println(substring(rn, 0, 2));
System.out.println(substring(n, 1, 3));
System.out.println(substring(r, 1, 3));
System.out.println(substring(rn, 1, 3));
System.out.println(substring(n, 3, 0));
System.out.println(substring(r, 3, 0));
System.out.println(substring(rn, 3, 0));
}
public static String substring(String text, int line, int character) {
int pos = 0;
char sep = 0;
while (line > 0) {
char c = text.charAt(pos++);
if (c == '\n' || c == '\r') {
if (sep == 0)
sep = c;
if (c == sep)
line--;
}
}
char c = text.charAt(pos);
if (c != sep && (c == '\n' || c == '\r'))
pos++;
return text.substring(0, pos + character);
}
假设换行符在字符串中是一致的,因此遇到第一个实际的换行符意味着所有其他字符看起来都一样,并且另一个字符要么不使用,要么可以忽略(但在循环后仍然需要一些特殊处理)。
代码实际上在这里工作:https://ideone.com/AWLBuD,但是 (*) 适用,正如您所看到的,IdeOne 在大多数情况下都成功转换,但是如果生成一个末尾带有“原始”换行符的字符串,然后得到 -d(添加“本机”换行符),则会导致打印 2 或 1 个总换行符,具体取决于“原始”换行符是否与“本机”换行符匹配。我认为这也可能发生在实际的游戏机中。substring(x, 3, 0)
println()
"...如何获取第 n 行的多行
字符串
中的字符位置以及该行上的字符索引?
即用于 string.substring(0, x)。..."
使用正则表达式模式计算换行符分隔符。
下面是一个示例。
String substring(String text, int line, int character) {
Pattern p = Pattern.compile("\\R");
Matcher m = p.matcher(text);
int o = 0;
while (line-- > 0 && m.find()) o = m.end();
return text.substring(0, o + character);
}
评论