2

I have a text file. It is a big text file and This file contains some patterns like the following:

(1), (3), (1,2,3), (1,2,3,4,5,6,7,8,9)

...and like this,

(Fig1) (Fig1,Fig2), (Table-1, Table-2) etc.

I want the output like this

[1], [3], [1,2,3], [1,2,3,4,5,6,7,8,9], (Fig1) (Fig1,Fig2), (Table-1, Table-2)

I mean I only want to change the () into [] which contains only numbers. Please help me

1
  • 1
    Is it OK to also subtitute things like (), (,,), (,1,)? When you say numbers, is it only positive decimal integer numbers like in your sample or can it be things like -1, 1.2, 1e9, 0x12... ? Can we assume that parenthesis won't be nested (no ("Fig(1,2)", "Fig3") for instance? May 12, 2017 at 5:51

3 Answers 3

2

With sed:

sed 's/(\([0-9,]*\))/[\1]/g' filename.txt

[0-9,]* is a regular expression matching any number of digits and commas. I enclosed it in \(\) so I could reuse it in the replacement string as \1. The simple parenthesis without backslashes stand for themselves. They get replaced by the [] (which don't have a special meaning in the replacement string).

0
1

Python 3 script solution

The script below is an explicit more "manual" solution that doesn't use regular expressions, but does the job with a few extra considerations. The key to its operation is that the script opens for reading whatever file we've provided on command-line, iterates over each character in each line, seeking to find brackets. If we see brackets, we record whatever is inside, and after throwing away commas we decide if that's a numerical string or not. If it is a numerical string - the recorded items go into list of words, which is later rebuild into a line using .join() function with ", " as separator. Fairly straight-forward.

 #!/usr/bin/env python3
import sys

with open(sys.argv[1]) as fd:
    for line in fd:
        # we're going to store everything into list of words
        # and record individual characters into 'item' string
        # and rebuild everything as we go.
        words = []
        item_found = False
        item = ""
        counter = 0
        for char in line:
            # if we see ( or [ we start recording chars
            # difference is that [ means item already been edited
            # so no need to do anything - just put it into words list
            # as is
            if char == "(" or char == "[":
                item_found = True
                counter = counter + 1
                continue

            if char == ")":
                item_found = False
                if item.replace(",","").isdigit():
                   words.append("[" + item + "]")
                else:
                   words.append("("+item+")")
                item = ""

            if char == "]":
              item_found = False
              item = item + char
              words.append("[" + item) 
              item = ""

            if item_found:
                item = item + char

        # if we didn't see any open brackets or no closing brackets
        # just print the line as is - otherwise give us the altered one
        if counter == 0 or item_found:
            print(line.strip())
        else:
            print(", ".join(words))

Test run:

I took liberty with OP's input to include extra 2 lines that include couple different test cases.

$ # original input file
$ cat input.txt
(1), (3), (1,2,3), (1,2,3,4,5,6,7,8,9), (Fig1) (Fig1,Fig2), (Table-1, Table-2)
(table-25),[1,2,3],(figure-35)
(figure-1),(figure-2)
$ # script output 
$ ./change_brackets.py input.txt                                                          
[1], [3], [1,2,3], [1,2,3,4,5,6,7,8,9], (Fig1), (Fig1,Fig2), (Table-1, Table-2)
(table-25), [1,2,3], (figure-35)
(figure-1), (figure-2)

With 40,000 lines of text it performs fairly quick:

$ wc -l big_input.txt                                                                     
40000 big_input.txt
$ time ./change_brackets.py big_input.txt  > /dev/null                                    
    0m01.64s real     0m01.60s user     0m00.01s system

Possible suggestion for improvement ( and to address one of the things that Stephane has mentioned ) is to change if item.replace(",","").isdigit() line into if item.replace(",","").replace(".","").isdigit(). This will allow us to deal with floating point numbers (such as 3.1415) as well.

Lengthy ? Yes. Explicit ? Yes. Works ? Well, yes.

2
  • What happend to the third item of the second line of your output?
    – Philippos
    May 12, 2017 at 7:40
  • @Philippos Thanks for pointing that out. The issue was that I forgot to clear the item variable in the 3rd if statement, the if car == "]" was missing item = "" at the end. Fixed that already. May 12, 2017 at 7:50
1
sed -E 's/[(](([0-9]+,?)+)[)]/[\1]/g'

sed -e 's/(\(\([0-9]\{1,\},\{0,\}\)\{1,\}\))/[\1]/g'

We are looking for the regex [0-9]+,? occurring at least once and at the same time bounded by ( and ) brackets. The outer brackets are changed to [ ] on success.

The second sed statement is the POSIXly version of the above.

You must log in to answer this question.

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