Страницы

Поиск по вопросам

среда, 28 ноября 2018 г.

Парсинг сложной Xml в С#

Столкнулся с проблемой. У меня есть сложная xml
Watershed0001150 Watershed-0000001150 14 ff 06/03/2015 00:00:00 06/03/2015 00:00:00 117 10.5 06/03/2015 01:00:00 100 7
Надо это десериализовать в class. Я не могу понять, как это сделать, если у меня может быть от с1 до с10000000


Ответ

Один из вариантов - реализовать интерфейс IXmlSerializable, например:
[XmlRoot("XML")] public class Xml { [XmlAttribute("id")] public string Id { get; set; } [XmlElement("DATA")] public Data Data { get; set; } }
public class Data { [XmlElement("SRL_NUM")] public string SrlNum { get; set; } [XmlElement("METER_ID")] public string MeterId { get; set; } [XmlElement("MAC")] public string Mac { get; set; } [XmlElement("METER_ADDR")] public string MeterAddr { get; set; } [XmlElement("METER_TIME")] public string MeterTime { get; set; } [XmlElement("REC")] public List Recs { get; } = new List(); }
public class Rec : IXmlSerializable { public int Id { get; set; } public string Ts { get; set; } public List Cs { get; } = new List();
public XmlSchema GetSchema() => null;
public void ReadXml(XmlReader reader) { Id = int.Parse(reader.GetAttribute("id")); reader.ReadStartElement(); Ts = reader.ReadElementContentAsString("TS", ""); while (reader.NodeType == XmlNodeType.Element) Cs.Add(reader.ReadElementContentAsDouble()); reader.ReadEndElement(); }
public void WriteXml(XmlWriter writer) { writer.WriteStartAttribute("id"); writer.WriteValue(Id); writer.WriteEndAttribute(); writer.WriteStartElement("TS"); writer.WriteValue(Ts); writer.WriteEndElement(); for (int i = 0; i < Cs.Count; ++i) { writer.WriteStartElement("C" + (i + 1)); writer.WriteValue(Cs[i]); writer.WriteEndElement(); } } }
Десериализация вполне стандартна:
string s = @"..."; var ser = new XmlSerializer(typeof(Xml)); var xml = (Xml)ser.Deserialize(new StringReader(s));

Написание новых man-страниц

В /usr/share/man/man1/ находятся мануалы для программ с расширением gz. Внутри этих gz архивов html (если верить команде file /usr/share/man/man1/ab.1) документ с похожим содержанием: (
.\" XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .\" DO NOT EDIT! Generated from XML source. .\" XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .de Sh \" Subsection .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Ip \" List item .br .ie \
(.$>=3 .ne \\$3 .el .ne 3 .IP "\\$1" \\$2 .. .TH "AB" 1 "2015-05-01" "Apache HTTP Server" "ab"
.SH NAME ab \- Apache HTTP server benchmarking tool
Вопросы:
Это чистый html ? Как добавить дополнительную локализацию,чтобы сохранился оригинал.Т.е переведённый файл нужно будет добавить в этот же gz архив или создавать для новой локализации отдельный gz архив в отдельной папке /usr/share/man/ru/man1/локализация_для_проги.gz ? Что означает конечная цифра 1 в названии файла ? При локализации ab.1 мне нужно будет изменить его на ab.2 ?


Ответ

Это чистый html ?
Нет, это даже не язык разметки. это plain-text, разбавленный командами форматирования roff. когда-то программа, понимающая это форматирование, и способная оформить текст в каком-нибудь другом формате, называлась runoff, потом пошли наследники — troff, nroff, groff. (доп. см. $ man 7 man)
Как добавить дополнительную локализацию,чтобы сохранился оригинал.Т.е переведённый файл нужно будет добавить в этот же gz архив или создавать для новой локализации отдельный gz архив в отдельной папке /usr/share/man/ru/man1/локализация_для_проги.gz?
Да, локализованные man-страницы надо помещать в каталоги, называющиеся по имени языка локализации. каталоги должны располагаться на том же уровне иерархии, что и каталоги для секций (см. следующий ответ), и тоже должны быть разбиты на под-каталоги с секциями (man1, man2 и т.д.). т.е., путь должен звучать примерно как: /usr/share/man/ru/man1/программа.1 (или /usr/share/man/ru/man1/программа.1.gz, если файл сжат программой gzip)
Что означает конечная цифра 1 в названии файла?
Это номер секции. цитата из $ man 1 man
1 Executable programs or shell commands 2 System calls (functions provided by the kernel) 3 Library calls (functions within program libraries) 4 Special files (usually found in /dev) 5 File formats and conventions eg /etc/passwd 6 Games 7 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) 8 System administration commands (usually only for root) 9 Kernel routines [Non standard]
Для примера: $ man 1 man — это запуск программы man с передачей ей двух параметров — номера секции (1) и названия запрашиваемой страницы (man). номера секций фигурируют не только в именах каталогов, но и в именах файлов (предполагаю, что последнее — это просто дань традиции или «тяжёлое наследие»)
При локализации ab.1 мне нужно будет изменить его на ab.2?
Нет, переименовывать не надо. Разве что файл по смыслу должен принадлежать секции 2 и размещаться каталоге ru/man2 (см. предыдущий ответ).

Вопрос по x = yield f() — какой порядок вызова и присваивания?

объясните, что означает эта строка внутри функции
some_variable = yield some_function()
если я правильно понимаю, то yield some_function() в данном случае отправляет результат в генератор, после чего записывается в переменную и продолжается выполнение кода после этого присваивания.
Если я понял верно, тогда вопрос. А присваивание происходит после того, как происходит возврат и обработка текущего результата, или сначала выполняется до конца текущая функция, затем в генератор возвращается результат?


Ответ

Если создать генератор g = gen(), то сам он ничего не делает, пока снаружи его не продвинут с помощью next(g). next(g) равнозначен g.send(None)
g.send(y) позволяет передать в генератор значение:
def gen(): while True: x = yield '.' print(x * x)
Так как g = gen() само по себе не продвигает генератор, то необходимо вызывать next(g), чтобы код дошёл до строчки с x = yield '.'. Так как в начале функции нет yield, то первый вызов всегда эквивалентен g.send(None). Значения y, переданные в последующих вызовах, уже присваиваются x
>>> g = gen() >>> z = g.send(None) >>> z '.' >>> z = g.send(1) 1 >>> z '.' >>> z = g.send(2) 4 >>> z '.'
В данном случае генератор генерирует '.' постоянно.
Видно, что первый вызов g.send(None) не дошёл до x (иначе в дальнейшем получили бы TypeError из-за None * None). То есть значение переданное в g.send() возвращается из yield, на котором генератор стоял, а затем код продвигается до следующего yield и его аргумент возвращается как результат и присваивается z
Теперь можно объяснить, что происходит с:
def gen(): some_variable = yield some_function()
по шагам:
Создаём геренратор:
>>> g = gen()
Код внутри генератора при этом не выполняется Продвигаем код до первого yield, вызывается some_function(), получаем значение some_function()
>>> g.send(None) 'result from some_function()'
Имя some_variable ещё не присвоено по окончанию g.send() Следующий вызов g.send(1) присваивает 1 к some_variable имени и выбрасывает StopIteration, когда код покидает генератор:
>>> g.send(1) Traceback (most recent call last): File "", line 1, in StopIteration

Подобные генераторы или как их иногда называют сопрограммы (coroutine) могут использоваться, чтобы перемежать исполнение нескольких функций в одном потоке. Практический пример, можно выполнить NREQUESTS сетевых запросов по NCONCURRENT одновременных запросов, используя twisted.internet.defer.inlineCallbacks декоратор:
#!/usr/bin/env python """Make NREQUESTS http POST requests `NCONCURRENT` at the time using treq module.""" import os from twisted.internet import defer, task # $ pip install twisted import treq # $ pip install treq
def n_at_a_time(it, n): """Iterate over `it` concurently `n` items at a time.
`it` - an iterator creating Deferreds `n` - number of concurrent iterations return a deferred that fires on completion """ return defer.DeferredList([task.coiterate(it) for _ in range(n)])
@defer.inlineCallbacks def make_request(): # post request response = yield treq.post('http://localhost:8001/post', {'a':'b', 'c': 'd'}) data = yield response.content() # read response
def main(reactor): reqs = (make_request() for _ in range(int(os.environ['NREQUESTS']))) return n_at_a_time(reqs, n=int(os.environ['NCONCURRENT']))
task.react(main)
По похожему, но несколько другому механизму (основанному на yield from, а не yield) работают asyncio coroutines.

Сжатие журнала БД

Добрый день, со временем журнал БД сильно увеличился. Как мне его сжать? Модель восстановления полная. Интересует метод с возможностью восстановиться на любой момент момент времени и без. Знаю метод:
USE MyBase; GO ALTER DATABASE MyBase SET RECOVERY SIMPLE; GO DBCC SHRINKFILE (MyBase_Log, 1); GO ALTER DATABASE MyBase SET RECOVERY FULL; GO
есть ли еще варианты?


Ответ

У вас полная модель восстановления и вы не делаете бэкап журнала транзакций? От этого у вас и разрастается журнал. Вам необходимо добавить бэкап журнала транзакций и тогда вы сможете сразу после его выполнения выполнить операцию DBCC SHRINKFILE без изменения модели восстановления БД, так же рекомендую не урезать размер файла до 1 мб, а хотя бы указать 1ГБ(но это все зависит от размера вашей базы).

