如何在不检查字符串大小或越界的情况下获取字符串的前n个字符?

214

我怎样能在Java中获取一个字符串的前n个字符,而不需要先进行大小检查(内联方式可接受),也不会冒险出现IndexOutOfBoundsException


1
除非你捕获异常,否则我不知道你打算如何处理字符长度大于字符串长度的情况。 - Matt Boehm
3
为什么?你为什么不检查长度或捕获异常? - paxdiablo
1
出于好奇,你为什么想要避免大小检查。这不是C语言。 - Tom Hawtin - tackline
我想表达的是希望避免使用if/else块,而不是厌恶实际检查长度。 - antony.trupe
可能是重复问题: https://dev59.com/12oy5IYBdhLWcg3wg-Um#35252333 - Whimsical
最简单的方法就是先通过在字符串末尾添加空格来确保字符串更长。 - John Lord
8个回答

432

这里有一个简洁的解决方案:

String upToNCharacters = s.substring(0, Math.min(s.length(), n));

个人观点:虽然这个解决方案很“巧妙”,但我认为它比使用if/else的解决方案实际上更难以阅读。如果读者没有见过这个技巧,他/她必须更加努力地思考来理解代码。在我看来,在if/else版本中,代码的含义更为明显。如需更简洁/易读的解决方案,请参见@paxdiablo的答案


1
+1。如果这个函数被命名为safe_substring或substring_safe,就像paxdiablo的答案一样,那么使用起来会更容易阅读/意图更明显。 - ToolmakerSteve
3
我不同意你的说法。如果这被包装在一个函数中,函数内部是什么并不重要,任何“整洁”都绝对被缺乏清晰度所压倒。这个解决方案的重点是它对于你不想创建一个包装函数的情况非常“整洁”。 - Stephen C
最好使用StringUtils,这样可以避免IndexOutOfBoundsException和NullPointerException。 - Lluis Martinez
我并不认为防止NPE(NullPointerException)是一件好事。一个NPE意味着你的s中应该有一个null,这是一个bug的标志,而不是应该被隐藏的东西。处理null并不是OP陈述的要求的一部分。 - Stephen C

112

不要重复造轮子...:

org.apache.commons.lang.StringUtils.substring(String s, int start, int len)

Javadoc指出:

StringUtils.substring(null, *, *)    = null
StringUtils.substring("", * ,  *)    = "";
StringUtils.substring("abc", 0, 2)   = "ab"
StringUtils.substring("abc", 2, 0)   = ""
StringUtils.substring("abc", 2, 4)   = "c"
StringUtils.substring("abc", 4, 6)   = ""
StringUtils.substring("abc", 2, 2)   = ""
StringUtils.substring("abc", -2, -1) = "b"
StringUtils.substring("abc", -4, 2)  = "ab"
因此:
StringUtils.substring("abc", 0, 4) = "abc"

1
它并没有回答问题,但无论如何它仍然提供了解决方案。如果楼主能够理解,我认为这是一个更好的解决方案。 - aullah
7
指出StringUtils.substring(yourString, 0, n)yourString.substring(0, n)不同可能也是有用的。前者使用了StringUtils, 而后者使用了String.substring(如果结束索引超出字符串长度会抛出异常)。 - ToolmakerSteve
只是作为FYI,如果您查看此方法的源代码,它处理的情况是结尾大于长度的情况,通过将结尾更改为长度:if (end > str.length()) { end = str.length();} - bholl
2
StringUtils.substring(String s, int start, int len) 的最后一个参数不是 len,而是结束索引。 - gorootde
StringUtils.substring("abc", 0, 4) = "abc",对我很有用。谢谢! - Akash5288

70

1
这难道不是最好的解决方案吗?为什么没有很多人点赞呢? - Do Will
5
也许是因为其他人没有和你持相同的观点? :-) - Stephen C
1
这个答案比原问题的提问日期晚得多。 - Whimsical
5
因为在可执行环境中添加第三方库并不总是值得的。 - LarsH
@LarsH 不过 Apache Commons 已经被广泛应用于许多项目中了。添加另一个第三方库只为了部分字符串切片似乎有些多余。 - tsh
1
@tsh 我同意,在这些情况下,使用Apache Commons是一个非常好的选择。但也有许多项目尚未使用AC。 - LarsH

