Regex สำหรับการแปลง CamelCase เป็น camel_case ใน java


86

ฉันเข้าใจว่าทำไมผลลัพธ์ที่ต้องการไม่ได้รับสำหรับการแปลงโดยใช้ regex สตริงเช่นFooBarการที่จะช่วยให้แทนFoo_Bar Foo_Bar_ฉันสามารถทำอะไรบางอย่างกับ String.substring substring(0, string.length() - 2)หรือเพียงแค่แทนที่อักขระตัวสุดท้าย แต่ฉันคิดว่ามีทางออกที่ดีกว่าสำหรับสถานการณ์ดังกล่าว

นี่คือรหัส:

String regex = "([A-Z][a-z]+)";
String replacement = "$1_";

"CamelCaseToSomethingElse".replaceAll(regex, replacement); 

/*
outputs: Camel_Case_To_Something_Else_
desired output: Camel_Case_To_Something_Else
*/

คำถาม: กำลังมองหาวิธีที่ดีกว่าเพื่อให้ได้ผลลัพธ์ที่ต้องการหรือไม่?


คำถามนี้คล้ายกับstackoverflow.com/questions/4886091/…
Paul Vargas

คำตอบ:


171

ดูคำถามนี้และCaseFormatจากฝรั่ง

ในกรณีของคุณสิ่งที่ต้องการ:

CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "SomeInput");

@eliocs คำถามไม่ได้ติดแท็ก android และ "neater way" .. ขอบคุณสำหรับการ

2
ลิงก์ CaseFormat ออฟไลน์อยู่ แทนที่อยู่ที่นี่
Anticom

66

ผูกตัวพิมพ์เล็กและตัวพิมพ์ใหญ่เป็นสองกลุ่มก็ใช้ได้

public  class Main
{
    public static void main(String args[])
    {
        String regex = "([a-z])([A-Z]+)";
        String replacement = "$1_$2";
        System.out.println("CamelCaseToSomethingElse"
                           .replaceAll(regex, replacement)
                           .toLowerCase());
    }
}

2
หมายเหตุ: หากอนุญาตให้ใช้คำตัวอักษรเดี่ยวในสตริงอินพุตเช่น "thisIsATest" โค้ดด้านบนจะพิมพ์ "this_is_atest" ฝรั่งในคำตอบที่ยอมรับจะให้ผลลัพธ์เป็น "this_is_a_test"
DtotheK

IBMIsMyCompanyหนึ่งนี้จะไม่ทำงานในการเริ่มต้นที่ชื่อแคปเช่น:
User3301

37

คุณสามารถใช้ข้อมูลโค้ดด้านล่าง:

String replaceAll = key.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();

จะเกิดอะไรขึ้นถ้าสตริงของฉันมีตัวเลข - mode3 จะลงเอยด้วย mode3 ในขณะที่ฉันต้องการ mode_3
Mike Stoddart

มันไม่ได้แปลงกรณีอูฐเหมือนMyUUIDขีดเส้นใต้อย่างถูกต้องฉันmy_uu_idเข้าใจแล้ว
User3301

6

ฉันไม่สามารถให้ RegEx ได้มันก็จะซับซ้อนมากอยู่ดี

ลองใช้ฟังก์ชันนี้ด้วยการจดจำตัวย่อโดยอัตโนมัติ

ขออภัย Guava lib ตรวจไม่พบตัวย่อตัวพิมพ์ใหญ่โดยอัตโนมัติดังนั้น "bigCAT" จะถูกแปลงเป็น "BIG_C_A_T"

/**
 * Convert to UPPER_UNDERSCORE format detecting upper case acronyms
 */
private String upperUnderscoreWithAcronyms(String name) {
    StringBuffer result = new StringBuffer();
    boolean begin = true;
    boolean lastUppercase = false;
    for( int i=0; i < name.length(); i++ ) {
        char ch = name.charAt(i);
        if( Character.isUpperCase(ch) ) {
            // is start?
            if( begin ) {
                result.append(ch);
            } else {
                if( lastUppercase ) {
                    // test if end of acronym
                    if( i+1<name.length() ) {
                        char next = name.charAt(i+1);
                        if( Character.isUpperCase(next) ) {
                            // acronym continues
                            result.append(ch);
                        } else {
                            // end of acronym
                            result.append('_').append(ch);
                        }
                    } else {
                        // acronym continues
                        result.append(ch);
                    }
                } else {
                    // last was lowercase, insert _
                    result.append('_').append(ch);
                }
            }
            lastUppercase=true;
        } else {
            result.append(Character.toUpperCase(ch));
            lastUppercase=false;
        }
        begin=false;
    }
    return result.toString();
}

