การใช้นิพจน์ปกติเพื่อแยกค่าใน Java


169

ฉันมีหลายสายในรูปแบบคร่าวๆ:

[some text] [some number] [some more text]

ฉันต้องการแยกข้อความใน [บางหมายเลข] โดยใช้คลาส Java Regex

ฉันรู้ว่าฉันต้องการใช้นิพจน์ปกติอย่างไร (แม้ว่าคำแนะนำทั้งหมดยินดีต้อนรับ) สิ่งที่ฉันสนใจจริงๆคือการเรียก Java เพื่อใช้สตริง regex และใช้กับแหล่งข้อมูลเพื่อสร้างมูลค่า [หมายเลขบางหมายเลข]

แก้ไข: ฉันควรเพิ่มว่าฉันสนใจเพียง [หมายเลขหนึ่ง] (โดยทั่วไปตัวอย่างแรก) สตริงที่มานั้นสั้นและฉันจะไม่มองหาเหตุการณ์ที่เกิดขึ้นจำนวนมาก [บางหมายเลข]


11
... และตอนนี้ฉันไม่ไปค้นคว้า ลองดูว่า SO สามารถหาคำตอบให้ฉันก่อนที่ฉันจะคิดออกเองหรือเปล่า :-P
Craig Walker

นี่เป็นคำถามสัมภาษณ์ที่ บริษัท การธนาคาร / การลงทุน / การค้าสำหรับวิศวกรรมซอฟต์แวร์ใช่ไหม? : P
ennth

@ nennth ไม่ได้ปิด! มันเป็นรหัสการผลิตในเว็บไซต์ขนาดเล็ก Biz ... หลายเดือนที่ผ่านมา
Craig Walker

1
ฉันถูกถามคำถามที่เหมือนกันเกือบทั้งหมดในการสอบการเข้ารหัสของ JP Morgan Chase Software Engineering เมื่อไม่กี่วันที่ผ่านมา: P
ennth

คำตอบ:


316

ตัวอย่างเต็มรูปแบบ:

private static final Pattern p = Pattern.compile("^([a-zA-Z]+)([0-9]+)(.*)");
public static void main(String[] args) {
    // create matcher for pattern p and given string
    Matcher m = p.matcher("Testing123Testing");

    // if an occurrence if a pattern was found in a given string...
    if (m.find()) {
        // ...then you can use group() methods.
        System.out.println(m.group(0)); // whole matched expression
        System.out.println(m.group(1)); // first expression from round brackets (Testing)
        System.out.println(m.group(2)); // second one (123)
        System.out.println(m.group(3)); // third one (Testing)
    }
}

เนื่องจากคุณกำลังค้นหาหมายเลขแรกคุณสามารถใช้ regexp ดังกล่าว:

^\D+(\d+).*

และm.group(1)จะส่งคืนหมายเลขแรกให้คุณ โปรดทราบว่าหมายเลขที่เซ็นชื่อสามารถมีเครื่องหมายลบได้:

^\D+(-?\d+).*

62
อย่าลืมนำวัตถุ Patter มาใช้ซ้ำ การรวบรวมลายใช้เวลามาก
Rastislav Komara

14
ตกลง โดยปกติฉันจะกำหนดรูปแบบเป็นรูปแบบคงที่สุดท้ายส่วนตัวแบบ PATTERN = Pattern.compile ("... "); แต่นั่นเป็นเพียงฉัน
Allain Lalonde

6
เราสามารถใช้รูปแบบ p = Pattern.compile ("\\ d +");
javaMan

15
นี่เป็นคำตอบที่ไม่ดีหากไม่มีคำอธิบาย
Martin Spamer

คุณสามารถนำ Matcher กลับมาใช้ใหม่ได้ โทรหาวิธีการรีเซ็ตของ Matcher ระหว่างการใช้งานแต่ละครั้ง หากคุณกำลังแชร์ตัวจับคู่ข้ามหลายเธรดที่เกิดขึ้นพร้อมกันคุณควรซิงโครไนซ์การดำเนินการ
Marquez

41
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex1 {
    public static void main(String[]args) {
        Pattern p = Pattern.compile("\\d+");
        Matcher m = p.matcher("hello1234goodboy789very2345");
        while(m.find()) {
            System.out.println(m.group());
        }
    }
}

เอาท์พุท:

1234
789
2345

คำถามนี้ถามเฉพาะการเกิดขึ้นครั้งแรกของตัวเลขเท่านั้น
NoBrainer

34

