Sublime Forum

Need to split one .txt file into multiple files

#1

hi all,

I am wanting to import a whole bunch of things from OmniFocus to MacOS’s Notes as easily as I can. Notes will happily import separate .txt files and make each one its own individual note, which is great. Omnifocus will export a project to one big .txt document, but not separate ones. (CSV is also an export option.)

My question is, is there a way to get Sublime to split one large text file into many others? I can set the break points where it should be split with any set of characters, not a problem. Just looking to not manually save 200+ text files…

0 Likes

#2

This isn’t something that Sublime can do out of the box, but it seems entirely plausible to create a simple script outside of Sublime to do this or even a quick one-off sublime plugin; say for example a TextCommand that generates a series of files based on the name of the current file with an extra numeric bit crammed into the name to keep the file names distinct, for example.

If you can provide an example of such a file with a couple of notes in it, we can probably help you out.

0 Likes

#3

It can do it if you write a plugin…

All you would need to do is read the content from the view, OR read the file into memory with Python - preferably called via a command.

Next, determine how it is going to be split and incorporate that. Finally, take the text you split and save it into each individual file, ensuring the file doesn’t already exist - if it does either rename the existing one or give the new one a new name.

0 Likes

#4

The simplest/cleanest output I can get is by exporting a project as a .txt file. There’s a little bit of extraneous OmniFocus-specific stuff I can easily find/replace, and then I get something like this below (a mix of real items and a few dummy items).

Right now, each new item starts with a hyphen and a space, but I can obviously delimit the beginning of a note with any series of characters necessary. All I’m really looking to do is have a new text file where each of those hyphens starts a new item. Some of these have very long notes sections which are generally soft-wrapped but have hard returns at the end of each paragraph.

- Why Productive People Have Empty Schedules | Fast Company | Business + Innovation
	http://www.fastcompany.com/3009536/leadership-now/why-productive-people-have-empty-schedules
	WHY PRODUCTIVE PEOPLE HAVE EMPTY SCHEDULES
	Back in 1991, Warren Buffett <//www.fastcompany.com/person/warren-buffett> met Bill Gates, though as he tells Levo League <www.fastcompany.com/3009443/the-takeaway/5-lessons-from-warren-buffetts-office-hours>, neither of them were excited to see one another. But it turned out they had a great time talking--and during the course of the conversation, Buffett pulled out the little black date book that he carries in his pocket.
	He flipped through it: The pages were practically empty.
	"You've gotta keep control of your time," Buffett says, "and you can't unless you say no. You can't let people set your agenda in life."
	To be creative, you need to say "no"
	Buffett, now 82, admits that it doesn't get easier as you get older. But even if you're not inclined to decline--like when your friend asks you to attend something--you need to develop the ability to say no. He's far from alone: As Kevin Ashton <//www.fastcompany.com/person/kevin-ashton> wrote in a recent essay <https://medium.com/thoughts-on-creativity/bad7c34842a2> for Medium, being stingy with your time is part of leading a creative, productive life.
	Ashton, who coined the phrase "Internet of Things," observes a common thread between ur-manager Peter Drucker, novelist Charles Dickens, and photographer Richard Avedon: All of them guarded their time. Why? For in order to do your work, Ashton observed, you must have time:
	"Wipe away the magic and myth of creating and all that remains is work ... No matter what you read, no matter what they claim, nearly all creators spend nearly all their time on the work of creation. There are few overnight successes and many up-all-night successes."
	This is why if we want to do the work that we want to do, we need to own our time--how else can we spend it productively? As Reddit CEO Yishan Wong explained in an epic, lesson-filled Quora thread <www.quora.com/What-are-some-important-and-generalizable-life-lessons>, time is limited in three ways:
	Time is highly limited: As humans, we're immature in our first decades, and declining in health in our last
	Time is uniquely limited: You can't bank, transfer, or recover time, unlike money.
	Time is equitably limited: Americans can, on average, expect to live about 77 years. That expectation isn't equal with resources like money.
	Taking time
	The good Mr. Buffett talks about how Berkshire Hathaway is his canvas, one that he's happy to paint every day. But if you are to paint your canvas--if you are to do meaningful work <www.fastcompany.com/3008736/leadership-now/secret-finding-meaningful-work>--you will need time. In this way, Ashton observes, managing time is the key to cultivating your creativity:
	"Saying 'no' has more creative power than ideas, insights and talent combined. No guards time, the thread from which we weave our creations. The math of time is simple: you have less than you think and need more than you know."
	How do you guard your time? Let us know in the comments.


- test item 1
	descriptive text