5

ทำไมไม่จับคู่อักขระก่อนหน้าเป็นไม่ขึ้นต้นบรรทัด$?

String text = "CamelCaseToSomethingElse";
System.out.println(text.replaceAll("([^_A-Z])([A-Z])", "$1_$2"));

โปรดทราบว่าเวอร์ชันนี้ปลอดภัยที่จะดำเนินการกับสิ่งที่มีอูฐอยู่แล้ว


คุณกำลังพยายามใช้^และ$เป็นจุดยึดหรือไม่? เนื่องจากความหมายเปลี่ยนไปเมื่อคุณใส่ไว้ในคลาสอักขระ [^$_A-Z]ตรงกับตัวอักษรที่ไม่ใด ๆ$, _หรืออักษรตัวพิมพ์ใหญ่และฉันไม่คิดว่าสิ่งที่คุณหมาย
Alan Moore

ไม่ได้ตั้งใจที่จะเป็นจุดยึดฉันพยายามที่จะไม่จับคู่อักขระส่วนบน แต่$ถูกเพิ่มเข้ามาอย่างผิดพลาดเนื่องจากเป็นเทคนิคที่ฉันใช้กับชื่อคลาส
Brett Ryan

3

เพิ่มการยืนยันผู้มองที่มีความกว้างเป็นศูนย์

http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html

อ่านเอกสารสำหรับ (?=X)ฯลฯ

โดยส่วนตัวแล้วฉันจะแยกสตริงจริงๆแล้วรวมกันใหม่ สิ่งนี้อาจเร็วกว่าเมื่อทำถูกต้องและทำให้เข้าใจรหัสได้ง่ายกว่าการใช้เวทมนตร์ในการแสดงออกทั่วไป อย่าเข้าใจฉันผิด: ฉันชอบสำนวนปกติ แต่นี่ไม่ใช่นิพจน์ทั่วไปที่เรียบร้อยและไม่ใช่การเปลี่ยนแปลงนี้ไม่ใช่งาน regexp แบบคลาสสิก ท้ายที่สุดดูเหมือนว่าคุณต้องการพิมพ์ตัวพิมพ์เล็กด้วย?

การแฮ็กที่น่าเกลียด แต่รวดเร็วคือการแทนที่(.)([A-Z]+)ด้วย$1_$2แล้วตัวพิมพ์เล็กทั้งสตริงในภายหลัง (เว้นแต่คุณจะสามารถทำ regexps แบบขยายแบบ perl ซึ่งคุณสามารถใช้ตัวพิมพ์เล็กแทนได้โดยตรง!) ฉันยังคงพิจารณาแยกที่การเปลี่ยนแปลงจากล่างขึ้นบนจากนั้นจึงเปลี่ยนจากนั้นจึงเข้าร่วมเป็นวิธีที่เหมาะสมและอ่านง่ายที่สุดในการทำเช่นนี้


ใช่ในที่สุดฉันก็อยากให้เป็นตัวพิมพ์เล็กด้วย
ajmartin

ดังนั้นฉันจะแบ่งมันออกเป็นกลุ่มที่ตรงกัน[A-Z][a-z]*พิมพ์ตัวพิมพ์เล็กตัวแรกและเข้าร่วมอีกครั้ง หรือเคล็ดลับการแทนที่ + ตัวพิมพ์เล็กที่ฉันเพิ่งเพิ่มในการตอบกลับหลัก
มี QUIT - Anony-Mousse

2
public class ReplaceFromCameltoSnake {
    public static void main(String args[]){
        String s1=" totalAmountWithoutDiscount";  
        String replaceString=s1.replaceAll("([A-Z]+)","\\_$1").toLowerCase(); 
        System.out.println(replaceString);  
    }
}

