如何检查一个字符串是否只包含ASCII字符?

144

调用Character.isLetter(c)如果字符是字母将返回true。但是有没有一种快速的方法可以找到一个String是否只包含ASCII基本字符?

14个回答

137

Guava 19.0 开始,您可以使用:

boolean isAscii = CharMatcher.ascii().matchesAllOf(someString);

这里使用了matchesAllOf(someString)方法,它依赖于工厂方法ascii()而不是现在已弃用的ASCII单例。

这里的ASCII包括所有ASCII字符,包括小于0x20(空格)的不可打印字符,例如制表符、换行/回车,以及代码为0x07BEL和代码为0x7FDEL

即使先前版本的注释中指示了代码点,但此代码错误地使用了字符而不是代码点。幸运的是,创建值为U+010000或更高的代码点所需的字符使用了两个超出ASCII范围的代理字符。因此,该方法仍然可以成功地测试ASCII,即使字符串包含表情符号。

对于没有ascii()方法的早期Guava版本,您可以编写以下内容:

boolean isAscii = CharMatcher.ASCII.matchesAllOf(someString);

34
虽然不需要另一个第三方库会更好,但Colin的答案更简短,更易读。建议使用第三方库是完全可以的,不应该因此收到负面评价。 - Jesper
1
我还应该指出,CharMatchers 真的非常强大,可以做比这更多的事情。此外,除了 ASCII 之外,还有许多预定义的 CharMatchers,以及用于创建自定义 CharMatchers 的优秀工厂方法。 - ColinD
7
CharMatcher.ASCII现已被弃用,并将于2018年6月被删除。 - thisarattr

129

你可以使用java.nio.charset.Charset来完成它。

import java.nio.charset.Charset;

public class StringUtils {
  
  public static boolean isPureAscii(String v) {
    return Charset.forName("US-ASCII").newEncoder().canEncode(v);
    // or "ISO-8859-1" for ISO Latin 1
    // or StandardCharsets.US_ASCII with JDK1.7+
  }

  public static void main (String args[])
    throws Exception {

     String test = "Réal";
     System.out.println(test + " isPureAscii() : " + StringUtils.isPureAscii(test));
     test = "Real";
     System.out.println(test + " isPureAscii() : " + StringUtils.isPureAscii(test));
     
     /*
      * output :
      *   Réal isPureAscii() : false
      *   Real isPureAscii() : true
      */
  }
}

11
我认为将CharsetEncoder设为静态的不是一个好主意,因为根据文档,“此类的实例不适用于多个并发线程使用”。 - pm_labs
@paul_sns,你是对的,CharsetEncoder不是线程安全的(但Charset是),所以将其设为静态的不是一个好主意。 - RealHowTo
17
使用Java 1.7或更高版本,可以使用StandardCharsets.US_ASCII代替Charset.forName("US-ASCII") - Julian Lettner
@RealHowTo 正确的解决方案不应该依赖于注释,请修复此问题,也许可以使用基于StandardCharsets的一行代码方法?我可以发布另一个答案,但我宁愿修复这个备受赞赏的答案。 - Maarten Bodewes

84

以下是另一种不依赖库且使用正则表达式的方法。

您可以使用下面这行代码:

text.matches("\\A\\p{ASCII}*\\z")

完整示例程序:

public class Main {
    public static void main(String[] args) {
        char nonAscii = 0x00FF;
        String asciiText = "Hello";
        String nonAsciiText = "Buy: " + nonAscii;
        System.out.println(asciiText.matches("\\A\\p{ASCII}*\\z"));
        System.out.println(nonAsciiText.matches("\\A\\p{ASCII}*\\z"));
    }
}

理解正则表达式:

  • li \\A:输入的起始位置
  • \\p{ASCII}:任何 ASCII 字符
  • *:所有重复
  • \\z:输入的终止位置

17
\A - 输入的开头...\p{ASCII}* - 任意次数的任何ASCII字符...\z - 输入的结尾 - Arne Deutsch
@ArneDeutsch,您介意我改进答案并包含对\P{Print}\P{Graph}的引用以及描述吗?为什么需要\A\z - Maarten Bodewes
那个正则表达式是什么?我知道 $ 表示字符串结尾,^ 表示开头,但从未听说过 \A \p \z,你能否附上 javadoc 的参考资料? - deathangel908
@deathangel908 \A 是输入的开始。 \z 是输入的结束。在MULTILINE模式下,^和$的行为不同,并且DOTALL会改变\A和\z的行为。请参见https://dev59.com/JXA65IYBdhLWcg3wvxeE#3652402。 - Raymond Naseef

