I received a text-only file which has the heading text, including the section numbers like "".

Is there a tool (or macro) for Microsoft Word or for RTF text that can count how many levels this contains, e.g., "" is level 4, and convert that paragraph to the appropriate Heading X style?

I imagine I can write some macro that recursively searches for $[0-9]{1,}[.][^0-9] and then for $[0-9]{1,}[.][0-9]{1,}[.][^0-9], applying style Heading [loopcount], but would rather avoid reinventing the wheel.
(Apologies if the pseudo-regex is wrong)

edit: sample text

1 Introduction
Lorem ipsum 
2 next section
2.1 next subsection
Correct horse battery staple has 4.56 quanta of uncertainty.
Notice that 'body text' can have numbers in it; need to look for numbers which are at the start of a new paragraph
2.2 complaints subsection
but microsoft word's "wild card" toolset doesn't include a "start-of-line" 
character so far as I know.
There can by multiple paragraphs of body text in any section. There is at least one paragraph mark before a new heading but could be multiple ones.
2.2.1 nice deep subsection
Je ne veux pas travailler; je ne veux pas dejuner; ...

You could use some python, it's built in re regular expression library and the python-docx library.

  • Python and python-docx are both free, gratis & open source, and available for most platforms including Windows
  • The process will work even on a machine that doesn't have word installed.
  • The code is short and simple
  • You must get the indentation right
  • You could tidy the example below and make it into a script, (.py), file that takes a filename in and creates a docx of the same base name.
import re
import docx
headerre = re.compile(r'^(\d+(\.\d+)*)[ \t].+') # Regular expression for number(s) at start of line

document = docx.Document()  # Create a document
for line in open('yourfile.txt').readlines():  # Read the lines
    match = headerre.match(line)  # Test the lines
    if match is None:  # Not a heading
       document.add_paragraph(line) # Add as standard para
    else: # it is a heading
        lev = match.group(1).count('.') + 1  # Count the dots for the level
        document.add_heading(line, level=lev) # Add to the document
document.save('yourfile.docx') # Save it

and you are done.

| improve this answer | |
  • Yeah, I'm painfully familiar with the white-space rules in Python :-( . As we all know, porting from one editor to another turns tabs into 5 spaces, etc. Anyway, I'll try to implement this answer , with a loop which modifies the regexp you provided to match up each sub-heading level. – Carl Witthoft Aug 22 '16 at 13:38
  • 1
    @CarlWitthoft - the regexp that I provided should match all sub-heading levels as long as they start at the beginning of a line, the ^ with a number, _the first \d+ and consist only of groups of numbers separated by . this is the (\.\d+)* which says 0 or more .numbers, - then the .s are counted to find out the (sub-)heading depth. I have corrected it for multiple digits at each level which I had missed. – Steve Barnes Aug 22 '16 at 17:19
  • 1
    The easy answer to the editor tab problem is quite simple - set your editor to use 4 spaces instead of a tab, (not display a tab as 4 spaces), most programming editors have this option and don't ever use actual tabs. Oh! and avoid notepad like the plague. – Steve Barnes Aug 22 '16 at 17:27
  • Steve, sorry to bother you, but I get the following error message Traceback (most recent call last): File "<stdin>", line 6, in <module> TypeError: 'builtin_function_or_method' object has no attribute '__getitem__' Can you suggest what I should track down? I'm using python 2.7.10 in cygwin . – Carl Witthoft Aug 22 '16 at 18:38
  • 1
    Steve, that did the trick. Thanks for saving me the work of remembering Python syntax :-) . The output looks great, other than every paragraph mark having been replaced with <para><manual linebreak><manual linebraeak> , which is trivial to fix with Word's editor. I'll be sure to put you as the author if I distribute this script. – Carl Witthoft Aug 23 '16 at 11:27

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.