2

I have a string in the form

  1. |a Some text, letters or numbers. | Some other text letters or numbers |b some other part of text |c some other letters or numbers

notice the bar can be on its own, as in "numbers. | Some other" or with a character "|a", "|b", "|c", and so on, potentially through to "|z"

but this could also be

  1. |a Title without any other bars

In other words the number of bars is unknown.

I need to find two regular expressions to use with sed:

  1. The first, finds all text between |a and |b, or |b and |c, and so on

in 1), for example,

find all text after a| but before b|, yielding:

Some text, letters or numbers. | Some other text letters or numbers

find all text after b| but before c|, yielding, in the example above:

Some other part of text

  1. A second expression is needed to find all text after |a, but, rather than stopping at |b, simply deletes any bars, on their own (|), or with another character |a, |b, |c, etc.

in 1) for example:

Some text, letters or numbers Some other text letters or numbers some other part of text some other letters or numbers

0

2 Answers 2

2

Assuming GNU utilities and a data file data,

  1. grep -Po '(?<=\|a).*(?=\|b)' data

     Some text, letters or numbers. | Some other text letters or numbers 
    
  2. sed -r -e 's/^.?*\|a//' -e 's/\|[a-z]?//g' data

     Some text, letters or numbers.  Some other text letters or numbers  some other part of text  some other letters or numbers 
     Title without any other bars 
    

Change the |a and |b to |c and |d, etc., as required.

Notice that neither of these removes the whitespace surrounding the |x markers, so your text has a leading space and a trailing space (neither of which can be shown here). If you want that removed too you need to include it as part of the pattern:

grep -Po '(?<=\|a ).*(?= \|b)' data
sed -r -e 's/^.?*\|a ?//' -e 's/ ?\|([a-z] ?)?//g' data

As written here, the sed command will join subsections together. If you want them to have a space between them just change the // at the end to / /.

0
1

It wasn't clear to my whether or not you wanted the letters in your delimiters to be sequential or not, so I went ahead and assumed you wanted to deal with the harder case of requiring the delimeters to be sequential (i.e. |a is paired with |b but not with |c). I'm not sure if you can do that with regular expressions alone (at least not without an extremely verbose regular expression). Anyway, here is a simple Python script that handles that case:

#!/usr/bin/env python2
# -*- coding: ascii -*-
"""parse.py"""

import sys
import re

def extract(string):
    """Removes text between delimters of the form `|START` and `|STOP`
    where START is a single ASCII letter and STOP is the next sequential
    ASCII character (e.g. `|a` and `|b` if START=a and STOP=b or
    `|x` and `|y` if START=x and STOP=y)."""

    # Find the opening delimiter (e.g. '|a' or '|b')
    start_match = re.search(r'\|[a-z]', string)
    start_index = start_match.start()
    start_letter = string[start_index+1]

    # Find the matching closing delimiter
    stop_letter = chr(ord(start_letter) + 1) 
    stop_index = string.find('|' + stop_letter)

    # Extract and return the substring
    substring = string[start_index+2:stop_index]
    return(substring)

def remove(string):

    # Find the opening delimiter (e.g. '|a' or '|b')
    start_match = re.search(r'\|[a-z]', string)
    start_index = start_match.start()
    start_letter = string[start_index+1]

    # Remove everything up to and including the opening delimiter
    string = string[start_index+2:]

    # Remove the desired substrings which occur after the delimiter
    string = re.sub(r'\|[a-z]?', '', string)

    # Return the updated string
    return(string)

if __name__=="__main__":
    input_string = sys.stdin.readline()
    sys.stdout.write(extract(input_string) + '\n')
    sys.stdout.write(remove(input_string))

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .