Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   "Метки-переменных" в XML (http://www.flasher.ru/forum/showthread.php?t=177861)

Universe 10.04.2012 14:30

"Метки-переменных" в XML
 
Такой вопрос: есть ли такая фича при написании строки в xml как то указывать не текст а "метку" которая будет говорить компилятору, чтобы он искал в пределах данного класса переменную и подставлял её вместо метки в результирующую строку? Или это можно сделать только при помощи методов класса String?

gloomyBrain 10.04.2012 14:33

Вас непонятно. Вы компилируете xml?

Universe 10.04.2012 14:37

я загружаю строку из xml, но хочу чтобы в ней содержалась ссылка на переменную которая есть у меня в классе, в котором я эту строку использую. Т.е. вот строка из xml
"Попробуйте ещё раз! У вас осталось %tries% попытки!"
Переменная tries у меня объявлена в классе сверху. Хочется чтобы получилось как то вот так:
"Попробуйте ещё раз! У вас осталось" + tries + "попытки!"

wvxvw 10.04.2012 14:38

Код AS3:

<xml>{this.variable}</xml>

?

EDIT: А... XML уже готовый? Ну тогда нет, нет ничего специально заготовленного, надо самому.

Universe 10.04.2012 14:43

регулярным выражением, через replace ? А как перевести в строку имя переменной?

wvxvw 10.04.2012 14:54

Переменные бывают связаными в лексическом окружении и статическом / динамически (lexical binding and static / dynamic binding). Первый случай - внутри функции аргументы и локальные переменные связаны лексически с контекстом функции. Имена таких переменных не хранятся в скомпилированом коде. Статически / динамически связанные переменные - например, поля класса, их, естественно, можно получить как объект[имя свойства], т.как имена хранятся в скомпилированом коде.
Так что если речь о лексической привязке - то только вручную, типа: string.replace(/variable/g, variable);

gloomyBrain 10.04.2012 14:56

Ну при таком раскладе тебе нужно что-то вроде своего мета-языка небольшого изобрести. То есть смотри как - есть 2 вещи:
- xml с заготовками
- хранилище данных (оттуда берем данные для подстановки)

так вот, имея xml вида:
Код AS3:

<node>У Вас осталось [player.numTries] попыток</node>

Вот тут нужно вычленить все "выражения" (то что внутри []-скобочек) и скомпилировать их. Копиляция - довольно простая вешь - есть хранилище данных, есть имя свойства (указанное как [player.numTries]).
То есть берем наше хранилище, берем у него свойство player, у того что получили берем свойство numTries. То что получили - кладем в xml вместо выражения [player.numTries]. Ну и выводим результат уже с подстановкой.
Кстати, очень крутая система, туда же можно прикрутить условия, вычисления и локализацию. Даже вызовы методов можно. Главное задать "контекст исполнения" - в рассмотренном выше примере это хранилище данных.

ramshteks 10.04.2012 15:03

вот у темя есть нужная для вас функция:
Код AS3:

static public function printfwv(string:String, vars:Object):String {
                        if (vars == null) return string;
 
                        const NOT_EXIST:int = -1;
 
                        const ALPHABET:String = "qwertyuiopasdfghjklzxcvbnm_";
                        const S_TEXT:int = 1;
                        const S_VAR:int = 2;
                        const PREFIX:String = "%";
 
                        //быстрая проверка на содержание двух %
                        var iof:int = string.indexOf(PREFIX);
                        if (iof == NOT_EXIST) {
                                return string;
                        }else {
                                if (string.indexOf(string, iof++) == NOT_EXIST) return string;
                        }
                        //
 
                        var src:String = string;
                        var buffer:String = "";
                        var state:int = S_TEXT;
                        var var_buff:String = "";
                        var c:String;
 
                        for (var i:int = 0, len:int = src.length; i < len; i++) {
                                c = src.charAt(i);
 
                                switch(state) {
                                        case S_TEXT:
                                                if (PREFIX == c) {
                                                        state = S_VAR;
                                                }else {
                                                        buffer += c;
                                                }
                                                break;
 
                                        case S_VAR:
                                                if (ALPHABET.indexOf(c) != -1) {
                                                        var_buff += c;
                                                }else {
                                                        if (c == PREFIX) {
                                                                if (vars[var_buff] == undefined) {
                                                                        buffer += PREFIX + var_buff;
                                                                        var_buff = "";
                                                                        i--;
                                                                        state = S_TEXT;
                                                                }else {
                                                                        buffer += vars[var_buff];
                                                                }
                                                        }else{
                                                                buffer += PREFIX + var_buff + c;
                                                        }
                                                        var_buff = "";
                                                        state = S_TEXT;
                                                }
                                                break;
                                }
                        }
 
                        if (state == S_VAR) {
                                buffer += PREFIX + var_buff;
                        }
 
                        return buffer;
                }

первым параметром вы передаете строку типа "bla bla %var% bla bla %var% bla bla %var2%", а вторым объект, если объект содержит переменные с именем var и var2, то вы функция вернет, то что вам нужно

Добавлено через 49 секунд
правда никакой привязки к xml нет, поэтому из xml вам самому придется выдрать строку-шаблон

Universe 10.04.2012 16:44

о, супер, большое спасибо ramshteks! Сейчас попробую!))

