Sublime Forum

Renaming several strings of text by their pattern

#1

Hello to all.

I’m new to Sublime Text and have been searching online for a way to do this but have not been successful thus far. What I’d basically like to do is have the following strings of text:

global.test_a = 1\2\3\sample_a.xxx
global.test_b = 1\2\3\sample_b.xxx
global.test_c = 1\2\3\sample_c.xxx

renamed as follows:

FTEST(global.test_a)
FTEST(global.test_b)
FTEST(global.test_c)

Note that there are a couple hundred of such items with variations that I’d like to rename. How would I go about doing so without extensive copy-pasting? I though the Find/Replace function would have such a utility but did not find it there.

Thank you for any help.

0 Likes

#2

Open the replace panel (ctrl+h), enable the regex mode (alt+r) (it’s a toggle, make sure that’s it’s on). Then, you can use a regex:

In the first field:

global.test_([a-z]) = 1\2\3\sample_a.xxx

In the second field:

FTEST(global.test_$1)

Here, it looks for global.test_, then any letter [a-z], the () are use to capture it, so we can reuse it in the second field (the $1), and then for = 1\2\3\sample_a.xxx

As you can see, it’s fairly simple, and if you want to improve, learn the regex (best long term solution), and/or give more infos, I’ll make you a better one (good short term solution).

I strongly recommend learning regex (regular expression), you’ll use them everywhere. Here’s a pretty cool tool that I like to use: regex101.

Matt

3 Likes

#3

Man, wish I had known about a tool like this when I learned regex.

Sometimes I think they should teach regex when they teach programming. It is one of the most useful tools, and I know so many programmers that can’t use it effectively.

2 Likes

#4

You know what? same. :smile: It’s still useful though…

Don’t they? I learned about regex when I learned PHP (I even thought it a php feature, no other language had this, not even python! :disappointed_relieved:).

How did you learn to program?

0 Likes

#5

I am an Electrical Engineer major, so honestly, I spent most of my time learning to evaluate circuits and such. I kind of fell into firmware programming. You just sort of pick it up along the way. But I know a lot of guys who aren’t good at regex who are CS majors. Maybe they do teach it at some schools.

0 Likes

#6

on Windows, there is also https://www.regexbuddy.com/ to help you learn regex :slightly_smiling:

2 Likes

#7

@math2001;

Thank you for the reply!

I tried your solution. I see what’s being done but for some reason, no changes in the strings occur. I’ve ensured that regex mode (icon with the period and asterisk I believe within the Find/Replace pane) is enabled.

The Find What field has; global.test_([a-z]) = 1\2\3\sample_a.xxx
The Replace With field has: FTEST(global.test_$1)

Then I click Replace All but the replace panel closes and doesn’t seem to make the desired changes. The Replace button does not have any effect either it seems.

Would my cursor need to be positioned in a certain way, or breaks and paragraphs adjusted/highlighted perhaps? I’ll also look into learning about the regex tool you’ve mentioned.

Thanks again!

EDIT: @kingkeith: thanks for the link. Have bookmarked it!

0 Likes

#8

Can you show me your file’s content?

0 Likes

#9

There are a couple of problems with this regex.

Firstly, the '\' character is “magic” in a regular expression; it tells the regex that the character that follows it should be interpreted specially. \2 and \3 match the text previously captured in the search by wrapping text in brackets (your search only has one set of brackets, so only \1 would be valid here). Also, \s means “match a single white space character such as a space, tab, newline, etc”.

Since your search doesn’t capture 3 different sets of text with brackets and there is no white space character in the result, it doesn’t match.

In order to just say "I want to match a '\'" character, you need to quote it to tell the regex engine it’s not special. That would make your regex:

global.test_([a-z]) = 1\\2\\3\\sample_a.xxx

However, based on your sample text, this will only match the first line. This is because the same lower case letter appears in the match on both sides of the equal sign (test_a = sample_a and so on), but the search text always has an a at the end on the right hand side, and only the first line ends that way.

This is where the capture group comes into play. The lower case letter on the left side of the = character is “captured” by the brackets.

To match every such like, you can use the special \1 construct to match whatever was captured on the left, making your regex (notice only a single backslash on that \1 in this case):

global.test_([a-z]) = 1\\2\\3\\sample_\1.xxx

With all that said, if your intention is actually to throw away everything to the right of the = in your replacement (including the = itself), this regex is shorter and sweeter:

