Sublime Forum

Bulk Find and Replace

#1

Hi All

I know this has been mentioned previously (and the solution amiss), so trying it again.

I have a file with dates I need to +1 each time.

So
201405 > 201406
201406 > 201407
201407 > 201408

So if my file has 201405, I could run a command (or regex) to move to 201406.
If my file has 201406, then I can run THE SAME COMMAND to get 201407.

I’ve looked at RegReplace and have really struggled with it. I tried recording a macro, by ST3 cannot record “find and replace” functions.

Please can you help?

Thanks!

0 Likes

#2

I can help with RegReplace, because it should be able to handle this, but you will have to be specific with what parts you are struggling with.

0 Likes

#3

Thank you! I appreciate the assistance.

So in its very basic form, I have a file with 201406 as text in it. I would like to run a command to convert that to 201407.

Default.sublime-commands

{ "caption": "Date Up", "command": "date_up", "args": {"replacements": "date_up"]} } ]

reg_replace.sublime-settings

{ "replacements": { "date_up": { "find": "201406", "replace": "201407" } } }

That’s all I have at the moment. As you can imagine the intention here is to create a command to increase/decrease all dates by one depending on which command I run.

At the moment…I have the above, but uncertain what do to next?

0 Likes

#4

I can already see some issues which I will detail for you when I get a chance, but can you post an example file? I say this because searching for a simple number may give you a lot of false positives. But if I can see the context in which these numbers are used, I may be able to give you a regex that is a lot more reliable.

0 Likes

#5

Hi

Okay, this is the type of file that I would be using this “find and replace” on frequently.

create multiset table db2.table_20140727,no fallback,no before journal,no after journal as ( select a.var1, 20140727 as day_id from db1.table4 a join db3.var2 b on b.var1 = a.var1 and b.var2_start_dttm < '2014-07-27 00:00:00' where a.var4_start_dttm < '2014/07/27 00:00:00' ) with data unique primary index (var1);

The intention of the command:

  • 20140727 > 20140728
  • 2014-07-27 > 2014-07-28
  • 2014/07/27 > 2014/07/28

And rinse and repeat should I run the command again for the next day and so on.

I’m basically trying to determine if I can convert from Emeditor’s Bulk Find and Replace utility…http://www.emeditor.com/text-editor-features/more-features/batch-replace/

Thanks!

0 Likes

#6

Okay, here is the regreplace solution. I went ahead and decided to increment by days because that was the most complicated. You can use scale the logic down to increment by month or even year.

So first, we define the pattern. Since the regex engine that is being used is Python’s re regex engine, I used Python’s regex syntax to give each group a name. Because this is not a simple find and replace, we will have to use some procedural logic via a python module, so I don’t bother defining the replace, but instead define a plugin module to call. In this case, the plugin will require no arguments, so I do not bother defining any.:

[pre=#232628] “date_up”: {
“find”: “(?P\d{4})(?P\d{2})(?P\d{2})”,
“plugin”: “User.rr_modules.date_up”
}[/pre]

Now I created a folder in my “Users” folder called “rr_modules”. I then created a file called “date_up.py” inside “rr_modules”. Hopefully you are still with me. Inside of “date_up.py”, I added this code (notice the included tests in the comments):

[pre=#232628]SHORT_MONTH = 30
LONG_MONTH = 31
FEB_MONTH = 28
FEB_LEAP_MONTH = 29

JAN = 1
FEB = 2
MAR = 3
APR = 4
MAY = 5
JUN = 6
JUL = 7
AUG = 8
SEP = 9
OCT = 10
NOV = 11
DEC = 12

Test 1: 20140228

Test 2: 20141231

Test 3: 20140101

def is_leap_year(year):
return ((year % 4 == 0) and (year % 100 != 0)) or (year % 400 == 0)

def days_in_months(month, year):
days = LONG_MONTH
if month == FEB:
days = FEB_LEAP_MONTH if is_leap_year(year) else FEB_MONTH
elif month in SEP, APR, JUN, NOV]:
days = SHORT_MONTH
return days

def increment_by_day(day, month, year):
mdays = days_in_months(month, year)
if day == mdays:
day = 1
if month == DEC:
month = JAN
year += 1
else:
month += 1
else:
day += 1

return day, month, year

def replace(m):
g = m.groupdict()
year = int(g"year"].lstrip(“0”))
month = int(g"month"].lstrip(“0”))
day = int(g"day"].lstrip(“0”))

day, month, year = increment_by_day(day, month, year)

return "%04d%02d%02d" % (year, month, day)[/pre]

Finally, you create the rule in your Default.sublime-commands file (remove “find_only” argument if you don’t wish to be prompted):

[pre=#232628] // Convert tabs to spaces
{
“caption”: “Replace: Date Up”,
“command”: “reg_replace”,
“args”: {“replacements”: “date_up”], “find_only”: true}
},[/pre]

Then run. If you find any bugs, you will need to resolve them, but on initial testing, it seems to work fine. I didn’t bother making the regex to complex. I will actually let you sort out if you want expand the regex to reduce false positives or include other date formats.

0 Likes

#7

I really have no intentions of emulating other replace software in RegReplace. I realize this is a method that involves coding, but it gives me all the flexibility I need… and I am a programmer so… :smiley:.

0 Likes

#8

:open_mouth:

Wow, that was more than I was expecting. While I’m comfortable with Python code I did not realise I had to go that level.

The boon here is the it completely removes the need for brute find and replacing.

Thank you! I’ll need time to work through this…

0 Likes

#9

Yeah, traditional find and replace, which I mainly do, doesn’t usually require much, and external plugins are not needed. Static patterns are easy, all you need is a find rule and a replace template. If all you really wanted to do was the same static date, then you can keep everything the same and throw away the date_up.py file and use:

"date_up": { "find": "20140717", "replace": "20140718" }

But personally I think you will find that exhausting. RegReplace won’t remove all the work for you, but does make it a bit easier and allows you to do it once and forget about.

Something like incrementing any unexpected date is complicated. There are different days in each month, there is leap years, you need to handle the roll over points when you hit the end of a month or the end of a year; there is just no way to fit that into a replace template. What you are basically asking for is a fairly complicated dynamic replace, so I had to resort to using procedural logic via a replace plugin. Any tool that does this with a simple one liner has all the code behind the scene. RegReplace is a framework to help create and chain find and replace commands for frequent use. Sadly though, advanced replaces are going to require advanced logic up front to handle them. But after that, it is just a key press to execute.

Hopefully, I set you down a path to figure out what you need (if I didn’t scare you away :smile:).

0 Likes