Allain นั้นมีจาวาโค้ดอยู่แล้วดังนั้นคุณสามารถใช้มันได้ อย่างไรก็ตามการแสดงออกของเขาตรงกับเฉพาะในกรณีที่ตัวเลขของคุณจะนำหน้าด้วยตัวอักษรคำ

"(\\d+)"

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

หากคุณคาดหวังว่ามันจะถูกชดเชยด้วยช่องว่างมันจะทำให้ชัดเจนยิ่งขึ้นในการระบุ

"\\s+(\\d+)\\s+"

อาจจะดีกว่า

หากคุณต้องการทั้งสามส่วนสิ่งนี้จะทำ:

"(\\D+)(\\d+)(.*)"

แก้ไขการแสดงออกที่กำหนดโดย Allain และแจ็คขอแนะนำว่าคุณต้องระบุกลุ่มย่อยบางส่วนของตัวเลขที่ไม่ใช่ในเพื่อที่จะจับหลัก ถ้าคุณบอกเอนจิ้นของ regex ที่คุณกำลังมองหาอยู่\dมันจะไม่สนใจทุกสิ่งทุกอย่างก่อนตัวเลข หาก J หรือ A แสดงออกเหมาะกับรูปแบบของคุณแล้วการแข่งขันทั้งหมดเท่ากับสายป้อน และไม่มีเหตุผลที่จะระบุ มันอาจจะช้าลงถ้าหากมันไม่ได้ถูกมองข้ามทั้งหมด


คุณสามารถทดสอบสมมติฐานของ Axemans ได้ด้วยการรันการทดสอบตัวอย่างและตรวจสอบประสิทธิภาพของโซลูชัน vs. A / J ของเขา
anjanb

คุณไม่จำเป็นต้องระบุจุดเริ่มต้นและจุดสิ้นสุดของสตริง ไม่อย่างนั้นอย่างเช่น 124xxx123xxx จะจับคู่แม้ว่ามันจะไม่เข้ากับไวยากรณ์ของเขา หรือเป็น ^ และ $ โดยนัย?
Allain Lalonde

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

11

นอกเหนือจากPatternแล้วคลาสJava Stringยังมีวิธีการหลายอย่างที่สามารถทำงานกับนิพจน์ทั่วไปได้ในกรณีของคุณรหัสจะเป็น:

"ab123abc".replaceFirst("\\D*(\\d*).*", "$1")

โดยที่\\Dเป็นอักขระที่ไม่ใช่ตัวเลข


10

ใน Java 1.4 และสูงกว่า:

String input = "...";
Matcher matcher = Pattern.compile("[^0-9]+([0-9]+)[^0-9]+").matcher(input);
if (matcher.find()) {
    String someNumberStr = matcher.group(1);
    // if you need this to be an int:
    int someNumberInt = Integer.parseInt(someNumberStr);
}

8

ฟังก์ชันนี้รวบรวมลำดับที่ตรงกันทั้งหมดจากสตริง ในตัวอย่างนี้ใช้ที่อยู่อีเมลทั้งหมดจากสตริง

static final String EMAIL_PATTERN = "[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
        + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})";

public List<String> getAllEmails(String message) {      
    List<String> result = null;
    Matcher matcher = Pattern.compile(EMAIL_PATTERN).matcher(message);

    if (matcher.find()) {
        result = new ArrayList<String>();
        result.add(matcher.group());

        while (matcher.find()) {
            result.add(matcher.group());
        }
    }

    return result;
}

เพราะmessage = "adf@gmail.com, <another@osiem.osiem>>>> lalala@aaa.pl"มันจะสร้างรายชื่อ 3 องค์ประกอบ


3

ลองทำสิ่งนี้:

Pattern p = Pattern.compile("^.+(\\d+).+");
Matcher m = p.matcher("Testing123Testing");

if (m.find()) {
    System.out.println(m.group(1));
}

3
-1 เพราะ.+ตัวละครกินอย่างตะกละตะกลาม, \d+เพียงจับจาก"3" "123"นอกจากนี้ในตัวอักษรสตริงคุณจะต้องหลีกเลี่ยงแบ็กสแลช (ตัวอย่างของคุณจะไม่คอมไพล์)
Bart Kiers

3

ทางออกที่ง่าย

// Regexplanation:
// ^       beginning of line
// \\D+    1+ non-digit characters
// (\\d+)  1+ digit characters in a capture group
// .*      0+ any character
String regexStr = "^\\D+(\\d+).*";

// Compile the regex String into a Pattern
Pattern p = Pattern.compile(regexStr);

// Create a matcher with the input String
Matcher m = p.matcher(inputStr);