18
String upToNCharacters = String.format("%."+ n +"s", str);

如果 n 是一个变量(因此你必须构造格式字符串),那么这就很糟糕,但如果是一个常量,那就非常清晰了:

String upToNCharacters = String.format("%.10s", str);

文档


有趣的替代方案,虽然我无法想象会使用它,因为四年前已经有更传统的方法了。 - ToolmakerSteve
最佳答案是因为输入字符串只读取一次,所以没有必要将其存储在变量中,这使得可以将其嵌入到代码中。 - Profiterole

11

在SO上有一类问题有时候不太清晰,这个问题非常接近边缘 :-)

也许您可以解释一下为什么排除了两种方法中的一种。

如果只是因为您不想在代码中添加if语句或异常捕获代码,那么一个解决方案就是使用一个帮助函数来为您处理它,例如:

static String substring_safe (String s, int start, int len) { ... }

该方法将先检查长度,然后根据情况采取相应的行动(返回较小的字符串或用空格填充)。

然后您就不必在代码中担心它,只需调用:

String s2 = substring_safe (s, 10, 7);

改为:

String s2 = s.substring (10,7);

根据你对其他答案的评论,这种方式似乎可以解决你担心的问题,即在进行大量字符串构建操作时不中断代码流程。


2
+1:鉴于OP不想使代码混乱,这种方法比已接受的方法好得多。(或者可以参考Nickkk的解决方案,包含一个已经具有所需行为的函数库。) - ToolmakerSteve

5
使用substring方法,如下所示:
int n = 8;
String s = "Hello, World!";
System.out.println(s.substring(0,n);

如果n大于字符串的长度,就会抛出异常,正如一位评论者所指出的那样。一个简单的解决方案是在你的else子句中将所有这些内容包装在条件if(s.length()<n)中,你可以选择是否只想打印/返回整个字符串或以其他方式处理它。

1
这可能会导致IndexOutOfBoundsException异常。 - antony.trupe
1
你必须检查大小或捕获异常。我可以问一下,为什么在你的情况下这两种方法都不起作用? - Matt Boehm
在构建字符串的过程中。它们可以工作,但会打断代码的流程。 - antony.trupe
@Matt - 最好设计你的算法,使得不会发生异常。首先,创建和捕获异常是昂贵的操作。其次,异常处理可能会很复杂,特别是如果有需要关闭/释放等资源的情况。 - Stephen C
3
这怎么回答问题了?问题是在询问如何避免首先进行大小检查,也不会引发需要捕获的异常。 - ToolmakerSteve
显示剩余3条评论

3
另一种不必使用第三方库的紧凑方式是使用三元运算符(?:):
s = s.length() > n ? s.substring(0, n) : s;

但使用“一行”if语句可能同样简单:

if (s.length() > n) s = s.substring(0, n);

1

ApacheCommons让我惊讶了,StringUtils.abbreviate(String str, int maxWidth)会在字符串末尾添加“...”,没有选项可以更改后缀。

WordUtils.abbreviate(String str, int lower, int upper, String appendToEnd)会一直查找到下一个空格。

我就放在这里了:

public static String abbreviate(String s, int maxLength, String appendToEnd) {
    String result = s;
    appendToEnd = appendToEnd == null ? "" : appendToEnd;
    if (maxLength >= appendToEnd.length()) {
        if (s.length()>maxLength) {
            result = s.substring(0, Math.min(s.length(), maxLength - appendToEnd.length())) + appendToEnd;
        }
    } else {
        throw new StringIndexOutOfBoundsException("maxLength can not be smaller than appendToEnd parameter length.");
    }
    return result;
}

1
@VolkanGüven 这是因为这个“ApacheCommons让我吃惊”的句子。我犯了罪,批评了神圣的ApacheCommons库。或者别的什么…… - yuceel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接