Làm cách nào để xóa văn bản trong dấu ngoặc đơn bằng regex?


79

Tôi đang cố gắng xử lý một loạt tệp và sau đó tôi cần phải thay đổi để xóa thông tin không liên quan trong tên tệp; đáng chú ý, tôi đang cố gắng xóa văn bản bên trong dấu ngoặc đơn. Ví dụ:

filename = "Example_file_(extra_descriptor).ext"

và tôi muốn regex toàn bộ các tệp trong đó biểu thức dấu ngoặc đơn có thể nằm ở giữa hoặc ở cuối và có độ dài thay đổi.

Regex sẽ trông như thế nào? Cú pháp Perl hoặc Python sẽ được ưu tiên hơn.


Bạn có chắc rằng "extra_descriptor" không thể bao gồm dấu ")" không? Nếu nó có thể là vấn đề trở nên khó khăn hơn nhiều ...
dmckee --- cựu điều hành kitten

1
@dmckee: Sẽ khó hơn nếu các parens có thể được lồng vào nhau , mặc dù nếu bạn chỉ muốn loại bỏ mọi thứ giữa '(' và cuối cùng ')' đầu tiên thì không khó hơn nhiều: chỉ cần sử dụng một tham lam '. *' thay vì '. *?'.
j_random_hacker

2
@j_random_hacker Bạn nói đúng, khó hơn rất nhiều vì không thể nhận dạng các dấu ngoặc lồng nhau bằng FSM (bạn phải theo dõi mức độ lồng nhau là không giới hạn) và do đó không phải bằng regex. Để có thể thực hiện được, bạn phải giới hạn bản thân ở một mức giới hạn của việc làm tổ.
skyking

Câu trả lời:


133
s/\([^)]*\)//

Vì vậy, trong Python, bạn sẽ làm:

re.sub(r'\([^)]*\)', '', filename)

2
có lý do gì để thích hơn. *? hơn [^)] *
Kip

@Kip: không. Tôi không biết tại sao, nhưng. * Luôn là điều đầu tiên tôi nghĩ đến.
Can Berk Güder

@Kip:. *? không được xử lý bởi tất cả các trình phân tích cú pháp regex, trong khi [^)] * của bạn được xử lý bởi hầu hết tất cả chúng.
X-Istence

@Kip: Một lý do khác là backtracking.
Gumbo

13
. * nhận mọi thứ giữa dấu ngoặc đơn bên trái đầu tiên và dấu ngoặc đơn cuối cùng bên phải: 'a (b) c (d) e' sẽ trở thành 'ae'. [^)] * chỉ xóa giữa dấu ngoặc đầu tiên bên trái và dấu ngoặc đơn đầu tiên bên phải: 'ac (d) e'. Bạn cũng sẽ nhận được các hành vi khác nhau cho các parens lồng nhau.
daotoad

68

Mẫu khớp với các chuỗi con trong ngoặc đơn không có() chuỗi nào khác các ký tự ở giữa (như (xyz 123)in Text (abc(xyz 123)) là

\([^()]*\)

Chi tiết :

Xóa các đoạn mã:

  • JavaScript :string.replace(/\([^()]*\)/g, '')
  • PHP :preg_replace('~\([^()]*\)~', '', $string)
  • Perl :$s =~ s/\([^()]*\)//g
  • Python :re.sub(r'\([^()]*\)', '', s)
  • C # :Regex.Replace(str, @"\([^()]*\)", string.Empty)
  • VB.NET :Regex.Replace(str, "\([^()]*\)", "")
  • Java :s.replaceAll("\\([^()]*\\)", "")
  • Ruby :s.gsub(/\([^()]*\)/, '')
  • R :gsub("\\([^()]*\\)", "", x)
  • Lua :string.gsub(s, "%([^()]*%)", "")
  • Bash / sed :sed 's/([^()]*)//g'
  • Tcl :regsub -all {\([^()]*\)} $s "" result
  • C ++std::regex :std::regex_replace(s, std::regex(R"(\([^()]*\))"), "")
  • Mục tiêu-C :
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\([^()]*\\)" options:NSRegularExpressionCaseInsensitive error:&error]; NSString *modifiedString = [regex stringByReplacingMatchesInString:string options:0 range:NSMakeRange(0, [string length]) withTemplate:@""];
  • Swift :s.replacingOccurrences(of: "\\([^()]*\\)", with: "", options: [.regularExpression])


6

Nếu bạn không hoàn toàn cần phải sử dụng một regex, sử dụng xem xét sử dụng Perl của chữ :: Balanced để loại bỏ các dấu ngoặc đơn.

use Text::Balanced qw(extract_bracketed);

my ($extracted, $remainder, $prefix) = extract_bracketed( $filename, '()', '[^(]*' );

{   no warnings 'uninitialized';

    $filename = (defined $prefix or defined $remainder)
                ? $prefix . $remainder
                : $extracted;
}

Bạn có thể đang nghĩ, "Tại sao lại làm tất cả những điều này khi regex thực hiện thủ thuật trong một dòng?"

$filename =~ s/\([^}]*\)//;

