[Tutor] A regular expression problem

Steven D'Aprano steve at pearwood.info
Sun Nov 28 18:14:01 CET 2010


Josep M. Fontana wrote:
> I'm trying to use regular expressions to extract strings that match
> certain patterns in a collection of texts. Basically these texts are
> edited versions of medieval manuscripts that use certain symbols to
> mark information that is useful for filologists.
> 
> I'm interested in isolating words that have some non alpha-numeric
> symbol attached to the beginning or the end of the word or inserted in
> them. Here are some examples:
> 
> '¿de' ,'«orden', '§Don', '·II·', 'que·l', 'Rey»'

Have you considered just using the isalnum() method?

 >>> '¿de'.isalnum()
False

You will have to split your source text into individual words, then 
isolate those where word.isalnum() returns False.


> I'm using some modules from a package called NLTK but I think my
> problem is related to some misunderstanding of how regular expressions
> work.

The first thing to do is to isolate the cause of the problem. In your 
code below, you do four different things. In no particular order:

1 open and read an input file;
2 open and write an output file;
3 create a mysterious "RegexpTokenizer" object, whatever that is;
4 tokenize the input.

We can't run your code because:

1 we don't have access to your input file;
2 most of us don't have the NLTK package;
3 we don't know what RegexTokenizer does;
4 we don't know what tokenizing does.

Makes it hard to solve the problem for you, although I'm willing to make 
a few wild guesses (see below).

The most important debugging skill you can learn is to narrow the 
problem down to the smallest possible piece of code that gives you the 
wrong answer. This will help you solve the problem yourself, and it will 
also help others help you. Can you demonstrate the problem in a couple 
of lines of code that doesn't rely on external files, packages, or other 
code we don't have?


> Here's what I do. This was just a first attempt to get strings
> starting with a non alpha-numeric symbol. If this had worked, I would
> have continued to build the regular expression to get words with non
> alpha-numeric symbols in the middle and in the end. Alas, even this
> first attempt didn't work.
> 
> ---------
> with open('output_tokens.txt', 'a') as out_tokens:
>     with open('text.txt', 'r') as in_tokens:
>         t = RegexpTokenizer('[^a-zA-Z\s0-9]+\w+\S')
>         output = t.tokenize(in_tokens.read())
>         for item in output:
>             out_tokens.write(" %s" % (item))

Firstly, it's best practice to write regexes as "raw strings" by 
preceding them with an r. Instead of

'[^a-zA-Z\s0-9]+\w+\S'

you should write:

r'[^a-zA-Z\s0-9]+\w+\S'

Notice that the r is part of the delimiter (r' and ') and not the 
contents. This instructs Python to ignore the special meaning of 
backslashes. In this specific case, it won't make any difference, but it 
will make a big difference in other regexes.

Your regex says to match:

- one or more characters that aren't letters a...z (in either
   case), space or any digit (note that this is *not* the same as
   characters that aren't alphanum);

- followed by one or more alphanum character;

- followed by exactly one character that is not whitespace.

I'm guessing the "not whitespace" is troublesome -- it will match 
characters like ¿ because it isn't whitespace.


I'd try this:

# untested
\b.*?\W.*?\b

which should match any word with a non-alphanumeric character in it:

- \b ... \b matches the start and end of the word;

- .*? matches zero or more characters (as few as possible);

- \W matches a single non-alphanumeric character.

So putting it all together, that should match a word with at least one 
non-alphanumeric character in it.

(Caution: if you try this, you *must* use a raw string, otherwise you 
will get completely wrong results.)


> What puzzles me is that I get some results that don't make much sense
> given the regular expression.

Well, I don't know how RegexTokenizer is supposed to work, so anything I 
say will be guesswork :)


[...]
> If you notice, there are some words that have an accented character
> that get treated in a strange way: all the characters that don't have
> a tilde get deleted and the accented character behaves as if it were a
> non alpha-numeric symbol.

Your regex matches if the first character isn't a space, a digit, or a 
a-zA-Z. Accented characters aren't a-z or A-Z, and therefore will match.



-- 
Steven



More information about the Tutor mailing list