多行字符串的Java子串最多第n行字符串和该行的字符索引

kokeuurv  于 12个月前  发布在  Java
关注(0)|答案(5)|浏览(123)

在Java中,给定一个多行String,我想得到从开始到第n行的子字符串和该行的字符索引(行索引和字符索引都是从零开始的)。
例如,如果我们要实现这样一个方法:

/**
   * 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);

字符串
然后,考虑以下多行字符串:
你好
世界
如何

你呢?
对于给定的输入,上述方法应返回

  • String s(String s,0);

  • String s(String s,1,3);

你好
wor

  • String(String,String,String,String);

你好
世界
如何
我考虑了几种方法:
1.通过对String直到第n行的操作来构造子串:
一种方法是使用string.lines(),并构建子字符串。类似这样:
更新:更新了一个改进和更整洁的实施,基于厄立特里亚的answer

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创建对性能的影响。
1.获取子字符串,直到原始String中的字符索引:
更直观的方法可能是使用string.substring(0, x),其中x是第n行的字符索引(在原始的多行String中)以及该行中的位置。
然而,我不清楚什么是在原始String中找到该字符索引的最佳方法。
一种方法是迭代地使用string.indexOf(System.lineSeparator(),lineIndex)来识别原始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);
  }


但是,如果文本中的行分隔符与System.lineSeparator()不同,这将无法处理这种情况;这就是我的情况-也就是说,原始文本可能来自Unix或Windows环境,并且/或者此功能可能在Unix或Windows环境中执行,并且它们需要互操作。
当然,也可以使用string.replaceAll("\\r?\\n, System.lineSeparator()),但这将比使用string.lines()的第一种方法创建更多的String
注意事项:在这个问题中,我并没有处理错误情况--例如,行/字符索引超出了原始String的长度,或者字符索引超出了行的长度。或者,为了简单起见,我们可以假设它将返回行上或输入文本中的所有内容。
问题:
1.如何获得多行String中第n行的字符位置和该行的字符索引?
例如,在string.substring(0,x)中使用。
1.有没有比我上面列出的两种方法更好的方法来获取子字符串?

v1l68za4

v1l68za41#

假设你没有一个巨大的输入,我会把输入分成几行并存储在一个数组中,然后使用IntStream将每行索引Map到整行,除了该行等于参数line,然后Map到一个子字符串。

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()));
}

字符串

uubf1zoe

uubf1zoe2#

使用现有的系统类和方法总是会让你走更长的路,它们更有效,让你得到更精确的结果。

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();
    }
}

字符串

dba5bblo

dba5bblo3#

我认为这个解决方案适用于几乎所有的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测试了它,它适用于所有形式的多行字符串“"你的多行字符串在这里"";

64jmpszr

64jmpszr4#

如果你可以用原始的换行符(*)来返回子字符串,你可以循环字符,只在最后执行一个实际的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在大多数情况下都能成功转换,但在substring(x, 3, 0)产生一个结尾带有“原始”换行符的字符串的情况下,它会得到println()-d(添加“本机”换行符),它会导致打印2个或1个换行符,这取决于“原始”换行符是否与“本机”换行符匹配。linebreak与否。我认为这可能会发生在实际的控制台太。

klr1opcd

klr1opcd5#

  • "..

例如,用于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);
}

字符串

相关问题