global.test_([a-z]).*

The . means “match any character” and the * means “match the thing that comes before me 0 or more times”, so together they match the entire rest of the line.

3 Likes

#10

:disappointed: I’m stupid… Sorry @noname91 I should have tested it…

1 Like

#11

As an aside, when working with a regex (and less so with just a regular search), it can be very helpful to turn on the "highlight matches: option in the find panel.

That way, as you enter the regex, sublime will highlight in the buffer what it’s matching (in all of the match locations) allowing you to visually get a sense of exactly what text is going to be found by your search; if the highlight stops early, you can tell where you went wrong.

Of course, if you’re editing a large file this is not a great idea, as in that case you could potentially slow Sublime to a crawl.

1 Like

#12

You are forgiven. : P
Seriously though, the help is appreciated in any case!

@odatNurd: Your solution does work, but a few things:

  1. If I’d like to capture a combination of numbers and alphabets, would this be possible? I was using the example in the op for simplicity and thought it’d be easy but cannot find the function to do so. Ideally, I’d like to have:

global.alpha_1 = 1\2\3\alpha_1.xxx
global.beta_2 = 1\2\3\beta_2.xxx
global.gamma_3 = 1\2\3\gamma_3.xxx

As:

FTEST(global.alpha_1)
FTEST(global.beta_2)
FTEST(global.gamma_3)

Now… I notice in your solution code, you have: global.test_([a-z]).* . The [a-z] captures the letters and I’m guessing [0-9] captures numbers. But how would I capture both letters and numbers so that the string pattern follows the FTEST strings I have for alpha, beta, gamma etc.? I’ve tried a few combinations but they didn’t work.

  1. You wrote: “Firstly, the ‘’ character is “magic” in a regular expression; it tells the regex that the character that follows it should be interpreted specially.”

The slashes are simply the directory separations in the string that are pointing to a resource. If I’ve understood correctly, they actually have a function in Sublime and because of this, we need to use two slashes?

Thanks!

0 Likes

#13

The construct of putting characters inside a pair of [] is called a character class. A character class can contain a set of characters, a set of ranges, or a mixture of both.

So for example [Aa] means “match an A regardless of case”, [ab] means “match a lowercase a or b”, and so on. In the case of ranges, you can just run them together: [a-zA-Z] captures all upper and lowercase letters, and [a-zA-Z0-9] matches all upper and lower case letters as well as numbers.

In fact, these things are so commonly used that there is a shorthand to make your life easier:

  • \d means [0-9] and matches a digit
  • \w means [a-zA-Z0-9_] and matches a letter, number or an underscore, which is a word character
  • \s means [ \t\r\n] and matches a space, tab, newline or carriage return.

For your revised sample code, a regex such as the following does what you want:

(global\.\w+).*

Here the replacement text is just a simple FTEST(\1).

Some notes on this:

  1. Since you want to throw the global. part into the replacement text, this includes that part in the capture to save time in the replacement text (work smart, not hard ;)).

  2. This uses a + character, which means “match 1 or more of the preceding item” in the same way that a * means “match 0 or more of the preceding item”; basically for this to match there has to be something following the global. part in the line.

  3. Notice that I have used \. instead of just . alone. This is because the . characters means “match a single character, regardless of what it is”. By quoting it with a backslash character we tell the regular expression not to treat it specially, and just match it as a period. For your example it doesn’t matter, but technically speaking global.hello as a regular expression would match “global hello” (with a space) or even “global_hello”. That’s the sort of thing that can bite you in the butt when you don’t expect it.

They have a special meaning to the regular expression, not to sublime itself, but the gist of your understanding is correct, yes. The construction of a backslash followed by some character (e.g. \d meaning “a digit”) means that you can’t just throw a regular \ character out there because the expression will try to use the next character to see what special code you’re telling it to use.

Hence, the special code \\ means "I meant to match a \ charcter by itself.

Similarly, the following (non-exhaustive) list of characters has a special meaning in a regular expression, so if you want to literally match one of them, you have to prefix them with a \ character so that the regex knows you didn’t intend them to mean special things:

  • . to match a single character, no matter what it is
  • ( to start a capture group
  • [ to start a character class
  • * to match 0 or more of the preceding character
  • + to match 1 or more of the preceding character
1 Like

#14

Thanks for the comprehensive explanation, OdatNurd!

Bookmarked and saved your posts for future reference.

0 Likes