64

迭代字符串并确保所有字符的值小于128。

Java字符串在概念上被编码为UTF-16。 在UTF-16中,ASCII字符集被编码为值0-127,任何非ASCII字符的编码(可能由多个Java字符组成)都保证不包含数字0-127.


32
在Java 1.8中,您可以这样做:str.chars().allMatch(c -> c < 128)。意思是检查字符串中的所有字符是否都是ASCII码(即小于128)。 - Julian Lettner
9
如果你想要可打印的字符,你可能需要测试一下 c >= 0x20 && c < 0x7F。因为在七位编码中的前32个值是控制字符,最后一个值 (0x7F) 是 DEL。请注意不改变原文的意思。 - Maarten Bodewes

17

或者您可以从IDN类中复制代码。

// to check if a string only contains US-ASCII code point
//
private static boolean isAllASCII(String input) {
    boolean isASCII = true;
    for (int i = 0; i < input.length(); i++) {
        int c = input.charAt(i);
        if (c > 0x7F) {
            isASCII = false;
            break;
        }
    }
    return isASCII;
}

1
这甚至适用于2个字符的Unicode,因为第一个字符大于或等于U+D800。 - k3b
但请注意,它包含ASCII中的不可打印字符(这是正确的,但可能不是预期的)。当然,直接使用return false而不是使用isASCII = falsebreak也是可以的。 - Maarten Bodewes
1
这是来自Oracle JDK的代码。复制可能会引起法律问题。 - Arne Deutsch

11

Apache 的 commons-lang3 包含了很多有用的实用程序/便利方法,可以解决各种“问题”,包括此问题。

System.out.println(StringUtils.isAsciiPrintable("!@£$%^&!@£$%^"));

1
请注意,如果字符串包含制表符或换行符(\t \r \n),则 isAsciiPrintable 将返回 false。 - TampaHaze
@TampaHaze 这是因为在内部,它检查每个字符值是否介于32到127之间。我认为这是错误的。我们应该从0到127进行检查。 - therealprashant
2
@therealprashant 如果方法名称是 isAscii,我会同意你的看法。但是命名为 isAsciiPrintable 的方法意味着他们可能有意地排除了字符 0 到 31。 - TampaHaze

4

试一下这个:

for (char c: string.toCharArray()){
  if (((int)c)>127){
    return false;
  } 
}
return true;

1
“Try this”总是会被踩。这个代码段到底是做什么的?它包含了哪些内容,又有哪些不包含呢?顺便提一下,如果你将内存大小翻倍,也会被踩。 - Maarten Bodewes

3
private static boolean isASCII(String s) 
{
    for (int i = 0; i < s.length(); i++) 
        if (s.charAt(i) > 127) 
            return false;
    return true;
}

1
只回答代码,说明它的作用,即包括不可打印字符和未定义字符(0x7F),如果进行此检查。 - Maarten Bodewes
这个问题可能会困扰我,因为我的长时间运行的程序未能找到任何感兴趣的字符。charAt返回一个char。你能直接测试一个类型为char的变量是否大于一个int吗?还是你的测试会自动进行转换?也许你可以,也许它确实可以?我已经将其转换为int,如下所示:if ((int)s.charAt(i) > 127)。不确定我的结果是否有任何不同,但我对让它运行感到更加放心。我们拭目以待 :-\ - harperville
这似乎可行,并且在一系列相当不科学的本地微基准测试中,这是我最快的方式。使用“toCharArray”的类似方法会分配一个数组,因此性能比这个差。另一个小优化似乎是将lenght()提取到本地变量中。 - centic

3

3

如果字符串仅包含ASCII字符,则返回true,否则返回false。

最初的回答:This will return true if String only contains ASCII characters and false when it does not

Charset.forName("US-ASCII").newEncoder().canEncode(str)

如果你想要移除非ASCII字符,这是代码片段:

if(!Charset.forName("US-ASCII").newEncoder().canEncode(str)) {
                        str = str.replaceAll("[^\\p{ASCII}]", "");
                    }

香草Java,易于阅读,这个答案有什么不喜欢的呢?尽管为了避免在“US-ASCII”中出现拼写错误:StandardCharsets.US_ASCII.newEncoder().canEncode(str) - user2077221
你可以使用更简单的方式来代替 [^\\p{ASCII}],即 \\P{ASCII}。大写字母 \P 是小写字母 \p 的补集。 - Ahmet

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