Як я можу видалити текст у дужках за допомогою регулярного виразу?


79

Я намагаюся обробляти купу файлів, і мені потрібно змінити, щоб видалити сторонні дані з імен файлів; зокрема, я намагаюся видалити текст у дужках. Наприклад:

filename = "Example_file_(extra_descriptor).ext"

і я хочу згенерувати цілу купу файлів, де вираз у дужках може бути посередині або в кінці, і змінної довжини.

Як би виглядав регулярний вираз? Синтаксис Perl або Python буде кращим.


Ви впевнені, що "extra_descriptor" не може включати ")"? Якщо це можливо, проблема стає набагато складнішою ...
dmckee --- кошеня екс-модератора

1
@dmckee: Це важче , якщо круглі дужки можуть бути вкладеними , хоча , якщо ви просто хочете , щоб позбутися від усього , між першим «(» і останній «)» це не набагато складніше: «*» просто використовувати жадібний замість '. *?'.
j_random_hacker

2
@j_random_hacker Ви маєте рацію, це набагато складніше, оскільки вкладені дужки не можуть бути розпізнані FSM (ви повинні відстежувати необмежений рівень вкладеності), а отже, не регулярним виразом. Щоб це було можливо, вам доведеться обмежитися обмеженим рівнем вкладеності.
skyking

Відповіді:


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

Отже, у Python ви зробите:

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

2
чи є якась причина віддавати перевагу. *? понад [^)] *
Кіп,

@Kip: ні. Не знаю чому, але. * - це завжди перше, що спадає на думку.
Can Berk Güder

@Kip:. *? обробляється не всіми синтаксичними аналізаторами регулярних виразів, тоді як вашим [^)] * обробляють майже всі.
X-Istence,

@Kip: Інша причина - зворотне відстеження.
Гамбо,

13
. * отримує все між першим лівим та останнім правим: "a (b) c (d) e" стане "ae". [^)] * видаляє лише між першим лівим та першим правильним елементами: 'ac (d) e'. Ви також отримаєте різну поведінку для вкладених парен.
daotoad

68

Шаблон , який відповідає підрядка в дужках , що не мають ніякої іншої (і )символи між ними (як (xyz 123)в Text (abc(xyz 123)) є

\([^()]*\)

Подробиці :

Видалення фрагментів коду:

  • 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("\\([^()]*\\)", "")
  • Рубін :s.gsub(/\([^()]*\)/, '')
  • R :gsub("\\([^()]*\\)", "", x)
  • Луа :string.gsub(s, "%([^()]*%)", "")
  • Баш / сед :sed 's/([^()]*)//g'
  • Tcl :regsub -all {\([^()]*\)} $s "" result
  • C ++std::regex :std::regex_replace(s, std::regex(R"(\([^()]*\))"), "")
  • Завдання-C :
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\([^()]*\\)" options:NSRegularExpressionCaseInsensitive error:&error]; NSString *modifiedString = [regex stringByReplacingMatchesInString:string options:0 range:NSMakeRange(0, [string length]) withTemplate:@""];
  • Стрімкий :s.replacingOccurrences(of: "\\([^()]*\\)", with: "", options: [.regularExpression])


6

Якщо вам абсолютно не потрібно використовувати регулярний вираз, скористайтесь можливістю використовувати Perl's Text :: Balanced для видалення дужок.

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

Ви можете думати: "Навіщо все це, коли регулярний вираз робить фокус в один рядок?"

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

Текст :: Збалансовані дескриптори вкладених дужок. Так $filename = 'foo_(bar(baz)buz)).foo'буде видобуто правильно. Запропоновані тут рішення на основі регулярних виразів не зможуть використовувати цей рядок. Один зупиниться біля першого закриваючого батька, а інший з’їсть усіх.

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

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

   # text balanced example returns 'foo_).foo'

Якщо будь-яка з поведінок регулярних виразів є прийнятною, використовуйте регулярний вираз - але задокументуйте обмеження та припущення, що висуваються.


Хоча я знаю, що ви не можете проаналізувати вкладені дужки за допомогою (класичних) регулярних виразів, якщо ви знаєте, що ніколи не зіткнетеся з вкладеними дужками, ви можете спростити проблему до такої, що МОЖЕ зробити це за допомогою регулярних виразів, і досить просто. Використовувати інструмент синтаксичного аналізу, коли він нам не потрібен, надмірно.
Кріс Лутц,

@Chris Lutz - Я повинен був сказати "розглянути", а не "використовувати" у першому реченні. У багатьох випадках регулярний вираз виконує цю роботу, саме тому я сказав використовувати регулярний вираз, якщо поведінка є прийнятною.
daotoad

3

Якщо шлях може містити дужки, то r'\(.*?\)'регулярного виразу недостатньо:

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)

За замовчуванням функція зберігає фрагменти в дужках у частинах каталогу та розширення шляху.

Приклад:

>>> 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

Для тих, хто хоче використовувати Python, ось проста процедура, яка видаляє підрядки в дужках, включаючи ті, що мають вкладені дужки. Гаразд, це не регулярний вираз, але він зробить свою справу!

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')

Я вже хотів написати засіб для видалення вкладених дужок, але ви економите мій час, дякую! 😊
АйванФ.

1

Якщо ви можете витримати використання sed(можливо, виконати з вашої програми, це було б так просто, як:

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

Ви просто групуєте вираз .*.
Гамбо

@Gumbo: Ні, він ні. У групах "\ (... \)".
runrig

Опс, вибачте. Не знав цього.
Гамбо

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.