perl - Split string by methods within the strings - regex

I have two questions:
First, how can I split the following string into individual strings split by the methods within the string? I tried using regex, but was unsuccessful.
$objc = "- (void)method {
NSLog(#"method");
if (1 == 1) {
//blah blah blah
}
}
- (id)otherMethodWithProperty:(NSString *)property {
NSLog(#"otherMethodWithProperty:");
return property;
}
-(id) methodWithMoreProperties: (id)property Property2:(UIView *)property2 Property3:(NSString *)property3 {
id view = property;
if (view) {
NSLog(#"%#", view);
}
return view;
}"
Second question is after splitting into individual strings, is it possible to grab each property and add it within the respective string? For example:
I take the string:
"-(id) methodWithMoreProperties: (id)property Property2:(UIView *)property2 Property3:(NSString *)property3 {
id view = property;
if (view) {
NSLog(#"%#", view);
}
return view;
}"
grab the properties "property, property2, property3" and add them within the string after the first "{" and before the last "}":
"-(id) methodWithMoreProperties: (id)property Property2:(UIView *)property2 Property3:(NSString *)property3 {
NSLog(#"%#\n%#\n%#", property, property2, property3);
id view = property;
if (view) {
NSLog(#"%#", view);
}
return view;
NSLog(#"FINISH: %#\n%#\n%#", property, property2, property3);
}"
I've been googling and testing code for hours and I've only managed, using regex, to get the method name
-(id) methodWithMoreProperties:
and add it within the string, but haven't been able to grab the properties themselves and add them after the first { and before the last }

not all was done by regex, but I think it's more readable
# split string into methods
my #methods = split /^-/m, $objc;
foreach my $method_content (#methods) {
my $method_declaration = (split /{/, $method_content, 2)[0];
my ($method_name, #properties) = $method_declaration =~ /\)\s*(\w+)/g;
if (#properties) {
my $sprintf_format = join '\n', ('%#') x #properties;
my $sprintf_values = join ', ', #properties;
my $begin_message = sprintf 'NSLog(#"%s", %s);', $sprintf_format, $sprintf_values;
my $end_message = sprintf 'NSLog(#"FINISH: %s", %s);', $sprintf_format, $sprintf_values;
$method_content =~ s/{/{\n $begin_message/;
$method_content =~ s/}\s*$/ $end_message\n}\n\n/;
}
print "-$method_content";
}
but the $end_message should be better put before the methods's return or it'll never be triggered.

You can use this pattern:
my #matches = $objc =~ /(-\s*+\([^)]++\)(?>\s*+\w++(?>:\s*+\([^)]++\)\s*+\w++)?+)*+\s*+({(?>[^{}]++|(?-1))*+}))/g;
(you only have to costumize the capturing groups as you want)

Related

phrase search in meteor search-source package

I have a meteor app for which I added the search-source package to search certain collections and it works partially. That is, when I search for the term foo bar it returns results for each of "foo" and "bar". This is fine, but I want to also be able to wrap the terms in quotes this way: "foo bar" and get results for an exact match only. at the moment when i do this i get an empty set. Here is my server code:
//Server.js
SearchSource.defineSource('FruitBasket', function(searchText, options) {
// options = options || {}; // to be sure that options is at least an empty object
if(searchText) {
var regExp = buildRegExp(searchText);
var selector = {$or: [
{'fruit.name': regExp},
{'fruit.season': regExp},
{'fruit.treeType': regExp}
]};
return Basket.find(selector, options).fetch();
} else {
return Basket.find({}, options).fetch();
}
});
function buildRegExp(searchText) {
// this is a dumb implementation
var parts = searchText.trim().split(/[ \-\:]+/);
return new RegExp("(" + parts.join('|') + ")", "ig");
}
and my client code:
//Client.js
Template.dispResults.helpers({
getPackages_fruit: function() {
return PackageSearch_fruit.getData({
transform: function(matchText, regExp) {
return matchText.replace(regExp, "<b>$&</b>")
},
sort: {isoScore: -1}
});
}
});
Thanks in advance!
I've modified the .split pattern so that it ignores everything between double quotes.
/[ \-\:]+(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/
Thus, you can simply wrap an exact phrase search in double quotes and it won't get split.
There is one more thing; since we don't need the quotes, they are removed in the next line using a .map function with a regex that replaces double quotes at the start or the end of a string part: /^"|"$/
Sample code:
function buildRegExp(searchText) {
// exact phrase search in double quotes won't get split
var arr = searchText.trim().split(/[ \-\:]+(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/);
var parts = arr.map(function(x){return x.replace(/^"|"$/g, '');});
return new RegExp("(" + parts.join('|') + ")", "ig");
}
console.log(buildRegExp("foo bar"));
console.log(buildRegExp("\"foo bar\""));

Parsing tags in string

I'm trying to parse a string with custom tags like this
[color value=0x000000]This house is [wave][color value=0xFF0000]haunted[/color][/wave].
I've heard about ghosts [shake]screaming[/shake] here after midnight.[/color]
I've figured out what regexps to use
/\[color value=(.*?)\](.*?)\[\/color\]/gs
/\[wave\](.*?)\[\/wave\]/gs
/\[shake\](.*?)\[\/shake\]/gs
But the thing is - I need to get correct ranges (startIndex, endIndex) of those groups in result string so I could apply them correctly. And that's where I feel completely lost, because everytime I replace tags there's always a chance for indexes to mess up. It gets espesically hard for nested tags.
So input is a string
[color value=0x000000]This house is [wave][color value=0xFF0000]haunted[/color][/wave].
I've heard about ghosts [shake]screaming[/shake] here after midnight.[/color]
And in output I want to get something like
Apply color 0x000000 from 0 to 75
Apply wave from 14 to 20
Apply color 0xFF0000 from 14 to 20
Apply shake from 46 to 51
Notice that's indices match to result string.
How do I parse it?
Unfortunately, I'm not familiar with ActionScript, but this C# code shows one solution using regular expressions. Rather than match specific tags, I used a regular expression that can match any tag. And instead of trying to make a regular expression that matches the whole start and end tag including the text in between (which I think is impossible with nested tags), I made the regular expression just match a start OR end tag, then did some extra processing to match up the start and end tags and remove them from the string keeping the essential information.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
class Program
{
static void Main(string[] args)
{
string data = "[color value=0x000000]This house is [wave][color value=0xFF0000]haunted[/color][/wave]. " +
"I've heard about ghosts [shake]screaming[/shake] here after midnight.[/color]";
ParsedData result = ParseData(data);
foreach (TagInfo t in result.tags)
{
if (string.IsNullOrEmpty(t.attributeName))
{
Console.WriteLine("Apply {0} from {1} to {2}", t.name, t.start, t.start + t.length - 1);
}
else
{
Console.WriteLine("Apply {0} {1}={2} from {3} to {4}", t.name, t.attributeName, t.attributeValue, t.start, t.start + t.length - 1);
}
Console.WriteLine(result.data);
Console.WriteLine("{0}{1}\n", new string(' ', t.start), new string('-', t.length));
}
}
static ParsedData ParseData(string data)
{
List<TagInfo> tagList = new List<TagInfo>();
Regex reTag = new Regex(#"\[(\w+)(\s+(\w+)\s*=\s*([^\]]+))?\]|\[(\/\w+)\]");
Match m = reTag.Match(data);
// Phase 1 - Collect all the start and end tags, noting their position in the original data string
while (m.Success)
{
if (m.Groups[1].Success) // Matched a start tag
{
tagList.Add(new TagInfo()
{
name = m.Groups[1].Value,
attributeName = m.Groups[3].Value,
attributeValue = m.Groups[4].Value,
tagLength = m.Groups[0].Length,
start = m.Groups[0].Index
});
}
else if (m.Groups[5].Success)
{
tagList.Add(new TagInfo()
{
name = m.Groups[5].Value,
tagLength = m.Groups[0].Length,
start = m.Groups[0].Index
});
}
m = m.NextMatch();
}
// Phase 2 - match end tags to start tags
List<TagInfo> unmatched = new List<TagInfo>();
foreach (TagInfo t in tagList)
{
if (t.name.StartsWith("/"))
{
for (int i = unmatched.Count - 1; i >= 0; i--)
{
if (unmatched[i].name == t.name.Substring(1))
{
t.otherEnd = unmatched[i];
unmatched[i].otherEnd = t;
unmatched.Remove(unmatched[i]);
break;
}
}
}
else
{
unmatched.Add(t);
}
}
int subtractLength = 0;
// Phase 3 - Remove tags from the string, updating start positions and calculating length in the process
foreach (TagInfo t in tagList.ToArray())
{
t.start -= subtractLength;
// If this is an end tag, calculate the length for the corresponding start tag,
// and remove the end tag from the tag list.
if (t.otherEnd.start < t.start)
{
t.otherEnd.length = t.start - t.otherEnd.start;
tagList.Remove(t);
}
// Keep track of how many characters in tags have been removed from the string so far
subtractLength += t.tagLength;
}
return new ParsedData()
{
data = reTag.Replace(data, string.Empty),
tags = tagList.ToArray()
};
}
class TagInfo
{
public int start;
public int length;
public int tagLength;
public string name;
public string attributeName;
public string attributeValue;
public TagInfo otherEnd;
}
class ParsedData
{
public string data;
public TagInfo[] tags;
}
}
The output is:
Apply color value=0x000000 from 0 to 76
This house is haunted. I've heard about ghosts screaming here after midnight.
-----------------------------------------------------------------------------
Apply wave from 14 to 20
This house is haunted. I've heard about ghosts screaming here after midnight.
-------
Apply color value=0xFF0000 from 14 to 20
This house is haunted. I've heard about ghosts screaming here after midnight.
-------
Apply shake from 47 to 55
This house is haunted. I've heard about ghosts screaming here after midnight.
---------
Let me show you a parsing method that you can apply not only to the case above, but to every case with a pattern cutting through the case. This method is not limited to the terms - color, wave, shake.
private List<Tuple<string, string>> getVals(string input)
{
List<Tuple<string, string>> finals = new List<Tuple<string,string>>();
// first parser
var mts = Regex.Matches(input, #"\[[^\u005D]+\]");
foreach (var mt in mts)
{
// has no value=
if (!Regex.IsMatch(mt.ToString(), #"(?i)value[\n\r\t\s]*="))
{
// not closing tag
if (!Regex.IsMatch(mt.ToString(), #"^\[[\n\r\t\s]*\/"))
{
try
{
finals.Add(new Tuple<string, string>(Regex.Replace(mt.ToString(), #"^\[|\]$", "").Trim(), ""));
}
catch (Exception es)
{
Console.WriteLine(es.ToString());
}
}
}
// has value=
else
{
try
{
var spls = Regex.Split(mt.ToString(), #"(?i)value[\n\r\t\s]*=");
finals.Add(new Tuple<string, string>(Regex.Replace(spls[0].ToString(), #"^\[", "").Trim(), Regex.Replace(spls[1].ToString(), #"^\]$", "").Trim()));
}
catch (Exception es)
{
Console.WriteLine(es.ToString());
}
}
}
return finals;
}
I also have an experience parsing JSON with a single regular expression. If you wonder what it is, visit my blog www.mysplitter.com .

How to remove asterisk from this spin syntax code?

here is my code it is a text spinner (synonym)
public function fetchContent($keyword)
{
$customContent = $this->getOption('custom_content_text');
$this->_setHttpStatusCode(200);
if (!$customContent)
{
$this->_setContentStatus(self::CONTENT_STATUS_NO_RESULTS);
return false;
}
if (preg_match_all('/({\*)(.*?)(\*})/', $customContent, $result))
{
if (is_array($result[0]))
{
foreach ($result[0] as $index => $group_string)
{
//replace the first or next pattern match with a replaceable token
$customContent = preg_replace('/(\{\*)(.*?)(\*\})/', '{#'.$index.'#}', $customContent, 1);
$words = explode('|', $result[2][$index]);
//clean and trim all words
$finalPhrase = array();
foreach ($words as $word)
{
if (preg_match('/\S/', $word))
{
$word = preg_replace('/{%keyword%}/i', $keyword, $word);
$finalPhrase[] = trim($word);
}
}
$finalPhrase = $finalPhrase[rand(0, count($finalPhrase) - 1)];
//now inject it back to where the token was
$customContent = str_ireplace('{#' . $index . '#}', $finalPhrase, $customContent);
}
$this->_setContentStatus(self::CONTENT_STATUS_PASSED);
}
}
return $customContent;
}
}
there is regex that request bracket like this
{*spin1|spin2|spin3*}
here is the regex from the snippet above
if (preg_match_all('/({\*)(.*?)(\*})/', $customContent, $result))
$customContent = preg_replace('/(\{\*)(.*?)(\*\})/', '{#'.$index.'#}', $customContent, 1);
i would like to remove the * to format allow just {spin1|spin2|spin3} wich is more compatible with most spinner ,
i tried with some regex that i find online
i tried to remove the * from both regex without result
thanks you very much for your help
Remove \* instead of just * – Lucas Trzesniewski

Magento: get a list of attribute values

I am new to Magento and I'm building a bookshop. I have an attribute called author, and I would like to show a list of all authors (a list of their attribute values). I tried to create a widget and use this code in it but it returns me an empty array. How can I achieve this? Where should I place the code, in a widget, a block?
protected function _toHtml()
{
$name='author';
$attributeInfo = Mage::getResourceModel('eav/entity_attribute_collection')->setCodeFilter($name)->getFirstItem();
$attributeId = $attributeInfo->getAttributeId();
$attribute = Mage::getModel('catalog/resource_eav_attribute')->load($attributeId);
$attributeOptions = $attributeInfo->getSource()->getAllOptions(false);
$html = '<ul>';
foreach($attributeOptions as $opt)
$html .= '<li>'.$opt[0].'</li>';
$html .= '</ul>';
return $html;
}
$attributes = $product->getAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getIsVisibleOnFront()) {
$value = $attribute->getFrontend()->getValue($product);
echo $value
}
}
you can get attribute with this code just write this code in view.phtml
Thank you very much, your code worked quite good for a particular product, however i finally get what I wanted using this:
$attributeCode='author';
// build and filter the product collection
$products = Mage::getResourceModel('catalog/product_collection')
->addAttributeToFilter($attributeCode, array('notnull' => true))
->addAttributeToFilter($attributeCode, array('neq' => ''))
->addAttributeToSelect($attributeCode);
// get all distinct attribute values
$usedAttributeValues = array_unique($products->getColumnValues($attributeCode));
sort($usedAttributeValues);

Hide product description

How can I hide product description when the description is long in Opencart (product page) to reduce the load product page, but after clicking on the detail link then came out a full description.
In image you can see Example, Sorry for my bad english, Thanks!
Here is a link to example image example
Why not just truncate it? It will force it to be the right length for you every time!
Go to catalog/controller/product/category.php and when you see
foreach ($results as $result) {
if ($result['image']) {
$image = $this->model_tool_image->resize($result['image'], $this->config->get('config_image_product_width'), $this->config->get('config_image_product_height'));
} else {
$image = false;
}
Add this next:
function truncate($description, $tLimit="20", $break=" ", $pad="...")
{
if(strlen($string) <= $tlimit) return $string;
if(false !== ($breakpoint = strpos($string, $break, $tlimit))) {
if($breakpoint < strlen($string) - 1) {
$string = substr($string, 0, $breakpoint) . $pad;
}
}
return $description;
}
Feel free to change the variables:
$tLimit is how many letters you want to allow it.
$break is where you want it to cut off, right now it is set to cut off at the next space. You can have it interrupt words if you like by putting $break=""
$pad is what you want it to show after it cuts off the text.
If you really want no description to show at all Then I recommend still doing something similar to the original script.
function getDescriptionLength($description, $tLimit="20")
{
if(strlen($string) <= $tlimit) return $string;
else {
$description = NULL;
}
return $description;
}