如何在不检查大小或超出范围的情况下获取字符串的前 n 个字符?

Posted

技术标签:

【中文标题】如何在不检查大小或超出范围的情况下获取字符串的前 n 个字符?【英文标题】:How do I get the first n characters of a string without checking the size or going out of bounds? 【发布时间】:2010-12-07 17:21:42 【问题描述】:

如何在不先进行大小检查(可以接受内联)或冒IndexOutOfBoundsException 的风险的情况下获取 Java 中字符串的第一个 n 字符?

【问题讨论】:

除非你捕捉到异常,否则我不知道你打算如何处理字符长度大于字符串长度的情况。 为什么?您对检查长度或捕获异常有什么反感? 出于好奇,为什么要避免尺寸检查。这不是 C。 我的意思是希望避免使用 if/else 块,而不是厌恶实际检查长度。 可能重复:***.com/questions/8499698/… 【参考方案1】:

使用substring方法,如下:

int n = 8;
String s = "Hello, World!";
System.out.println(s.substring(0,n);

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

【讨论】:

这可能会引发 IndexOutOfBoundsException 顺便说一句,如果你打算用 Java 编程,你应该尽量记住 String (java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html) 的大部分 API 方法。 我已经排除了子字符串,至少就其本身而言,作为不是的答案。 您必须检查大小或捕获异常。请问为什么在你的情况下做这两种方法都行不通? 如何回答这个问题?问题是如何不必先进行大小检查,也不会导致需要捕获的异常。【参考方案2】:

这是一个巧妙的解决方案:

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

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

【讨论】:

+1。如果这被包装在一个名为 safe_substring 或 substring_safe 的函数中会更好,就像 paxdiablo 的回答一样,这样使用更容易阅读/意图更明显。 我不同意你所说的。如果它被包装在一个函数中,函数内部的内容无关紧要,任何“整洁”肯定会因缺乏清晰度而被压倒。这个解决方案的要点是,对于您不想想要创建包装函数的情况,它是“简洁的”。 使用 StringUtils 会更整洁。它可以防止 IndexOutOfBoundsException 和 NullPointerException。 我不相信防止 NPE 是一件好事。 NPE 意味着您应该在s 中有一个null。这是错误的标志,而不是应该隐藏的东西。处理 null 不是 OP 规定要求的一部分。【参考方案3】:

有一类关于 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);

这适用于您似乎担心的情况(基于您对其他答案的 cmets),在进行大量字符串构建时不会破坏代码流。

【讨论】:

+1:鉴于 OP 不希望代码混乱,这是一种比公认的方法更好的方法。 (或参见 Nickkk 的解决方案,即包含一个已经具有按需要运行的函数的库。)【参考方案4】:

不要重新发明***...:

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"

【讨论】:

它没有回答问题,但无论如何它仍然提供解决方案。如果OP能够理解,我认为这是一个更好的解决方案。 指出StringUtils.substring(yourString, 0, n)yourString.substring(0, n) 不同可能也很有用。前者来自StringUtils,而后者使用String.substring(如果结束索引超过字符串长度,则会出现异常)。 正如您在查看此方法的源代码时一样,它通过将结尾更改为长度来处理结尾大于长度的情况:if (end > str.length()) end = str.length(); StringUtils.substring(String s, int start, int len)的最后一个参数不是len,是end-Index。 StringUtils.substring("abc", 0, 4) = "abc",为我工作。谢谢!【参考方案5】:
String upToNCharacters = String.format("%."+ n +"s", str);

如果n 是一个变量,那就太糟糕了(所以你必须构造格式字符串),但如果是一个常量,那就很清楚了:

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

docs

【讨论】:

有趣的替代方案,虽然我无法想象曾经使用它,考虑到四年前给出的更传统的方法。 最佳答案,因为输入的String只读取一次,所以不需要将其存储在变量中,这样可以整齐地嵌入。【参考方案6】:

Apache Commons Lang 对此有一个 StringUtils.left 方法。

String upToNCharacters = StringUtils.left(s, n);

【讨论】:

这不应该是最好的解决方案吗?为什么没有很多人赞成这个? 可能是因为其他人的观点与您不同? :-) 这个答案比原来的提问日期晚了很多。 @DoWill:因为向您的可执行环境添加(其他)第 3 方库并不总是值得的。 @LarsH 但是 Apache Commons,正如它所填充的那样,已经包含在许多项目中。它不会为部分字符串切片添加另一个 3rd 方库。【参考方案7】:

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;

【讨论】:

@VolkanGüven 正是因为这句“ApacheCommons 让我感到惊讶”。我通过批评神圣的 ApacheCommons 库犯了罪。或者什么...

以上是关于如何在不检查大小或超出范围的情况下获取字符串的前 n 个字符?的主要内容,如果未能解决你的问题,请参考以下文章

使回文检查器保持字符串索引超出范围错误的一半,如何解决?

如何在不包含(或少量)背景像素的情况下调整对象内的矩形或调整其大小?

如何在不使用类名作为范围的情况下获取类中成员函数的地址?

向量迭代器 + 偏移超出范围

如何在不复制的情况下从 N 维容器中获取可迭代范围?

检测设备是不是超出 wifi 范围