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

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.