Как реализовать строчную отладку кода в C#?

Добрый день!
У меня имеется код (c#), который я формирую динамически. К примеру, сформировался код следующего вида:
NetOffice.ExcelApi.Application excel = new NetOffice.ExcelApi.Application(); excel.Visible = true; excel.Workbooks.Open(@"C:\mail.xml"); while (true) { System.Threading.Thread.Sleep(5 * 1000); break; }
Я запускаю этот код при помощи Microsoft.CodeAnalysis.CSharp.Scripting, в отдельном делегате и всё ОК. Но теперь мне необходимо по-шагово отладить данный код. Т.е. фактически, я хочу остановиться в том месте, где мне необходимо и получить все значения локальных/глобальных переменных (классический debug).
Каким образом я могу реализовать такую логику?
UPDATE:
В итоге сделал способом, который обсуждался ниже, через Scripting API. Чтобы отслеживать текущий блок(этап) кода, перед каждым шагом я вставляю ко, который меняет специальный параметр текущего домена (в него передается UID шага). Этот параметр считывается из главного домена (из которого запускается выполнение кода).


Ответ

Огоооо, ну вы и задачу себе поставили. Писать полноценный отладчик — сложно. Посмотрите сюда на список всех отладочных интерфейсов!
Но для простых случаев — выполнение куска кода и проверка переменных — можно выехать на scripting API. Вот пример (основанный на этом ответе):
var cmd = "int x = 10; int y = 20; string z = (x + y).ToString();"; ScriptState state = await CSharpScript.RunAsync(cmd); foreach (var v in state.Variables) Console.WriteLine($"var {v.Name}, type = {v.Type.Name}, value = {v.Value}");
Он выдаёт:
var x, type = Int32, value = 10 var y, type = Int32, value = 20 var z, type = String, value = 30

сборка предустановленного приложения

Существует ли разница между сборкой предустановленного приложения и сборкой стороннего приложения? Отличается .apk-файлы первой от второй? Если есть разница, то в чем она? Какие требования предъявляются?


Ответ

Не совсем уверен, что понимаю о чем идет речь, попробую ответить в меру своего понимания.
У каждого вендора (то бишь производителя) телефонов (по крайней мере у серьезных вендоров) имеется свои системные сертификаты, которым их версия оси/оболочки безусловно доверяет, соответственно приложения подписанные такими сертификатами имеют доступ к пермишенам/API недоступным простым смертным.
Здесь обсуждается способы хакинга подписи системными ключами. Ни разу не уверен, что все это работает, ибо чукча не хакер, а простой девелопер.
Возвращаясь к вашему вопросу:
Хочу узнать, могу отправить эту же сборку, или надо для производителя делать какую-то другую, отличную от моей?
Сборка наверное должна быть другая, поскольку в любом случае отличается манифест, наличием атрибута:
android:sharedUserId="android.uid.system"
Но этот атрибут не будет активирован пока не появится подпись вендора под ним. Технически APK можно подписать несколькими ключами, но проблема в том, что маркет не понимает приложения с несколькими подписями. Так что вам как то надо будет договориться с вендором, чтобы они подписали ваш калькулятор своей подписью - иначе не взлетит. Как вариант им можно послать неподписанный APK, они сами и подпишут его.

dbms_alert Интервалы опроса

Объясните, пожалуйста, вот этот абзац из документации
WAITANY procedure. If you use the WAITANY procedure, and if a signalling session does a signal but does not commit within one second of the signal, a polling loop is required so that this uncommitted alert does not camouflage other alerts. The polling loop begins at a one second interval and exponentially backs off to 30-second intervals.
Я правильно понимаю, что тут говорится о том, что при вызове WAITANY на сервере поток опрашивает наличие событий, через определенные интервалы? И если я вызвал WAITANY с достаточно большим таймаутом, то при возникновении события ко мне придет уведомление только после истечения текущего интервала запроса? Т.е. на сервере выполняется примерно такой код
function WaitAny(ATimeout) { const intervals = [0, 1, ....., 30); for (i = 0; i < intervals.length; i++) { Sleep(min(intervals[i], ATimeout)) if (IsExistsEvents()) return 0; ATimeout -= intervals[i]; if (ATimeout <= 0) return 1; } maxInterval = intervals[intervals.length - 1]; while (ATimeout > 0) { Sleep(min(maxInterval, ATimeout)) if (IsExistsEvents()) return 0; ATimeout -= maxInterval; } return 1; }


Ответ

TL;DR: Нет, при получении события, оповещение о нём будет практически сразу же передано в обработчик, то есть возврат из функции waitany. Но в определённых случаях задержка возможна (см. далее).

Передача события (signal) между отправителем и приёмником в dbms_alert транзакциональна и состоит из двух частей:
Не транзакциональная передача события посредством database pipe механизма (реализовано на dbms_pipe). Подтверждение события посредством commit в отправителе. В приёмнике это реализовано стандартным lock меанизмом БД.
В нормальном случае, приёмник ждёт получение события из pipe. При его получении, оно только регистрируется как принятое, но не подтверждённое. При наличии таких событий в приёмнике, waitany опрашивает статус сессии на предмет завершения транзакции, в которой событие было послано. Если транзакция завершается с commit, то waitany завершается и передаёт текущее событие в приёмник для дальнейшей обработки, в противном случае - rollback - событие удаляется из очереди принятых в waitany.
В исключительном, случае, о котором идёт речь в цитате из вопроса, событие ещё не зафиксировано. т.е. не подтверждено в сессии отправителя. Так как бесконечное ожидание на подтверждение будет блокировать получение других событий из pipe, начинается опрос с интервалом начиная с 1-ой с возрастанием до 30-ти секунд. То есть приёмник ожидает подтверждения завершения транзакции с текущим интервалом ожидания (1..30 сек.) и если оно не происходит, возвращается к считыванию из pipe новых событий, после чего цикл повторяется.
Почему последний случай исключительный? Если событие отправленно, но не зафиксировано сразу же, то это, как будет видно в примере ниже, не обеспечит гарантированной асинхронной обработки событий. Поэтому, или устранить задержку между signal и commit, или, если это не представляется возможным, отказаться от dbms_alert и посмотреть в сторону Advanced gueuing (dbms_aq).
Пример для наглядности. Понадобится три сесси: приёмнк, "ленивый" отправитель (PROCL=lazy SID=26) и "правильный" отправитель (PROCQ=quick SID=270).
Запустим приёмник событий:
SQL> set serveroutput on size unlimited exec dbms_alert.register ('PROCQ'); - dbms_alert.register ('PROCL'); - dbms_alert.register ('EXIT'); exec receiveSignals
В отправителе SID=26 пошлём событие с задержкой подтверждения:
SQL> exec sendSignal ('PROCL',delayed=>true) PROCL sent 14:02:10.482 from 26 delayed
В отправителе SID=270 пошлём событие и зафиксируем его сразу:
SQL> exec sendSignal ('PROCQ',delayed=>false) PROCQ sent 14:03:20.842 from 270
Вернёмся в SID=26 и подтвердим событие:
SQL> exec sendSignal (null,delayed=>false) none sent 14:03:34.272 from 26
В отправителе SID=270 пошлём событие EXIT:
SQL> exec sendSignal ('EXIT',delayed=>false) EXIT sent 14:03:41.849 from 270
Вывод в приёмнике после приёма события EXIT:
stat=0,sig=PROCL,received=14:03:34.274: msg=sent 14:02:10.482 from 26 delayed stat=0,sig=PROCQ,received=14:03:34.275: msg=sent 14:03:20.842 from 270 stat=0,sig=EXIT,received=14:03:41.852: msg=sent 14:03:41.849 from 270
Видно, что задержка в SID=26 вызвана поздним подтверждением и была получена в приёмнике сразу же после commit в отправителе (14:03:34). Но ожидание на подтверждение привело к задержке почти 15 сек на получения события от SID=270. (отпр. 14:03:20, получ. 14:03:34), т.е функция waitany в момент отправки подтверждённого события от SID=270, находилась в ожидании подтверждения от SID=26.

PS: Этот ответ не является переводом, ни прямым, ни вольным, ответа сотрудника компании Oracle на аналогичный вопрос ТС на engl. SO, он скорее всего попытка более наглядного пояснения ситуации.
Функции, которые использованы в воиспроизводимом примере выше:
create or replace procedure receiveSignals is sig varchar2 (32); stat number; msg varchar2 (64); begin loop dbms_alert.waitany (sig, msg, stat); dbms_output.put_line ( 'stat='||stat||',sig='||sig||',received='||to_char (systimestamp, 'hh24:mi:ss.ff3')||': msg='||msg); exit when sig = 'EXIT'; end loop; end; / create or replace procedure sendSignal (sig varchar2, delayed boolean := true) is msg varchar2 (1800) := 'sent '||to_char (systimestamp, 'hh24:mi:ss.ff3')||' from '|| sys_context ('USERENV', 'SID')||case when delayed then ' delayed' end; begin if sig is not null then dbms_alert.signal (sig, msg); end if; if not delayed then commit; end if; dbms_output.put_line (case when sig is null then 'none' else sig end||' '||msg); end; /

Как в Java сравнить 2 вещественных числа на равенство?

У меня есть 2 вещественных числа, например типа double. Я хочу их сравнить на равенство с заданной точность. Как правильно выполнять такое сравнение?
Пока в голову приходит такой вариант:
double x, y; ... public boolean isEqual(double x, double y, double eps){ return Math.abs(x-y) < eps; }
Кажется, что в Java должны быть встроенные способы сравнить 2 вещественных числа. Такие существуют?


Ответ

Вообще, это довольно интересный вопрос.
Да, в общем случае всегда рекомендуется использовать что-то вроде:
bool isEqual = fabs(a1 – a2) <= epsilon;
Но более интересный вопрос, какое значение для epsilon использовать.
К примеру, в C++ в float.h есть константа FLT_EPSILON, равная 1E-5. Всё, можно такое же значение использовать, да?
Вполне, если числа большие. Но если у вас число в промежутке [0;1], то начинаются проблемы. Точность вычисления будет соизмерима с фактическим значением, что приведёт к тому, что на такое сравнение нельзя будет положиться.
Относительный эпсилон
Для сравнения чисел можно руководствоваться правилом:
Для сравнения a1 и a2 вычисляем diff = fabs(a1-a2). Если diff меньше ,чем n% от max(abs(a1), abs(a2)), тогда a1 и a2 можно считать равными.
public static bool almostEqualRelative(float a, float b, float maxRelDiff = FLT_EPSILON) { // находим разницу float diff = fabs(a - b); A = fabs(a); B = fabs(b); // находим большее float largest = (b > a) ? b : a;
if (diff <= largest * maxRelDiff) return true;
return false; }
Более сложно и подробней про это можно почитать в этой занятной статье.

Отправка email (java)

Я пытаюсь отправить email с помощью следующего кода:
try { String mailFrom = "sender@mail.ru"; String pass = "pass"; String mailTo = "recipient@gmail.com";
java.util.Properties props = new Properties(); props.put("mail.smtp.host", "smtp.mail.ru"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.port", "587"); props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.connectiontimeout", "60000"); // 60 seconds props.put("mail.smtp.timeout", "60000");
Session session = Session.getInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return (new PasswordAuthentication(mailFrom, pass)); } }); Message msg = new MimeMessage(session); InternetAddress addressFrom = new InternetAddress(mailFrom); msg.setFrom(addressFrom); InternetAddress addressTo = new InternetAddress(mailTo); msg.setRecipient(Message.RecipientType.TO, addressTo); msg.setSubject("testemail"); Transport.send(msg); } catch (Throwable e) { System.err.println("Exception : " + e.toString()); }
но получаю ошибку:
Exception : javax.mail.MessagingException: Could not convert socket to TLS; nested exception is: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
При этом я добавила ssl-сертификат mail.ru в хранилище сертификатов JVM, и он был успешно добавлен.
Если же я добавляю в сессию следующее свойство:
props.put("mail.smtp.ssl.trust", "*");
то по прошествии минуты (по истечении установленного таймаута) получаю другую ошибку:
Exception : javax.mail.MessagingException: Exception reading response; nested exception is: java.net.SocketTimeoutException: Read timed out
Как исправить это и всё-таки отправить email?