$ 1- ใช้ในการสร้างกลุ่ม
abinash sahu

2

ไม่แน่ใจว่าเป็นไปได้ที่จะมีบางสิ่งที่โดดเดี่ยวด้วย regex บริสุทธิ์ โดยเฉพาะอย่างยิ่งเพื่อรองรับคำย่อ

ฉันได้สร้างฟังก์ชันเล็ก ๆ โดยได้รับแรงบันดาลใจจากคำตอบของ @radzimir ซึ่งรองรับคำย่อและไม่มีตัวอักษร:

จากhttps://gist.github.com/ebuildy/cf46a09b1ac43eea17c7621b7617ebcd :

private static String snakeCaseFormat(String name) {
    final StringBuilder result = new StringBuilder();

    boolean lastUppercase = false;

    for (int i = 0; i < name.length(); i++) {
        char ch = name.charAt(i);
        char lastEntry = i == 0 ? 'X' : result.charAt(result.length() - 1);
        if (ch == ' ' || ch == '_' || ch == '-' || ch == '.') {
            lastUppercase = false;

            if (lastEntry == '_') {
                continue;
            } else {
                ch = '_';
            }
        } else if (Character.isUpperCase(ch)) {
            ch = Character.toLowerCase(ch);
            // is start?
            if (i > 0) {
                if (lastUppercase) {
                    // test if end of acronym
                    if (i + 1 < name.length()) {
                        char next = name.charAt(i + 1);
                        if (!Character.isUpperCase(next) && Character.isAlphabetic(next)) {
                            // end of acronym
                            if (lastEntry != '_') {
                                result.append('_');
                            }
                        }
                    }
                } else {
                    // last was lowercase, insert _
                    if (lastEntry != '_') {
                        result.append('_');
                    }
                }
            }
            lastUppercase = true;
        } else {
            lastUppercase = false;
        }

        result.append(ch);
    }
    return result.toString();
}

1
นี่คือคำตอบที่มีคุณภาพซึ่งสามารถจัดการกับเคส edge ได้เกือบทั้งหมด
User3301

1
([A-Z][a-z\d]+)(?=([A-Z][a-z\d]+))

ควรค้นหาอักษรตัวใหญ่ตามด้วยตัวพิมพ์เล็ก ผู้มองเชิงบวกจะมองหาคำอื่นที่ขึ้นต้นด้วยอักษรตัวใหญ่ตามด้วยตัวพิมพ์เล็ก แต่จะไม่รวมไว้ในการจับคู่

ดูที่นี่: http://regexr.com?30ooo


0

ฉันต้องใช้สิ่งนี้เพื่อแปลงคีย์บางคีย์ในรูปแบบตัวพิมพ์ใหญ่อูฐเป็นตัวพิมพ์เล็กโดยมีขีดล่าง นิพจน์ทั่วไปที่ฉันคิดขึ้นคือ:

(?<!^|_|[A-Z])([A-Z])

ในภาษาอังกฤษย่อมาจากอักษรตัวใหญ่ซึ่งไม่ได้นำหน้าด้วยจุดเริ่มต้นของสตริงขีดล่างหรืออักษรตัวใหญ่อื่น

ในตัวอย่างด้านล่างอักขระที่เป็นตัวหนาคืออักขระที่ควรจับคู่โดยใช้นิพจน์ทั่วไปดังกล่าวข้างต้น:

  • อูฐC ase T o S omething E lse
  • อูฐC ase T o S omething E lse
  • camel_case_to_something_else
  • Camel_Case_To_Something_Else
  • CAMEL_CASE_TO_SOMETHING_ELSE

สังเกตว่านิพจน์ไม่มีผลต่อสตริงที่อยู่ในรูปแบบตัวพิมพ์เล็ก + ขีดล่าง

รูปแบบการแทนที่จะเป็น:

_l$1

ซึ่งหมายถึงตัวพิมพ์เล็กของกลุ่มการจับแรกกลุ่มการจับแรกเป็นอักษรตัวใหญ่ คุณสามารถลดสตริงทั้งหมดได้ในภายหลังเช่นกันเพื่อทำให้สองตัวอย่างสุดท้ายเป็นปกติจากรายการด้านบน

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