// If we find a match
if (m.find()) {
    // Get the String from the first capture group
    String someDigits = m.group(1);
    // ...do something with someDigits
}

โซลูชันในคลาส Util

public class MyUtil {
    private static Pattern pattern = Pattern.compile("^\\D+(\\d+).*");
    private static Matcher matcher = pattern.matcher("");

    // Assumptions: inputStr is a non-null String
    public static String extractFirstNumber(String inputStr){
        // Reset the matcher with a new input String
        matcher.reset(inputStr);

        // Check if there's a match
        if(matcher.find()){
            // Return the number (in the first capture group)
            return matcher.group(1);
        }else{
            // Return some default value, if there is no match
            return null;
        }
    }
}

...

// Use the util function and print out the result
String firstNum = MyUtil.extractFirstNumber("Testing4234Things");
System.out.println(firstNum);

1

ดูว่าคุณสามารถทำได้โดยใช้ StringTokenizer

String str = "as:"+123+"as:"+234+"as:"+345;
StringTokenizer st = new StringTokenizer(str,"as:");

while(st.hasMoreTokens())
{
  String k = st.nextToken();    // you will get first numeric data i.e 123
  int kk = Integer.parseInt(k);
  System.out.println("k string token in integer        " + kk);

  String k1 = st.nextToken();   //  you will get second numeric data i.e 234
  int kk1 = Integer.parseInt(k1);
  System.out.println("new string k1 token in integer   :" + kk1);

  String k2 = st.nextToken();   //  you will get third numeric data i.e 345
  int kk2 = Integer.parseInt(k2);
  System.out.println("k2 string token is in integer   : " + kk2);
}

เนื่องจากเรากำลังนำข้อมูลตัวเลขเหล่านี้มาเป็นตัวแปรสามตัวเราจึงสามารถใช้ข้อมูลนี้ได้ทุกที่ในรหัส (สำหรับการใช้งานเพิ่มเติม)


0

วิธีการเกี่ยวกับ[^\\d]*([0-9]+[\\s]*[.,]{0,1}[\\s]*[0-9]*).*ฉันคิดว่ามันจะดูแลตัวเลขที่มีส่วนที่เป็นเศษส่วน ฉันรวมช่องว่างสีขาวและรวม,เป็นตัวแยกที่เป็นไปได้ ฉันกำลังพยายามดึงตัวเลขออกมาจากสตริงรวมถึงการลอยและคำนึงถึงว่าผู้ใช้อาจทำผิดพลาดและรวมช่องว่างสีขาวในขณะที่พิมพ์ตัวเลข


0

บางครั้งคุณสามารถใช้วิธีง่าย ๆ . split ("REGEXP") ได้ใน java.lang.String ตัวอย่างเช่น:

String input = "first,second,third";

//To retrieve 'first' 
input.split(",")[0] 
//second
input.split(",")[1]
//third
input.split(",")[2]

0
Pattern p = Pattern.compile("(\\D+)(\\d+)(.*)");
Matcher m = p.matcher("this is your number:1234 thank you");
if (m.find()) {
    String someNumberStr = m.group(2);
    int someNumberInt = Integer.parseInt(someNumberStr);
}

1
โปรดแก้ไขด้วยข้อมูลเพิ่มเติม รหัสเท่านั้นและคำตอบ "ลองนี้" จะหมดกำลังใจเพราะพวกเขาไม่มีเนื้อหาที่ค้นหาได้และไม่อธิบายว่าทำไมคนควร "ลองนี้" เราใช้ความพยายามที่นี่เพื่อเป็นแหล่งข้อมูลสำหรับความรู้
Brian Tompsett - 汤莱恩

1
ลงคะแนนเพียงทำซ้ำคำตอบที่ถูกต้องที่ได้รับมาเป็นเวลานานโดยไม่ต้องเพิ่มค่าเพิ่มเติมใด ๆ
Forage

-1

หากคุณกำลังอ่านจากไฟล์สิ่งนี้จะช่วยคุณได้

              try{
             InputStream inputStream = (InputStream) mnpMainBean.getUploadedBulk().getInputStream();
             BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
             String line;
             //Ref:03
             while ((line = br.readLine()) != null) {
                if (line.matches("[A-Z],\\d,(\\d*,){2}(\\s*\\d*\\|\\d*:)+")) {
                     String[] splitRecord = line.split(",");
                     //do something
                 }
                 else{
                     br.close();
                     //error
                     return;
                 }
             }
                br.close();

             }
         }
         catch (IOException  ioExpception){
             logger.logDebug("Exception " + ioExpception.getStackTrace());
         }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.