I need to replace some 2- and 3-digit numbers with the same number plus 10000. So
Photo.123.aspx
needs to become
Photo.10123.aspx
and also
Photo.12.aspx
needs to become
Photo.10012.aspx
I know that in .NET I can delegate the replacement to a function and just add 10000 to the number, but I'd rather stick to garden-variety RegEx if I can. Any ideas?
James is right that you want to use the Regex.Replace method that takes a MatchEvaluator argument. The match evaluator delegate is where you can take the numeric string you get in the match and convert it into a number that you can add 10,000 to. I used a lambda expression in place of the explicit delegate because its more compact and readable.
using System;
using System.Text.RegularExpressions;
namespace RenameAspxFile
{
sealed class Program
{
private static readonly Regex _aspxFileNameRegex = new Regex(#"(\S+\.)(\d+)(\.aspx)", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);
private static readonly string[] _aspxFileNames= {"Photo.123.aspx", "Photo.456.aspx", "BigPhoto.789.aspx"};
static void Main(string[] args)
{
Program program = new Program();
program.Run();
}
void Run()
{
foreach (string aspxFileName in _aspxFileNames)
{
Console.WriteLine("Renamed '{0}' to '{1}'", aspxFileName, AddTenThousandToPhotoNumber(aspxFileName));
}
}
string AddTenThousandToPhotoNumber(string aspxFileName)
{
return _aspxFileNameRegex.Replace(aspxFileName, match => String.Format("{0}{1}{2}", match.Result("$1"), Int32.Parse(match.Result("$2")) + 10000, match.Result("$3")));
}
}
}
I think that using a RegEx for the match, and a function for the replace is most appropriate in this case, you are doing simple math, use something that is designed to do it.....
Is there any reason it has to be VB.NET?
Perl
s(
Photo\. (\d{2,3}) \.aspx
){
"Photo." . ($1 + 10000) . ".aspx"
}xe
Try the following:
"Photo\./d\.aspx" and replace with "Photo.1000$1.aspx"
"Photo\./d/d\.aspx" and replace with "Photo.100$1.aspx"
"Photo\./d/d/d\.aspx" and replace with "Photo.10$1.aspx"
That is the only way I see this happening.
If it's only two or three digit numbers:
(I assume you are using .NET Regex since we are talking about .aspx files)
Check for: Photo\.{\d\d\d}\.aspx
Replace with: Photo.10\1.aspx
Then check against: Photo\.{\d\d}\.aspx
Replace with: Photo.100\1.aspx
James Curran did it little faster than me but well here is what I have for you. Think it's the smallest code you can have with Regex to do what you want.
Regex regex = new Regex(#"(\d\d\d?)", RegexOptions.None);
string result = regex.Replace(#"Photo.123.asp", delegate(Match m)
{
return "Photo.1"
+ m.Groups[1].Captures[0].Value.PadLeft(4, '0')
+ ".aspx";
}
);
did you try just using PadLeft?
This appears to do what you want:
static public string Evaluator(Match match)
{
return "Photo.1"
+ match.Groups[1].Captures[0].Value.PadLeft(4, '0')
+ ".aspx";
}
public void Code(params string[] args)
{
string pattern = #"Photo\.([\d]+)\.aspx";
string test = "Photo.123.aspx";
Regex regex = new Regex(pattern);
string converted = regex.Replace(test, Evaluator)
Console.WriteLine(converted);
}
This will match the right part of the string, but won't tell you if it's two digits or three.
[^\d][\d]{2,3}[^\d]
Still, you could use that to grab the number, convert it to an int, add 10000, and convert that to the string you need.
Found this question since I was trying to do something similar in Vim.
Ill put the solution here.
:s/Photo\.\d\+\.aspx/\=Photo\.submatch(0)+10000\.aspx/g
Related
I want to replace my annotations from #RequestMapping to #GetMapping, #PutMapping ... annotations. When I looked at the Structural Find/Replace in IntelliJ looked like it could do the job.
I tried adding the following in the search
#RequestMapping( $key$ = $value$)
Added a filter on the key. text=method.
Now I want to extract the from the value (RequestMethod.GET) , the word after . (period). and then in the replacement add
#[Word(TitleCase)]Mapping( [everything except the key,value that was extracted in the search])
Haven't been able to figure out how to go about this. Would be nice to know if this can't be done, or any suggestions on how to do this. Looked at some of the other questions here on SO, but didn't find anything that could help. Most of the answers are to use regex in those cases.
Before:
#RequestMapping(
value = "/channels/{channel_name}",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public Channel updateChannel(
#PathVariable("channel_name") String channelName,
#Valid #RequestBody Channel channel) {
return channelService.updateChannel(channelName, channel);
}
#RequestMapping(
value = "/channels/{channel_name}",
method = RequestMethod.DELETE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Channel deleteChannel(
#PathVariable("channel_name") String channelName) {
return channelService.deleteChannel(channelName);
}
After
#PostMapping(value = "/channels/{channel_name}",
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public Channel updateChannel(
#PathVariable("channel_name") String channelName,
#Valid #RequestBody Channel channel) {
return channelService.updateChannel(channelName, channel);
}
#DeleteMapping(
value = "/channels/{channel_name}",
produces = MediaType.APPLICATION_JSON_VALUE)
public Channel deleteChannel(
#PathVariable("channel_name") String channelName) {
return channelService.deleteChannel(channelName);
}
I would do this dirty, with regex:
Replace RequestMethod.(.)(.+)(?=,) into RequestMethod.\U$1\L$2 (\L would turn the text into lowercase.)
Replace #RequestMapping\((\s+)(.+)(\s+?)(.+)RequestMethod.(.+?), into #$5Mapping\($1$2$3.
Then simplify this replacement chain:
Replace #RequestMapping\((\s+)(.+)(\s+?)(.+)RequestMethod.(\S)(.+?), into #\U$5\L$6\EMapping\($1$2
Update: Noticed the first parameter value is not specified whether in the line of #Mapping or a standalone line.
If you need it in the line of #Mapping, replace #RequestMapping\((\s+)(.+)(\s+?)(.+)RequestMethod.(\S)(.+?),\s into #\U$5\L$6\EMapping\($2$3.
If you need it to a standalone line, replace #RequestMapping\((\s+)(.+)(\s+?)(.+)RequestMethod.(\S)(.+?), into #\U$5\L$6\EMapping\($1$2.
I am using Ncalc to evaluate the presence of some string values
if (#Xval = 'Z','T','F')
this works well when #xval is inputted as a parameter as a single value(#Xval = 'Z'). That will return a true evaluation. I am now looking to evaluate the same formula when #Xval may be say 'Z','H' in other words Xval contains those 2 values and Im trying to find if 'Z' is among them.
The same goes for if (in (#Xval,'Z','H','M'),'T','F') where Im looking for the value of Xval in a group of options (Z,H,M).
Can I do this via custom functions? If so how? Any other ideas?
Thank you
You can try
Expression e = new Expression("if (iscontians("ZHM",#Xval),'T','F')", EvaluateOptions.IgnoreCase);
e.EvaluateFunction += evalFunction;
Write a custom function
private void evalFunction(string name, FunctionArgs args)
{
switch (name.ToUpper())
{
case "ISCONTAINS":
if (args.Parameters.Length < 2)
throw new ArgumentException("isContains() takes at least 2 arguments");
args.Result = args.Parameters[0].Evaluate().ToString().Contains(args.Parameters[1].Evaluate().ToString());
break;
default:
break;
}
}
I am relatively new to PigScript. I would like to know if there is a way of passing parameters to Java UDFs in Pig?
Here is the scenario:
I have a log file which have different columns (each representing a Primary Key in another table). My task is to get the count of distinct primary key values in the selected column.
I have written a Pig script which does the job of getting the distinct primary keys and counting them.
However, I am now supposed to write a new UDF for each column. Is there a better way to do this? Like if I can pass a row number as parameter to UDF, it avoids the need for me writing multiple UDFs.
The way to do it is by using DEFINE and the constructor of the UDF. So here is an example of a customer "splitter":
REGISTER com.sample.MyUDFs.jar;
DEFINE CommaSplitter com.sample.MySplitter(',');
B = FOREACH A GENERATE f1, CommaSplitter(f2);
Hopefully that conveys the idea.
To pass parameters you do the following in your pigscript:
UDF(document, '$param1', '$param2', '$param3')
edit: Not sure if those params need to be wrappedin ' ' or not
while in your UDF you do:
public class UDF extends EvalFunc<Boolean> {
public Boolean exec(Tuple input) throws IOException {
if (input == null || input.size() == 0)
return false;
FileSystem fs = FileSystem.get(UDFContext.getUDFContext().getJobConf());
String var1 = input.get(1).toString();
InputStream var1In = fs.open(new Path(var1));
String var2 = input.get(2).toString();
InputStream var2In = fs.open(new Path(var2));
String var3 = input.get(3).toString();
InputStream var3In = fs.open(new Path(var3));
return doyourthing(input.get(0).toString());
}
}
for example
Yes, you can pass any parameter in the Tuple parameter input of your UDF:
exec(Tuple input)
and access it using
input.get(index)
In perl you can write
$string =~ tr/[a,e,i,o,u,y]/[A,E,I,O,U,Y]/;
for example.
Is it possible to achieve the same "translation" effects with VB.Net regexes?
Thanks you!
PS: I'm not searching for a way to port this very example, it's more of a curiosity question :)
There is no standard method for this. You can do it by iterating over each character in your input string and using a dictionary to map it to another character (or leave it unchanged if the character is not found in the dictionary). The result can be built using a StringBuilder for performance reasons.
If performance is not an issue then you might be able to use a few replace operations instead:
s = s.Replace("a", "A")
.Replace("e", "E")
...
.Replace("y", "Y");
Here's one way to do this:
public string fakeTR(string theString, char[] org, char[] rep)
{
for(int i=0;i<org.lenght;i++)
{
theString = theString.Replace(org[i], rep[i]);
}
return theString;
}
You would be able to call it with somewhat clunky but shorter:
string v = "Black in South Dakota";
v = fakeTR(v, new char[]{'B','l','a','c','k'}, new char[]{'W','h','i','t','e'});
H/T http://discuss.joelonsoftware.com/default.asp?dotnet.12.306220.6
I have this function. The visual studio profile marked the line with string.Format as hot and were i spend much of my time.
How can i write this loop more efficiently?
public string EscapeNoPredicate(string sz)
{
var s = new StringBuilder(sz);
s.Replace(sepStr, sepStr + sepStr);
foreach (char v in IllegalChars)
{
string s2 = string.Format("{0}{1:X2}", seperator, (Int16)v);
s.Replace(v.ToString(), s2);
}
return s.ToString();
}
Instead of calculating s2s foreach v each time this method is called; you can store them precalculated. Of course I am assuming IllegalChars and seperator remains same.
In a string.format you can put objects, so (Int16)v is not needed. You can supply "v"