จะตรวจสอบได้อย่างไรว่า String มีเฉพาะ ASCII?


120

การโทรCharacter.isLetter(c)จะส่งกลับtrueหากอักขระเป็นตัวอักษร แต่มีวิธีค้นหาอย่างรวดเร็วว่ามีStringเพียงอักขระพื้นฐานของ ASCII หรือไม่?

คำตอบ:


128

ตั้งแต่Guava 19.0 เป็นต้นไปคุณสามารถใช้:

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

สิ่งนี้ใช้matchesAllOf(someString)วิธีการที่อาศัยวิธีการของโรงงานascii()แทนที่จะเป็นASCIIซิงเกิลตันที่เลิกใช้แล้วในขณะนี้

นี่ ASCII ประกอบด้วยอักขระ ASCII ทั้งหมดรวมทั้งตัวละครที่ไม่สามารถพิมพ์ต่ำกว่า0x20(เว้นวรรค) เช่นแท็บสายฟีด / การกลับมา แต่ยังBELมีรหัส0x07และมีรหัสDEL0x7F

รหัสนี้ใช้อักขระแทนจุดรหัสอย่างไม่ถูกต้องแม้ว่าจะมีการระบุจุดรหัสในข้อคิดเห็นของเวอร์ชันก่อนหน้าก็ตาม โชคดีที่อักขระที่จำเป็นในการสร้างจุดรหัสที่มีค่าU+010000หรือมากกว่านั้นใช้อักขระตัวแทนสองตัวที่มีค่าอยู่นอกช่วง ASCII ดังนั้นวิธีนี้ยังคงประสบความสำเร็จในการทดสอบ ASCII แม้กระทั่งสำหรับสตริงที่มีอิโมจิ

สำหรับ Guava เวอร์ชันก่อนหน้าโดยไม่มีascii()วิธีการที่คุณสามารถเขียน:

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

31
+1 แม้ว่าจะดีถ้าคุณไม่ต้องการไลบรารีของบุคคลที่สามอื่น แต่คำตอบของ Colin นั้นสั้นกว่าและอ่านง่ายกว่ามาก การแนะนำไลบรารีของบุคคลที่สามทำได้ดีและไม่ควรถูกลงโทษด้วยการโหวตเชิงลบ
Jesper

1
ฉันควรชี้ให้เห็นว่า CharMatchers นั้นทรงพลังอย่างไม่น่าเชื่อจริงๆและสามารถทำอะไรได้มากกว่านี้ นอกจากนี้ยังมี CharMatchers ที่กำหนดไว้ล่วงหน้าอีกมากมายนอกเหนือจาก ASCII และวิธีการจากโรงงานที่ยอดเยี่ยมสำหรับการสร้างแบบกำหนดเอง
ColinD

7
CharMatcher.ASCIIเลิกใช้แล้วและกำลังจะถูกลบออกในเดือนมิถุนายน 2018
thisarattr

108

คุณสามารถทำมันได้ด้วย 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
      */
  }
}

ตรวจหาอักขระที่ไม่ใช่ ASCII ในสตริง


10
ฉันไม่คิดว่าเป็นความคิดที่ดีที่จะทำให้ CharsetEncoder คงที่เนื่องจากตามเอกสาร "อินสแตนซ์ของคลาสนี้ไม่ปลอดภัยสำหรับการใช้เธรดพร้อมกันหลายชุด"
pm_labs

@paul_sns คุณพูดถูก CharsetEncoder ไม่ปลอดภัยต่อเธรด (แต่ Charset เป็น) ดังนั้นจึงไม่ควรทำให้มันคงที่
RealHowTo

12
ด้วย Java 1.7 หรือสูงกว่าสามารถใช้StandardCharsets.US_ASCIIแทนCharset.forName("US-ASCII").
Julian Lettner

@RealHowTo วิธีแก้ปัญหาที่ถูกต้องไม่ควรต้องอาศัยความคิดเห็นดูแลแก้ไขปัญหานี้และอาจใช้วิธีการ oneliner ตามStandardCharsets? ฉันสามารถโพสต์คำตอบอื่นได้ แต่ฉันอยากจะแก้ไขคำตอบที่ชื่นชมนี้
Maarten Bodewes