- test item 2
	descriptive text

- Henry Miller's 11 Commandments of Writing & Daily Creative Routine | Brain Pickings
	http://www.brainpickings.org/index.php/2012/02/22/henry-miller-on-writing/

- test item 3
	descriptive text

- test item 4
	descriptive text
0 Likes

#5

Thanks, but that’s well beyond my pay grade :slight_smile:

0 Likes

#6

An example of a plugin that would do something like this is the following bit of code. You can use Tools > Developer > New Plugin... and replace the stub that’s generated with this, then save in the location that Sublime will default to, which is in your User package. The name is unimportant as long as it’s got a py extension, but you may want to name it something that lets you know what it’s doing. :slight_smile:

import sublime
import sublime_plugin

import os
import re

# A regular expression that matches the start of a note. It's best
# that this either be something unique that won't appear in notes or,
# better still, anchored to the beginning of a line such as in the
# example text.
_note_prefix = r"^- "


class SplitNotesCommand(sublime_plugin.TextCommand):
    """
    Given a file with a name, try to find all of the notes using the
    above _note_prefix and write them out to files with names based on
    the name of the current file.

    When dry_run is True, the regions that are considered notes are
    outlined to allow you to verify that things are being found
    correctly.
    """
    def run(self, edit, dry_run=True):
        # Obtain a list of regions that match the note prefix. This
        # tells us where all of the notes in this file start.
        notes = self.view.find_all(_note_prefix)

        # Now we can iterate over all of the note prefix regions and
        # create a list that expands them to the whole note. We'll say
        # that a note begins right after the prefix and runs right up
        # to the start of the next prefix that appears in the file. In
        # the case of the last note, we instead end at the end of the
        # file instead.
        eof = sublime.Region(len(self.view))
        note_regions = []
        for idx, note in enumerate(notes):
            end = notes[idx + 1] if idx + 1 < len(notes) else eof
            note_regions.append(sublime.Region(note.b, end.a))

        # If this is just a dry run, mark the file to show where the
        # notes would be split, and then leave.
        if dry_run:
            self.view.add_regions("notes", note_regions, "comment",
                flags=sublime.DRAW_NO_FILL)
            return

        # Split the filename in half at the extension.
        base_name, ext = os.path.splitext(self.view.file_name())
        for idx, note in enumerate(note_regions):
            # Create a file name to be used for this note, which will
            # be based on the name of the current file with a numeric
            # prefix attached.
            note_name = "%s_%03d%s" % (base_name, idx + 1, ext)

            # Grab the contents of the file that spans the note region
            note_text = self.view.substr(note)

            # Write it to disk now. This doesn't error check, for
            # brevity. It's also not checking that it might clobber
            # over an existing file.
            with open(note_name, "w", encoding=self.view.encoding()) as file:
                file.write(note_text)

        # Display the count.
        sublime.message_dialog("Generated %d notes" % len(note_regions))

    def is_enabled(self):
        # Only enabled for files that have been saved
        return self.view.file_name() is not None


The code is commented to give you an idea of how it’s working, but basically it looks through the file for a regular expression that matches a note prefix, and then everything between prefixes (but not including the prefix itself) is considered to be a single note.

From there it can grab the text out of the file and write it out to a new file. The name chosen is based on the name and location of the file that you run the command in, with a numeric suffix added to the end, so for example sample.txt becomes sample_001.txt and so on.

This doesn’t check that files with those names don’t already exist, nor does it check if there’s an error writing to disk. Presumably as long as you have permission to create files in the location where the source is, and you safely name your source file, that’s not too much of a problem.

For safety if you run the command without any arguments it will mark the regions in the file that are considered to be notes without actually generating any files. That way you know for sure that it’s going to do what you expect before it starts dumping tons of files everywhere. You probably want to also put your source file in an empty folder to control where the output goes as well.

To use it, open the file with the source notes and use View > Show Console. While the notes file has the focus use one of the following lines to run the command; the first one runs the command in “safe” mode, just showing you what it would do, and the second one actually generates the files, once you’re sure it’s working.

view.run_command("split_notes")
view.run_command("split_notes", {"dry_run": False})
4 Likes

#7

It’s taken me a while to get back here, but I will try this out once I have an extra little chunk of time to figure it out. Thanks very much for this post! I’ll report back as to how it works out – and hopefully document my process a little in case anybody else wants to migrate things out of OmniFocus and into Notes.

2 Likes

#8

Wow. Superb. Thank you very much!

0 Likes

#9

What programming language did you use to write this plugin?

0 Likes

#10

Plugins in Sublime are written in Python.

1 Like