Văn bản :: Điều khiển cân bằng dấu ngoặc đơn lồng nhau. Vì vậy $filename = 'foo_(bar(baz)buz)).foo'sẽ được giải nén đúng cách. Các giải pháp dựa trên regex được cung cấp ở đây sẽ không thành công trên chuỗi này. Người nào sẽ dừng lại ở dấu ngoặc đóng đầu tiên, và người kia sẽ ăn hết chúng.

   $filename =~ s/\([^}]*\)//;
   # returns 'foo_buz)).foo'

   $filename =~ s/\(.*\)//;
   # returns 'foo_.foo'

   # text balanced example returns 'foo_).foo'

Nếu một trong hai hành vi regex có thể chấp nhận được, hãy sử dụng regex - nhưng ghi lại các giới hạn và các giả định đang được thực hiện.


Mặc dù tôi biết bạn không thể phân tích cú pháp dấu ngoặc lồng nhau với regex (cổ điển), nhưng nếu bạn biết mình sẽ không bao giờ gặp phải dấu ngoặc lồng nhau, bạn có thể đơn giản hóa vấn đề thành một vấn đề CÓ THỂ thực hiện được với regexes và khá dễ dàng. Việc sử dụng công cụ phân tích cú pháp khi chúng ta không cần là quá mức cần thiết.
Chris Lutz

@Chris Lutz - Tôi nên nói "cân nhắc" hơn là "sử dụng" trong câu đầu tiên. Trong nhiều trường hợp, regex sẽ thực hiện công việc, đó là lý do tại sao tôi đã nói sử dụng regex nếu hành vi đó được chấp nhận.
daotoad

3

Nếu một đường dẫn có thể chứa dấu ngoặc đơn thì r'\(.*?\)'regex là không đủ:

import os, re

def remove_parenthesized_chunks(path, safeext=True, safedir=True):
    dirpath, basename = os.path.split(path) if safedir else ('', path)
    name, ext = os.path.splitext(basename) if safeext else (basename, '')
    name = re.sub(r'\(.*?\)', '', name)
    return os.path.join(dirpath, name+ext)

Theo mặc định, hàm bảo tồn các phần có dấu ngoặc đơn trong các phần thư mục và phần mở rộng của đường dẫn.

Thí dụ:

>>> f = remove_parenthesized_chunks
>>> f("Example_file_(extra_descriptor).ext")
'Example_file_.ext'
>>> path = r"c:\dir_(important)\example(extra).ext(untouchable)"
>>> f(path)
'c:\\dir_(important)\\example.ext(untouchable)'
>>> f(path, safeext=False)
'c:\\dir_(important)\\example.ext'
>>> f(path, safedir=False)
'c:\\dir_\\example.ext(untouchable)'
>>> f(path, False, False)
'c:\\dir_\\example.ext'
>>> f(r"c:\(extra)\example(extra).ext", safedir=False)
'c:\\\\example.ext'

2

Đối với những người muốn sử dụng Python, đây là một quy trình đơn giản giúp loại bỏ các chuỗi con có dấu ngoặc đơn, kể cả những chuỗi có dấu ngoặc đơn lồng nhau. Được rồi, nó không phải là regex, nhưng nó sẽ làm được việc!

def remove_nested_parens(input_str):
    """Returns a copy of 'input_str' with any parenthesized text removed. Nested parentheses are handled."""
    result = ''
    paren_level = 0
    for ch in input_str:
        if ch == '(':
            paren_level += 1
        elif (ch == ')') and paren_level:
            paren_level -= 1
        elif not paren_level:
            result += ch
    return result

remove_nested_parens('example_(extra(qualifier)_text)_test(more_parens).ext')

Tôi đã muốn tự mình viết loại bỏ dấu ngoặc lồng nhau, nhưng bạn đã tiết kiệm thời gian của tôi, cảm ơn bạn! 😊
AivanF.

1

Nếu bạn có thể sẵn sàng sử dụng sed(có thể thực thi từ bên trong chương trình của bạn, nó sẽ đơn giản như:

sed 's/(.*)//g'

Bạn chỉ đang nhóm biểu thức .*.
Gumbo

@Gumbo: Không, anh ấy không. Trong nhóm sed, "\ (... \)".
runrig

Xin lỗi. Không biết điều đó.
Gumbo

0
>>> import re
>>> filename = "Example_file_(extra_descriptor).ext"
>>> p = re.compile(r'\([^)]*\)')
>>> re.sub(p, '', filename)
'Example_file_.ext'

0

Mã Java:

Pattern pattern1 = Pattern.compile("(\\_\\(.*?\\))");
System.out.println(fileName.replace(matcher1.group(1), ""));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.