77

นี่เป็นอีกวิธีหนึ่งที่ไม่ได้ขึ้นอยู่กับไลบรารี แต่ใช้ regex

คุณสามารถใช้บรรทัดเดียวนี้:

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

15
\\ A - จุดเริ่มต้นของการป้อนข้อมูล ... \\ p {ASCII} * - อักขระ ASCII ใดก็ได้ทุกครั้ง ... \\ z - สิ้นสุดการป้อนข้อมูล
Arne Deutsch

@ArneDeutsch คุณรังเกียจไหมถ้าฉันจะปรับปรุงคำตอบและรวมการอ้างอิงถึง\P{Print}และ\P{Graph}+ คำอธิบาย ทำไมคุณถึงต้องการ\Aและ\z?
Maarten Bodewes

regex คืออะไร? ฉันรู้ว่า $ เป็นจุดสิ้นสุดของสตริง ^ เริ่มต้นไม่เคยได้ยินเกี่ยวกับ \\ A \\ p \\ z อย่างใดอย่างหนึ่งคุณช่วยแนบข้อมูลอ้างอิงกับ javadoc ได้ไหม
deathangel908

@ deathangel908 \ A คือจุดเริ่มต้นของการป้อนข้อมูล \ z คือจุดสิ้นสุดของอินพุต ^ และ $ ทำงานแตกต่างกันในโหมด MULTILINE และ DOTALL เปลี่ยนพฤติกรรมของ \ A และ \ z ดูstackoverflow.com/a/3652402/1003157
Raymond Naseef

58

วนซ้ำในสตริงและตรวจสอบให้แน่ใจว่าอักขระทั้งหมดมีค่าน้อยกว่า 128

Java Strings ถูกเข้ารหัสตามแนวคิดเป็น UTF-16 ใน UTF-16 ชุดอักขระ ASCII จะถูกเข้ารหัสเป็นค่า 0 - 127 และการเข้ารหัสสำหรับอักขระที่ไม่ใช่ ASCII (ซึ่งอาจประกอบด้วยอักขระ Java มากกว่าหนึ่งตัว) รับประกันว่าจะไม่รวมตัวเลข 0 - 127


27
ด้วย Java 1.8 คุณสามารถทำได้:str.chars().allMatch(c -> c < 128)
Julian Lettner

7
หากคุณต้องการตัวอักษรพิมพ์คุณอาจต้องการที่จะทดสอบc >= 0x20 && c < 0x7Fเป็น 32 ค่าแรกของการเข้ารหัส 7 บิตเป็นตัวควบคุมและค่าสุดท้าย (0x7F) DELเป็น
Maarten Bodewes

15

หรือคุณคัดลอกรหัสจากคลาส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-char-unicode เนื่องจาก 1st-char คือ> = U + D800
k3b

แต่โปรดทราบว่าประกอบด้วยอักขระที่ไม่สามารถพิมพ์ได้ใน ASCII (ซึ่งถูกต้อง แต่อาจไม่เป็นที่คาดหวัง) มันเป็นไปได้แน่นอนที่จะใช้โดยตรงreturn falseแทนการใช้และisASCII = false break
Maarten Bodewes

1
นี่คือรหัสจาก Oracle JDK การคัดลอกอาจทำให้เกิดปัญหาทางกฎหมาย
Arne Deutsch

11

commons-lang3 จาก Apache มียูทิลิตี้ / วิธีอำนวยความสะดวกที่มีค่าสำหรับ 'ปัญหา' ทุกประเภทรวมถึงปัญหานี้ด้วย

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

1
โปรดทราบว่า isAsciiPrintable จะส่งกลับเท็จหากสตริงมีอักขระแท็บหรือตัวดึงข้อมูลบรรทัด (\ t \ r \ n)
TampaHaze