Ответ

Проблема была в том, что я пыталась отправить пустое сообщение, без тела (почтовый клиент обычно разрешает посылать такие сообщения, но в моём случае это привело к ошибке).
Это выяснилось после того, как я добавила в сессию свойство
props.put("mail.debug", "true");
После этого в отладочной информации я увидела причину ошибки:
354 Enter message, ending with "." on a line by itself DEBUG SMTP: MessagingException while sending, THROW: javax.mail.MessagingException: No MimeMessage content
После получения этого описания ошибки я добавила тело сообщения:
msg.setText("Test message!");
и email был успешно отправлен.

Использование final аргумента в локальном классе

Без всяких лишних слов напишу код:
public class A {
static Object f() { String str = "hello"; str = "world"; // (1) ошибка компиляции!
class X extends Object { public String toString() { return str; } }
return new X(); }
static Object g() { String[] s = { "hello", "silence" }; s[0] = "World"; // (2) НЕТ ошибки компиляции.
class X extends Object{ public String toString() { return s[0]; } }
return new X(); }
public static void main(String[] args) { }
}
Закомментирование строки (1) даст компилятору понять, что str в f() есть effectively final, код скомпилируется. Закомментирование строки (2) не нужно. Ведь s -- ссылка на массив, которая не изменяется. Взятие s[0] затем законно.
Вопрос: Зачем от нас требую использование только final или effectively final локальных переменных в локальном классе? Я могу работать с первой ячейкой массива s и всё у меня будет замечательно.