Добавлено через 21 минуту
чтото не пашит(((
Засунул вашу функцию в свой класс MyUtils, вызываю следующим образом
Код AS3:

MyUtils.stringVars("a123 %tries% vsvs", this);

var tries в классе есть, пробовал её делать и публичной и статической, но всё равно возвращает не изменённую строку((

ramshteks 10.04.2012 17:17

метод писался под такое использование:
printfwv("a123 %tries% vsvs", {tries:"TEST String"});

Для вашего варианта, даже не проверялось, так что подсказать не могу

тоесть должен работать вот такой вариант:
printfwv("a123 %tries% vsvs", {tries:String(this.tries)});

Добавлено через 10 минут
Я прошу прощения, сейчас обнаружил, что функция действительно не корректно работает

Universe 10.04.2012 17:31

сможете сюда скинуть рабочий вариант?:)

ramshteks 10.04.2012 17:41

на данный момент, я проверил работу и все таки первый вариант был рабочим. Я подумал, что он не работает, потому что использовал в тестовом варианте сейчас в названиях полей символы которые не разрешены.
обратите внимание в содержание константы ALPHABET
Все символы которые она содержит, являются разрешенными. В случае если в шаблоне в названии переменной есть символ которые не является разрешенным, например цифра или английская буква в верхнем регистре, то эта переменная не будет воспринята. И замены не произойдет. А вобще скиньте ваш код конкретный, который не работает. Я попробую вам помочь.

А так даже тот код который я скидывал вполне рабочий. Переменные работают для такого объекта прекрасно:
Код AS3:

package  
{
        /**
        * ...
        * @author Shirobok Pavel (ramshteks@gmail.com)
        */

        public class TestObject
        {
                public var test:String = "111";
                public var test1:int=112;
                public var test2:*= 113;
                public function TestObject()
                {
 
                }
 
                public function get test4():String {
                        return "114"
                }
 
                public function get test5():int {
                        return 115;
                }
 
        }
 
}

Использование:
Код AS3:

trace(StringUtils.printfwv("%test% %test1% %test2% %test4% %test5% ", new TestObject()));//Trace: 111 112 113 114 115

Добавлено через 17 минут
единственно нашел ошибку с вылитающим эксепшеном, если в объекте нет такой переменной. Теперь обработка не прекращается, а просто выдается трейс. Ну и переменная остается незамененной. +Добавил в алфавит цифры. Вот исправленный вариант

Код AS3:

static public function printfwv(string:String, vars:Object):String {
                        if (vars == null) return string;
 
                        const NOT_EXIST:int = -1;
 
                        const ALPHABET:String = "qwertyuiopasdfghjklzxcvbnm_1234567890";
                        const S_TEXT:int = 1;
                        const S_VAR:int = 2;
                        const PREFIX:String = "%";
 
                        //быстрая проверка на содержание двух %
                        var iof:int = string.indexOf(PREFIX);
                        if (iof == NOT_EXIST) {
                                return string;
                        }else {
                                if (string.indexOf(string, iof++) == NOT_EXIST) return string;
                        }
                        //
 
                        var src:String = string;
                        var buffer:String = "";
                        var state:int = S_TEXT;
                        var var_buff:String = "";
                        var c:String;
 
                        for (var i:int = 0, len:int = src.length; i < len; i++) {
                                c = src.charAt(i);
 
                                switch(state) {
                                        case S_TEXT:
                                                if (PREFIX == c) {
                                                        state = S_VAR;
                                                }else {
                                                        buffer += c;
                                                }
                                                break;
 
                                        case S_VAR:
                                                if (ALPHABET.indexOf(c) != NOT_EXIST) {
                                                        var_buff += c;
                                                }else {
                                                        if (c == PREFIX) {
                                                                var correct_object:Boolean = true;
                                                                try {
                                                                        vars[var_buff];
                                                                        if (vars[var_buff] == undefined) correct_object = false;
                                                                }catch (e:Error) {
                                                                        correct_object = false;
                                                                        trace("warning: was catched exception when using source-object:" + e.message);
                                                                }
 
                                                                if (!correct_object){
                                                                        buffer += PREFIX + var_buff;
                                                                        var_buff = "";
                                                                        i--;
                                                                        state = S_TEXT;
                                                                }else {
                                                                        buffer += vars[var_buff];
                                                                }
                                                        }else{
                                                                buffer += PREFIX + var_buff + c;
                                                        }
                                                        var_buff = "";
                                                        state = S_TEXT;
                                                }
                                                break;
                                }
                        }
 
                        if (state == S_VAR) {
                                buffer += PREFIX + var_buff;
                        }
 
                        return buffer;
                }


Universe 10.04.2012 22:07

незнаю....несколько раз проверил ваш метод и он не работает у меня(((
кстати, а запись new TestObject() ведь равносильна this, если мы пишем этот код внутри класса TestObject ?

strangedk 10.04.2012 22:38

А зачем всё так сложно, не проще иметь 2 узла xml, а между ними вставлять уже необходимую переменную.

<useVarCount>
<node>У вас осталось</node>
<node>попыток</node>
</useVarCount>

Universe 10.04.2012 22:39

Нашёл проблему: ваш метод не работает, если в строке перед %tries% идёт какойто текст. Как это решить?

Добавлено через 1 минуту
to strangedk - а если у вас таких узлов нужно 10 в строке, будете 10 тегов писать? Это как минимум не удобно

strangedk 10.04.2012 22:46

ага, буду 10 тегов)

Не, ну я просто размышляю простым путём, и ничто не мешает создать тег типа <injectNode> и в него пихать то что должно быть между тегами:

<node1>У вас осталось</node1>
<injectNode>counter</injectNode>
<node2>попыток</node2>

Я знаю что это совсем не гибко, и прекрасно это понимаю.
Но увидев тучу кода выше, я уж и не знаю какой метод лучше.

Хочется - пишите свой парсер, кто ж мешает

ramshteks 10.04.2012 22:46

так вот эту вещь удалите:
Код AS3:

//быстрая проверка на содержание двух %
                        var iof:int = string.indexOf(PREFIX);
                        if (iof == NOT_EXIST) {
                                return string;
                        }else {
                                if (string.indexOf(string, iof++) == NOT_EXIST) return string;
                        }
                        //

Добавлено через 7 минут
strangedk, Извините, конечно, на правах холивара. Но я удивляюсь порой программистам. Вы предлагаете меньше писать кода, но при этом предлагаете решение, которое труднее поддерживать и заранее более уязвимое к человеческому фактору и в плане количества данных гораздо более избыточное?

strangedk 10.04.2012 23:06

Я вас прекрасно понимаю. Сам стремлюсь всегда к совершенству, и это замечательно.
Только перебарщивать с преждевременной оптимизацией - зло

И так как мне совершенно не ясны масштабы задачи - я предложил свой вариант, который был применен в одном из моих приложений, буквально с 20 строчками текста.

Если же у вас таких внедрений будет много, тогда разумеется есть смысл изобрести свой парсер.

ramshteks 10.04.2012 23:12

всем давно известно(а может и не всем?), что "преждевременная оптимизация-это зло" фраза за которую даже Кнут уже извинялся. Так как ее цитирование без контекста, порождает вот такие "аргументы". Преждевременная оптимизация не зло. Забудьте уже про это.

strangedk 11.04.2012 00:02

Ага, в приложении которое должно выводить 2 строчки давайте прикрутим locale, ant, внешний logger и еще какой-нибудь фреймверк заюзаем, а-ля robotlegs или parsley.

- так чувствуется разница? )

ramshteks 11.04.2012 00:20

strangedk, вы путаете кислое с длинным. Если будет нужно иметь несколько сборок этого приложения - заюзаем ант. Если необходимо будет выяснять увидел пользователь текст или нет то внешний логгер. Если текст должен на 100 языках сделаем локализацию. Даже если на начальном этапе нужно будет всего один, а 99 появятся позже.

Есть задача. И если вы не понимаете, что задача это не только "здесь и сейчас", а еще и "завтра, послезавтра и через месяц", то вы обречены бороться со своевременной неоптимизацией.

Кроме всего переходить на частные крайности вроде вашего примера, просто не корректно и вы надеюсь это понимаете.

strangedk 11.04.2012 01:46

Ну если вернуться к сути вопроса - то повторюсь, что я совсем не в курсе масштабов задания.

Если нужно по быстрому, "сделали-забыли", и не сильно заморачиваясь - я бы не брался писать парсер подставляемых переменных. Это не оправданно.

Если проект интересный, и перспективный - то конечно и самому хочется сделать его максимально гибким.
И думаю я бы не остановился на XML.

Dukobpa3 11.04.2012 12:44

Код AS3:

text = "у вас осталось {{tries}} попыток"
name = "tries"
value = String(10)
 
text = text.replace(new RegExp("{{" + name + "}}", "g"), txt);
 
trace(text) // "у вас осталось 10 попыток"


strangedk 11.04.2012 12:52

Dukobpa3, хорошее решение, тоже думал в эту сторону.

Я думаю добавил бы связку name-value в Dictionary, и чтобы по имени сразу можно было обратиться к значению.

zuxul 11.04.2012 12:55

Dictionary может не подойти, нужен Object, у него ключи строковые

Dukobpa3 11.04.2012 12:55

Ну вообще у нас так же сделано как вот в этом варианте.
Просто получаем строку, и обжект с именами для замены.

Universe 11.04.2012 13:19

Цитата:

так вот эту вещь удалите:
Да, теперь всё работает! Большое спасибо за помощь, ramshteks! :)

ramshteks 11.04.2012 13:26

Dukobpa3, strangedk, решение с регекспами у меня тоже есть. Но оно немного для других целей. Оно гораздо проще, но и подходит далеко не всегда:
Код AS3:

private static const regexVar:RegExp = /%[A-Za-z]*%/;
 
                /**
                * Modified and return string, user var name having format "%varname%"
                * @example StringUtils.printf("Some text, %var%, %var2%, %var%", 1, "test", {});
                * return "Some text, 1, test, [object Object]"
                * @param        string formatted string
                * @param        ...args arguments to insert into
                * @return modified string
                */

                public static function printf(string:String, ...args):String {
                        var res:String = string;
                        for each(var p:String in args) {
                                res = res.replace(regexVar, p);
                        }
                        return res;
                }


in4core 11.04.2012 13:47

Цитата:

Dictionary может не подойти, нужен Object, у него ключи строковые
ЭЭЭ а у словарика не могут чтоли быть? ))))))))))))

Universe 11.04.2012 14:19

Теперь возник вопрос, а можно ли что-то подобное сделать но с функциями?)) Т.е. чтобы вместо текста выполнялся метод выбранного объекта))

Dukobpa3 11.04.2012 14:20

ну сделай, че.

Universe 11.04.2012 14:20

Цитата:

Оно гораздо проще, но и подходит далеко не всегда:
А для каких оно целей?

ramshteks 11.04.2012 14:24

Цитата:

Сообщение от Universe (Сообщение 1074386)
Теперь возник вопрос, а можно ли что-то подобное сделать но с функциями?)) Т.е. чтобы вместо текста выполнялся метод выбранного объекта))

конечно можно. Берите за основу тот код который я вам дал и дорабатывайте до вызова функций. Но если нет необходимости в передаче каких то параметров, то можно просто использовать геттеры

Добавлено через 1 минуту
Цитата:

Сообщение от Universe (Сообщение 1074388)
А для каких оно целей?

В моей системе локализации используется. Для подстановки значений в шаблонный текст


Часовой пояс GMT +4, время: 14:04.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.