@TampaHaze นั่นเป็นเพราะภายในการตรวจสอบค่าอักขระทุกตัวจะอยู่ระหว่าง 32 ถึง 127 ฉันคิดว่าผิด เราควรตรวจสอบตั้งแต่ 0 ถึง 127
therealprashant

1
@therealprashant ถ้าชื่อเมธอด isAscii ฉันเห็นด้วยกับคุณ แต่วิธีการที่ถูกตั้งชื่อว่า isAsciiPrintable หมายความว่าอาจมีการยกเว้นอักขระ 0 ถึง 31 โดยเจตนา
TampaHaze

4

ลองสิ่งนี้:

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

"ลองสิ่งนี้" จะได้รับการโหวตลดลงเสมอ สิ่งนี้ทำอะไร? มีอะไรบ้างและอะไรบ้างที่ไม่รวม? จะได้รับการโหวตลดลงเนื่องจากคุณเพิ่มขนาดหน่วยความจำเป็นสองเท่าด้วยเช่นกัน
Maarten Bodewes

1

วนซ้ำผ่านสตริงและใช้ charAt () เพื่อรับ char จากนั้นถือว่าเป็น int และดูว่ามีค่า Unicode (ชุดย่อยของ ASCII) ที่คุณชอบหรือไม่

แตกในตอนแรกที่คุณไม่ชอบ


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

คำตอบรหัสเท่านั้นโปรดระบุว่าจะทำอย่างไรกล่าวคือมีอักขระที่ไม่สามารถพิมพ์ได้และอักขระที่ไม่ได้กำหนด (0x7F) หากคุณทำการตรวจสอบนี้
Maarten Bodewes

อันนี้อาจทำให้ฉันเบื่อหลังจากโปรแกรมที่ใช้งานมายาวนานของฉันไม่พบตัวละครที่น่าสนใจ charAtส่งคืน a char. คุณสามารถทดสอบโดยตรงได้หรือไม่ว่าประเภทcharมากกว่า int โดยไม่ต้องแปลงเป็น int ก่อนหรือการทดสอบของคุณทำการครอบคลุมโดยอัตโนมัติหรือไม่ บางทีคุณอาจทำได้และอาจจะทำได้? ฉันเดินไปข้างหน้าและแปลงนี้เพื่อ int if ((int)s.charAt(i) > 127)เช่นดังนั้น: ไม่แน่ใจว่าผลลัพธ์ของฉันแตกต่างกันหรือไม่ แต่ฉันรู้สึกดีกว่าที่ปล่อยให้มันทำงาน เราจะได้เห็น: - \
harperville

0

มันเป็นไปได้ ปัญหาสวย

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

public class EncodingTest {

    static CharsetEncoder asciiEncoder = Charset.forName("US-ASCII")
            .newEncoder();

    public static void main(String[] args) {

        String testStr = "¤EÀsÆW°ê»Ú®i¶T¤¤¤ß3¼Ó®i¶TÆU2~~KITEC 3/F Rotunda 2";
        String[] strArr = testStr.split("~~", 2);
        int count = 0;
        boolean encodeFlag = false;

        do {
            encodeFlag = asciiEncoderTest(strArr[count]);
            System.out.println(encodeFlag);
            count++;
        } while (count < strArr.length);
    }

    public static boolean asciiEncoderTest(String test) {
        boolean encodeFlag = false;
        try {
            encodeFlag = asciiEncoder.canEncode(new String(test
                    .getBytes("ISO8859_1"), "BIG5"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return encodeFlag;
    }
}

0

สิ่งนี้จะคืนค่าเป็นจริงหาก String มีเฉพาะอักขระ ASCII และเป็นเท็จเมื่อไม่มี

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

หากคุณต้องการลบที่ไม่ใช่ ASCII นี่คือตัวอย่าง:

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

-2
//return is uppercase or lowercase
public boolean isASCIILetter(char c) {
  return (c > 64 && c < 91) || (c > 96 && c < 123);
}

โค้ดตอบได้ด้วยเวทมนตร์ 4 ตัวเท่านั้นและไม่มีคำอธิบายว่ามันทำอะไร กรุณาปรับเปลี่ยน
Maarten Bodewes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.