Мои мысли и непонятки: как вообще будет выглядеть код объекта класса f().X ? Это будет объект с кодом из Object, но его метод toString() будет таким (псевдокод):
public String toString() { return 0x47e9c2dd; }
Где, как понимаю, 0x47e9c2dd адрес той самой строки str? Но если бы оно работало так, то какая разница какой адрес мы бы положили в этот метод, когда создавали экземпляр класса f().X ...

Так же не понимаю: мы определяем метод toString() класса f().X ссылаясь на локальную переменную str, которая (как ссылка) исчезнет по завершению работы f(), но при том объект возвращённый из f() содержит в своём toString() ссылку на str (как объект). Значит у нас тратится RAM на то, чтобы держать метод f().X.toString() определённым?....

В общем я очень запутался, совершенно не понимаю происходящего с этими локальными классами в том свете, что на нас возложено это странное требование на final или effectively final. Объясните происходящее, пожалуйста...


Ответ

В Java замыкания захватывают значения, а не переменные. При компиляции внутреннего класса, Java создаст в нём поле для хранения значения переменной, "захваченной" из внешней области видимости:
$ javac A.java $ javap A$1X
Compiled from "A.java" class A$1X { final java.lang.String val$str; // <-- "hello" A$1X(); public java.lang.String toString(); }
У требования к неизменяемости обеих переменных есть несколько причин.
Уверен, самым главным было то, что Java позиционируется как язык, в корне пресекающий возможность совершения множества ошибок и вынуждающий программистов писать правильный код. А классические замыкания - это не самая простая и интуитивно понятная тема. Поэтому в Java замыкания сознательно ограничили и сделали более простыми.
Кроме того, если реализовывать классические замыкания, то необходимо было бы каким-то образом сохранять захваченную переменную. Например, выделять место в куче. Во-первых, это потребовало бы доработки виртуальной машины и, возможно, изменения байт-кода. Во-вторых, это повод для утечек памяти.
Можно было бы сделать поле val$str изменяемым, но это усложнило бы и замедлило использование анонимных классов и лямбда-выражений в многопоточной среде. Можно было бы дать программисту возможность выбирать, когда это поле финальное, а когда нет, но тогда оно перестаёт быть неявным и мы опять приходит к необходимости доработки виртуальной машины, усложнению языка и поводам для ошибок.
Можно было бы не ограничивать изменяемость переменной из внешней области видимости, но на уровне языка переменная str одна, вероятность того, что в разных областях видимости она может иметь разные значения - это ещё больший повод для ошибок, чем классические замыкания.
Подытоживая, ограничение на неизменяемость переменных одновременно сделало и язык лучше, и техническую реализацию замыканий проще.
Кое-что об этом можно почитать у Брайана Гетца в "State of the Lambda: Variable capture".

В чем отличие статического метода(@staticmethod) и метода класса (@classmethod). Python

Изучал PHP, немного имел дело с С++, начал учить питон и не могу понять одной вещи. Зачем нужен метод класса, если есть статический метод? В чем отличия между ними?


Ответ

@staticmethod определяет обычную функцию в пространстве имён класса. Может быть полезно для вспомогательных _* функций, чтобы не мусорить пространство имён модуля.
@classmethod создаёт метод класса. Объект класса явно передаётся через первый параметр как это с параметром self происходит для обычных методов. Также как и для self, переданный cls может отличаться от класса, в котором определён класс-метод (может быть потомок). Часто используется для создания альтернативных конструкторов (ArgumentParser.from_params()):
>>> from collections import OrderedDict >>> OrderedDict.fromkeys("abc") OrderedDict([('a', None), ('b', None), ('c', None)]) >>> import datetime as DT >>> DT.datetime.now() datetime.datetime(2018, 2, 3, 18, 52, 8, 43534)
Исключение __new__ метод, который является статическим (хотя @staticmethod не используется), а не класс-методом. Why isn't __new__ in Python new-style classes a class method?. Есть __init_subclass__, который является класс-методом, но не объявляется как таковой.
@classmethod участвуют в нескольких встроенных протоколах Питона, к примеру, issubclass(klass, Divisible) вызывает Divisible.__subclasshook__.

WPF При изменении разрешения экрана возникает искажение в виде ступеньки

Сделал форму WPF. Все выровнял. Однако при просмотре программы на мониторе с более высоким разрешением возникают искажения в виде "ступеньки". Как от них избавиться?

Код


Ответ

Такой эффект дает установка свойства Padding, уберите его
Свойство Padding увеличивает эффективный размер дочернего элемента на заданное значение
В данном случае вам просто подойдет установка нужного Margin, например Margin="5,5,0,5"
В более сложных случаях задайте контент чекбоксу явно:
Зоны оповещения

Firebase Notifications

Я сделал меню, в котором пользователи выбирают какие уведомления они хотят получать.
Уведомления я всегда отправляю с опрделённым ключом: WEB-получать уведомления от сайта, SHOP-от магазина.
С помощью SharedPreference сохраняю значения меню: какие уведомления пользователь хочет получать.
Собственно в FirebaseMessagingService что-то получилось, но он отвечает за получение уведомления на переднем плане, а вот как отследить эти уведомления, когда приложение выключено не знаю.
Есть ли какой способ?
public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService implements Constants{
SharedPreferences sp; @Override public void onMessageReceived(RemoteMessage remoteMessage) { sp = getSharedPreferences(CHECK_SETTINGS, Context.MODE_PRIVATE);
boolean webSite = sp.getBoolean(notifWebSite, true); boolean shops = sp.getBoolean(notifShops, true); if(webSite && shops){ if(remoteMessage.getData().equals("WEB")){ sendNotification(remoteMessage.getNotification().getBody(),remoteMessage.getData().get("WEB")); } if (remoteMessage.getData().equals("SHOP")){ sendNotification(remoteMessage.getNotification().getBody(),remoteMessage.getData().get("SHOP")); } } else if(!webSite && !shops){
} else if(!webSite && shops){ if(remoteMessage.getData().equals("SHOP")){ sendNotification(remoteMessage.getNotification().getBody(),remoteMessage.getData().get("WEB")); } } else if(webSite && !shops){ if(remoteMessage.getData().equals("WEB")){ sendNotification(remoteMessage.getNotification().getBody(),remoteMessage.getData().get("WEB")); } }

/*if(remoteMessage.getData().get("URL")==null){ sendNotification(remoteMessage.getNotification().getBody(),URL_HODITE_COM); } else{ sendNotification(remoteMessage.getNotification().getBody(),remoteMessage.getData().get("URL")); }*/
}
private void sendNotification(String body,String url) { Intent intent=new Intent(this,WebActivity.class); intent.putExtra(KEY_INTENT,url);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_CANCEL_CURRENT);
Notification.Builder builder = new Notification.Builder(this); // оставим только самое необходимое builder.setContentIntent(pendingIntent) .setWhen(System.currentTimeMillis()) //Время уведомления .setSmallIcon(R.mipmap.ico) .setContentTitle("Hodite") .setContentText(body); // Текст уведомления
Notification notification = builder.build(); notification.defaults = Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE; // ставим флаг, чтобы уведомление пропало после нажатия notification.flags |= Notification.FLAG_AUTO_CANCEL; NotificationManager nm=(NotificationManager)getSystemService(NOTIFICATION_SERVICE); nm.notify((body+url).hashCode(),notification); }
}


Ответ

Спасибо @eugeneek, помогли ТЕМЫ
FirebaseMessaging.getInstance().subscribeToTopic("НАЗВАНИЕ ТЕМЫ") // подписать FirebaseMessaging.getInstance().unsubscribeFromTopic("НАЗВАНИЕ ТЕМЫ") // отписать

Как упростить алгоритм определения сектора по углу и расстоянию?

Использую вот этот "виртуальный джойстик" для android приложения. Необходимо перевести данные от джойстика (он может дать: угол 0-359 и расстояние 0-99) в однозначное направление(право, право-вверх, вперед, лево-вверх, лево итд).
Например, разделить круг на равные части и каждой из пар присвоить направление. Что-то из серии:
string temp(double value){ string str = ""; if (value <= 1 || value >= 15) str = "право"; else if (value <= 2) str = "право вверх"; else if (value <= 4) str = "прямо"; else if (value <= 6) str = "лево вверх"; else if (value <= 8) str = "лево"; else if (value <= 10) str = "лево назад"; else if (value <= 12) str = "назад"; else if (value <= 14) str = "назад право"; return str; }
int main(){ int value = 240; // от 0 до 359 cout << temp(value / 22.5); }
UPD: Получается так. Еще бы без повторов.. Но так тоже неплохое упрощение.
string str[] = {"R", "U R", "U R", "F", "F", "U L", "U L", "L", "L", "D L", "D L", "D", "D", "D R", "D R", "R"};
int main(){ int value = 240; // от 0 до 359 cout << str[(int)(value / 22.5)]; }
UPD1: Или я не правильно понимаю идею комментария или правда не работает
string str[] = {"R", "U R","F", "U L", "L", "D L", "D", "D R"};
string str_dbl[] = {"R", "U R", "U R", "F", "F", "U L", "U L", "L", "L", "D L", "D L", "D", "D", "D R", "D R", "R"};
int main(){ int value; for (int i = 0; i < 5; i++){ value = rand() % 358 + 1; cout << i << '\t' << value << '\t'; cout << str_dbl[(int)(value / 22.5)] << "\t:\t"; cout << str[(int)((value - 22.5) / 45)] << endl; } }
// 0 172 L : U L // 1 263 D : D L // 2 308 D R : D // 3 212 D L : L // 4 92 F : U R


Ответ

Спасибо Serodv и Kromster. Конечный вариант:
string str[] = {"R", "U R","F", "U L", "L", "D L", "D", "D R"};
int main(){ int value = 240; // от 0 до 359 cout << str[(( (int)(value + 22.5) ) % 360) / 45]; }

Символ “рубль” для всех устройств

Использую такой знак ₽ на сайте, но на старых мобильных устройствах он отображается так □. Подскажите, пожалуйста, как лучше всего его оптимизировать под разные мобильные устройства?


Ответ

p{ font-family: 'PT Sans', serif; font-size: 25px; }


Если выбрать файл в input type file, затем его переименовать или удалить, то как проверить что файла нет?

Берем input type file Выбираем в нем файл (допустим, хотим отправить его на сервер аяксом) Переименуем выбранный файл (или удалим) Жмем кнопку "отправить", которая берет input-type-file и пытается отправить ранее выбранный, но уже не существующий файл. Аякс виснет.
Ума не приложу, как сделать проверку такого варианта развития событий ))
Проверки вроде привычных на размер или имя файла (ниже) НЕ ПОДХОДЯТ:
var fileVal = $file.val(), fileRaw = $file[0].files[0];
if ( fileVal ) {....} if( typeof fileRaw !== 'undefined' && fileRaw.size > 0 ) {....}
Всегда будет TRUE, input-type-file не видит что файла нет, вот вывод в консоль


Сделал вариант решения с использованием API FileReader - идея в том, чтобы попытаться считать файл перед отправкой при помощи этого API, и увидеть, есть ли на момент отправки файл или нет. Есть ответ от ниже Other - он классный, с промисами, плюс в том - что файл не считывается полностью. Я закину сюда свой код, к которому пришел, может кому-то пригодится тоже - без промисов, если файл считывается без ошибок полностью, то только тогда принимается решение - что он есть и можно отправлять его аяксом (файлики априори будут мелкие).
var $file = $("#" + inpFileId), fileVal = $file.val(), fileRaw = $file[0].files[0], formData, reader;
// Простая проверка что файл выбран, поле не пустое: if ( fileVal && typeof fileRaw !== "undefined" && fileRaw.size > 0 ) {
// Проверка, что выбраный файл не был удален после выбора с проверкой поддержки: if ( typeof window.FileReader === "function") { reader = new FileReader(); reader.readAsDataURL(fileRaw);
// Когда файл считался целиком без проблем: reader.onloadend = function () { //console.log("reader.result:", reader.result );
if ( !!reader.result ) { // Строка или NULL, если файла больше нет uploadSampleFileAjax(); } else { $B.alertCreate({ alertTitle: "Ошибка, файл не существует", alertText: "Выбранный файл не удается найти.
Возможно он был удален или переименован.
Выберите файл и повторите отправку." }); } }; } else { uploadSampleFileAjax(); } } else { $B.alertCreate({ alertTitle: "Ошибка, файл не выбран", alertText: "Выберите файл и повторите отправку." }); }


Ответ

Кэширование есть, где-то видел баг на гитхабе по этой теме (в библиотеке какой-то).
Решение, которое первое пришло в голову - определяем что это лиса и попытаться прочитать файл через FileReader
let iFile = document.querySelector('[type="file"]'); let iBtn = document.querySelector('[type="button"]'); let dResult = document.querySelector('div'); let isFF = !!~navigator.userAgent.toLowerCase().indexOf('firefox'); // Флаг лисы; у меня не применяется, ибо демо, но... iBtn.addEventListener('click', function (e) { let files = iFile.files; let result = {}; Promise.all([...files].map((file, i) => { return new Promise(function(ok, fail){ let r = new FileReader(); // Разумеется, читать весь файл не нужно, для этого читаем только первую часть - это уже значит что файл есть, иначе этот обработчик не выполняется; поэтому логгируем существование и прерываем чтение (кому хочется проверять все 10Gb файла впустую?) r.onprogress = function (e) { result[i] = true; r.abort(); ok(); } r.onerror = function (e) { // Файла нет - разговор короткий result[i] = false; ok(); // Не fail(), иначе Promise.all немедленно перейдёт к своему .then не дожидаясь остальных } r.readAsBinaryString(file); }); })).then(e => { dResult.innerHTML = Object.entries(result).map(([i, res]) => `Element #${i} "${files[i].name}": ${res ? 'Available' : 'Something wrong'}`).join('
'); }); }); |


Проверка демо: Выбираем файл (-ы), отсылаем, видим что всё есть, после чего меняем, допустим, имя выбранного файла в файловой системе и пробуем послать ещё раз, не перезагружая и не перевыбирая.