Страницы

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

суббота, 30 ноября 2019 г.

Как происходит запись в середину файла?

#javascript #java #cpp #python #xml


Верно ли, что нельзя дописать что-то не в конец файла, не перезаписав файл?
Как поступают, например, стандартные парсилки/редакторы XML (в любом языке программирования)
если я вставляю значение поля в середине файла? Перезаписывают весь файл или только
то, что после редактируемого места?
    


Ответы

Ответ 1



И верно и неверно. В общем случае верно. Файл с точки зрения приложения представляет собой непрерывный кусок дисковой памяти. Запись в середину неминуемо приводит к затиранию существующих данных. Обычно программы либо перезаписывают файл целиком, либо начиная с точки изменения и до конца. Однако, существует путь вставки данных в середину файла без перезаписи. Как известно, данные на диске хранятся в виде блоков и файл представляет собой совокупность блоков, причём где-то может храниться карта блоков для файла, либо же каждый блок содержит ссылку на последующий. Можно создать новый блок, записать в него данные и поменять карту, вставив ссылку на блок. Но у такого подхода есть и недостаток. Либо мы должны вставлять данные блоками определённого размера, либо использовать только часть блока и мириться с потерей части дисковой ёмкости.

Ответ 2



Почти любые стандартные и нестандартные средства работают не напрямую с диском или файловой системой тома, а обращаются с запросами на действие к файловому реквестору. Тот соответственно транслирует файловые операции в дисковые (и обратно), инициируя чтение или запись физических секторов. Если файл полностью считывается в память процесса и потом полностью записывается обратно, то дисковая подсистема тупо выполнит всё запрошенное, и запишет старую информацию поверх новой, несмотря на то, что в ней ни бита не поменялось (это я про "голову файла" до точки внесения изменений) - так дешевле, чем сравнивать то, что было, с тем, что стало. Но если процесс считывает некоторое "окно", в которое вносятся изменения, и затем это новое состояние сбрасывается на диск, то перезаписыванию подвергнутся только секторы, которые относятся в этому окну. И именно приложение, чтобы не поломать файл, вынуждено будет организовывать чтение следующих "окон", смещение в них информации и запись их нового состояния - и так до конца файла. Процесс, как понимаете, дорогой, а память нынче дёшева - потому подавляющее большинство приложений не занимаются подобным "крохоборством" и для изменения читают, а после изменения пишут, весь файл целиком. А чтобы при этом не нарваться на проблемы, предпочтительным является не перезапись поверх, а запись нового файла с переименованием и/или удалением старого. Поиск свободного пространства под файл, по возможности непрерывного - тоже недорогая операция, так что новый файл скорее всего окажется физически совсем не в том месте, где лежал файл до корректировки. А заодно такая методика является и пассивным методом борьбы с фрагментацией.

Ответ 3



Стандартные парсилки/редакторы XML считывают файл полностью в оперативную память, а потом также полностью записывают его на диск. Для компьютера это гораздо проще и быстрее. Если работать напрямую с жестким диском, то это будет ОЧЕНЬ медленно.

Ответ 4



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

Различие define и const

#cpp


В чем различие между этими определениями переменной:

#define N 100


и

const int n = 100;


Что из этих двух является более предпочтительным и в чем достоинства каждого?
    


Ответы

Ответ 1



Это очень дискуссионный вопрос у всех. Но в целом, в с++ лучше использовать const везде, где это возможно. Плюсы: на них действуют области видимости. компилятор относится к ним как к обычными переменным, доступным только на чтение. у них есть тип. У define это все - недостатки. Они действуют от места определения и до ... конца. Хотя, где этот конец будет - ещё нужно хорошо подумать. Они могут переопределять друг дружку и иногда искать, где именно было в очередной раз переопределено - ещё то удовольствие. Компилятор часто выдает очень "загадочные ошибки". Классический пример с макросом min/max. Файл windows.h определяет их. И может быть очень весело. Детали - NOMINMAX. Ещё у define есть большой недостаток - это то, что некоторые думают, что там есть скобки. А их нет. #define add(a,b) a+b if (add(x,y) * add(x,y)) {} кажется, что будет посчитано (x+y) в квадрате. А на самом деле - .... на самом деле x+y + x*y. Но мы немного отвлеклись. Такого сделать обычным const уже нельзя. Но легко сделать inline функциями. И их результат будет предсказуемый. Поэтому: eсли нужно просто определить константу - используйте const (даже само слово намекает). eсли нужно определить макрос - используйте функции (если хочется интересней - используйте inline, но компиляторы сейчас достаточно умные). если нужно усложнить код и добавить синтаскического сахара - тогда самое время использовать define. UPD А ещё хорошо найти книгу Эффективное использование C++. 55 верных советов улучшить структуру и код ваших программ - Скотт Майерс и почитать второй совет (в любом случае - там всю книгу можно читать и читать).

Ответ 2



В чистом языке С++, если у вас есть выбор между этими двумя вариантами, то ясно, что предпочтительным является именно вариант с const. Однако вопрос задан несколько ограничивающе, ибо почему-то предложены к рассмотрению только эти два способа. В рамках современного языка С++ у вас есть третий способ constexpr int N = 100; который фактически является унифицированным вариантом, специально предназначенным для создания констант времени компиляции, как именованных, так и вычислимых. В данном случае, если не лезть в иррелевантные детали, разницы между const и constexpr не будет, однако существует мнение, что использование constexpr предпочтительнее именно в вашем варианте - когда вас интересует именно константа времени компиляции, но совершенно не интересует ее адресная идентичность, т.е. вы никогда не будете рассматривать адрес этой константы. Вариант с const в рамках этого мнения условно "отодвигается" на роли, которые этот квалификатор играет в языке С, т.е. создание "неизменяемых переменных" в памяти. Но, опять же, такое различие существует лишь на уровне соглашений, т.е. разницы между const int и constexpr int в тривиальных применениях вы не заметите. Однако и у #define тоже есть свои преимущества. Ограничение на использование только либо const, либо #define варианта возникнет тогда, когда вы будете создавать кросс-компилируемый С и С++ код (например, заголовочные файлы библиотек, предназначенных для использования в обоих языках). Тогда вам в большинстве случаев придется пользоваться именно #define, ибо в языке С квалификатор const не создает констант. В языке С для создания именованных констант времени компиляции у вас есть только enum и #define. В качестве еще одного преимущества варианта с #define можно назвать очевидную (но часто забываемую) возможность использовать #define-константы в условных выражениях препроцессора, т.е. в #if. Ни const, ни constexpr вам там не помогут. Шаблонное метапрограммирование и появление в языке С++ таких C++17 свойств, как "static if" (if constexpr) тоже частично переносит такие применения из области препроцессора в область самого языка и, соответственно, снижает ценность #define-констант в С++, но тем не менее эта деталь тоже заслуживает упоминания.

Плавная прокрутка к якорю без jQuery

#javascript #jquery


Есть отличный скрипт плавной прокрутки на jQuery.

var $page = $('html, body');
$('a[href*="#"]').click(function() {
    $page.animate({
        scrollTop: $($.attr(this, 'href')).offset().top
    }, 400);
    return false;
});


Как можно отобразить на чистом JS?
    


Ответы

Ответ 1



// собираем все якоря; устанавливаем время анимации и количество кадров const anchors = [].slice.call(document.querySelectorAll('a[href*="#"]')), animationTime = 300, framesCount = 20; anchors.forEach(function(item) { // каждому якорю присваиваем обработчик события item.addEventListener('click', function(e) { // убираем стандартное поведение e.preventDefault(); // для каждого якоря берем соответствующий ему элемент и определяем его координату Y let coordY = document.querySelector(item.getAttribute('href')).getBoundingClientRect().top + window.pageYOffset; // запускаем интервал, в котором let scroller = setInterval(function() { // считаем на сколько скроллить за 1 такт let scrollBy = coordY / framesCount; // если к-во пикселей для скролла за 1 такт больше расстояния до элемента // и дно страницы не достигнуто if(scrollBy > window.pageYOffset - coordY && window.innerHeight + window.pageYOffset < document.body.offsetHeight) { // то скроллим на к-во пикселей, которое соответствует одному такту window.scrollBy(0, scrollBy); } else { // иначе добираемся до элемента и выходим из интервала window.scrollTo(0, coordY); clearInterval(scroller); } // время интервала равняется частному от времени анимации и к-ва кадров }, animationTime / framesCount); }); }); h2 { margin-bottom: 100vh; }

heading 1

heading 2

heading 3

heading 4



Ответ 2



Кросплатформенный вариант: currentYPosition - определяет текущее положение скрола elmYPosition - определяет положение элемента smoothScroll - собственно сама функция. function currentYPosition() { // Firefox, Chrome, Opera, Safari if (self.pageYOffset) return self.pageYOffset; // Internet Explorer 6 - standards mode if (document.documentElement && document.documentElement.scrollTop) return document.documentElement.scrollTop; // Internet Explorer 6, 7 and 8 if (document.body.scrollTop) return document.body.scrollTop; return 0; } function elmYPosition(eID) { var elm = document.getElementById(eID); var y = elm.offsetTop; var node = elm; while (node.offsetParent && node.offsetParent != document.body) { node = node.offsetParent; y += node.offsetTop; } return y; } function smoothScroll(eID) { var startY = currentYPosition(); var stopY = elmYPosition(eID); var distance = stopY > startY ? stopY - startY : startY - stopY; if (distance < 100) { scrollTo(0, stopY); return; } var speed = Math.round(distance / 100); if (speed >= 20) speed = 20; var step = Math.round(distance / 25); var leapY = stopY > startY ? startY + step : startY - step; var timer = 0; if (stopY > startY) { for ( var i=startY; i stopY) leapY = stopY; timer++; } return; } for ( var i=startY; i>stopY; i-=step ) { setTimeout("window.scrollTo(0, "+leapY+")", timer * speed); leapY -= step; if (leapY < stopY) leapY = stopY; timer++; } } Ответ взят отсюда Упрощенная версия, когда надо просто проскролить к определенной позиции function scrollTo(element, to, duration) { if (duration <= 0) return; var difference = to - element.scrollTop; var perTick = difference / duration * 10; setTimeout(function() { element.scrollTop = element.scrollTop + perTick; if (element.scrollTop === to) return; scrollTo(element, to, duration - 10); }, 10); } Ответ взят отсюда

Ответ 3



Есть еще стандартный способ плавной прокрутки Element.scrollIntoView(). const anchors = document.querySelectorAll('a[href*="#"]') for (let anchor of anchors) { anchor.addEventListener('click', function (e) { e.preventDefault() const blockID = anchor.getAttribute('href').substr(1) document.getElementById(blockID).scrollIntoView({ behavior: 'smooth', block: 'start' }) }) } h2 { margin-bottom: 100vh; }

heading 1

heading 2

heading 3

heading 4



Ответ 4



var nodeObj = document.querySelector('.button-node'); nodeObj.scrollIntoView({ behavior: "smooth", block: "start" });

Ответ 5



const anim = (sel, duration) => { let to = document.querySelector(sel).getBoundingClientRect().top, temp; return (sel) => { cancelAnimationFrame(temp); var start = performance.now(); var from = window.pageYOffset || document.documentElement.scrollTop; requestAnimationFrame(function step(timestamp) { var progress = (timestamp - start) / duration; 1 <= progress && (progress = 1); window.scrollTo(0, from + to * progress | 0); 1 > progress && (temp = requestAnimationFrame(step)) }); } }

Окружность с градиентом

#css


Как создать такую окружность, как на рисунке?
На рисунке есть ещё задний фон. Можно нарисовать круг с градиентом, и с помощью :before
закрыть внутреннюю часть по периметру, но как тогда показать задний фон через :before?

    


Ответы

Ответ 1



Используя пример ответа @Максим Ленский, сгенерированный генератором, можно сделать читабельный и понятный вариант:

Ответ 2



Как вариант можно использовать mask-image: .box { height: 200px; width: 200px; border-radius: 50%; background-image: linear-gradient(to right, #0B73D3, #37DAEF, #86E98E); -webkit-mask-image: radial-gradient(circle at center, transparent 67%, white 68%); mask-image: radial-gradient(circle at center, transparent 67%, white 68%); } body { background-image: url(https://www.w3schools.com/css/img_fjords.jpg); background-size: cover; }
Но, к сожалению, поддержка браузерами оставляет желать лучшего.

Ответ 3



Как обычно такими вещами занимается svg , рекомендую почитать о этой прелести, есть отличный сайт администратор которого присусвует на ruSO

Ответ 4



Можно сделать это чистым CSS без SVG, но тогда придётся явно задавать цвет фона для картинки: div { width: 100px; height: 100px; border-radius: 50%; position: relative; /* Явно задаём белый цвет */ background-color: white; } div:after { content: ""; background: linear-gradient(#0070d8, #2cdbf1, #83eb8a); position: absolute; left: -5px; right: -5px; top: -5px; bottom: -5px; border-radius: 50%; z-index: -1; }


Ответ 5



Можно использовать ответ @ArtemGorlachev и немного его модифицировать для того, чтобы он растягивался под произвольный блок: Можете сделать SVG либо отдельным файлом (предпочтительный способ), либо как часть значения background (представленный ниже код будет работать только в webkit браузерах): div { width: 250px; height: 250px; background: url('data:image/svg+xml;utf8,'); }
Для того, чтобы код работал в MS Edge и Firefox символы внутри url должны быть escaped после кодировки utf8, для них результат с учётом обходного пути (замена двойных кавычек на одинарные, # на %23 и % на %25) будет выглядет так: div { width: 250px; height: 250px; background: url("data:image/svg+xml;utf8,"); background-size: 100% 100%; /* Фикс для Firefox, чтобы изображение корректно растягивалось */ }


Как можно сделать фон штриховкой на CSS/HTML?

#html #css #svg


Можно ли сделать такой штрих, если да, то как


    


Ответы

Ответ 1



Можно, используя градиент .dash { height: 30px; background: repeating-linear-gradient(-60deg, #555 0, #555 1px, transparent 1px, transparent 5px); }


Ответ 2



Рисуете свою полоску в SVG, генерируете data:uri, вставляете на повторяющийся фон. Никаких артефактов, векторный фон готов: .dash { height: 30px; background-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iNHB4IiBoZWlnaHQ9IjRweCIgdmlld0JveD0iMCAwIDQgNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNCA0IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxsaW5lIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0FCQUJBQiIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgeDE9IjAiIHkxPSIwIiB4Mj0iNCIgeTI9IjQiLz4NCjwvc3ZnPg0K"); background-repeat:repeat; background-position:0 0; background-size:4px 4px; }


Почему композиция не нарушает Принцип единственной обязанности?

#java #ооп #архитектура #solid


Я решаю задачу о нахождении лидера (leader election).

Задача чисто алгоритмическая. Есть 2 формы задачи. У меня есть абстрактный класс
для представления данных и абстрактный класс Solver. Для каждой формы задачи эти классы
я расширяю в соответствии с нуждами этой формы задачи. То есть, решая задачу для первой
формы мне нужно написать так в клиентском коде:

MyData data = new MyDataForm1();
MySolver solver = new MySolverForm1();


Это можно было бы скомпозировать так: 

public abstract class AbstractTask{
// some code
}

public class Task1{
    MyData data = new MyDataForm1();
    MySolver solver = new MySolverForm1();

    //some code
}

public class Task2{
    MyData data = new MyDataForm2();
    MySolver solver = new MySolverForm2();

    //some code
}


Тогда в клиентском коде для первой формы, например, можно будет делать так:

AbstractTask task = new Task1();


Это же композиция? Но тут такая проблема: Получается что у классов Task 2 обязанности.
Они и данные хранят и задачу решают (да, делегируя это экземплярам MyData и MySolver,
но все же). И так ведь получается всегда при композиции. Мы включаем экземпляры нескольких
классов в один класс в качестве полей. У включенных классов были какие то обязанности.
Значит у включающего класса будет несколько обязанностей. 

Я понимаю тут так, что это более высокий уровень абстракции. Например, на уровне
отдельных деталей машины можно рассматривать мотор, магнитолу и руль как отдельные
классы, которые выполняют какую-то обязанности. Но потом мы начинаем рассматривать
машину. Она состоит из этих частей. Но теперь мы рассматриваем на более высоком уровне
абстракции (нам просто надо ездить), поэтому можно сказать что класс Машина все-таки
выполняют всего одну задачу (езда).
    


Ответы

Ответ 1



У включенных классов были какие то обязанности. Значит у включающего класса будет несколько обязанностей. Не-а. Только его собственная. Во всяком случае, так будет выглядеть со стороны. Действует инкапсуляция. Обязанность вложенного класса является уже внутренней деталью реализации и эта деталь не должна быть видна тому, кто этим классом пользуется. Из этого же вытекает правило, по которому можно понять, разумно ли применение композиции в каждом конкретном случае: полностью ли "внутренняя" обязанность "погружена" во "внешнюю" (без существенного дополнительного "натягивания").

Ответ 2



Я понимаю тут так, что это более высокий уровень абстракции. Например, на уровне отдельных деталей машины можно рассматривать мотор, магнитолу и руль как отдельные классы, которые выполняют какую-то обязанности. Но потом мы начинаем рассматривать машину. Она состоит из этих частей. Но теперь мы рассматриваем на более высоком уровне абстракции (нам просто надо ездить), поэтому можно сказать что класс Машина все-таки выполняют всего одну задачу (езда). Вы здесь сами верно ответили на вопрос. Дело в том, что у нового класса будет обязанность, отличная от обязанностей объектов, которые он в себя включает. Если говорить конкретно, то обязанность нового класса заключается в том, чтобы соединять вместе данные и алгоритм решения. В последующем может обнаружиться, что такой объект будет весьма полезен, если понадобятся промежуточные шаги. Эта обязанность звучит несколько абстрактно, но тем не менее имеет право на жизнь. И, как можно заметить, она более высокоуровневая, чем обязанности включаемых классов. Об этом я вскользь упоминал в предыдущем ответе на ваш вопрос об SRP. Можно вывести правило: чем выше уровень "модуля", тем более высокоуровневыми являются его обязанности. Возьмем тип float. Его обязанность -- реализовывать работу с числами с плавающей точкой (представление, плюс базовые математические операции). Используя тип float (т.е. используя композицию), вы пишете функцию расчета гипотенузы по заданным катетам. Обязанность этой функции -- считать гипотенузу. Дальше вы включаете эту функцию в пакет PlanimetryAlgorithms. Обязанность этого пакета -- предоставлять различные алгоритмы, связанных с планиметрий (т.е. геометрией фигур на плоскости). Пакет PlanimetryAlgorithms может, в свою очередь, входить в библиотеку Geometry, куда также будут входить пакеты для других разделов геометрии. Обязанность этой библиотеки -- предоставлять различные функции, касающиеся геометрии вообще. Как видно, с повышением гранулярности обязанность становится более высокоуровневой, но при этом ее единственность соблюдается. Функция расчета гипотенузы не выдает нам заодно значения всех углов в этом треугольнике, пакет PlanimetryAlgorithms не содержит методов для рисования фигур, а библиотека Geometry не начинает вдруг заниматься физикой.

Ответ 3



Верно, все дело именно в уровне абстракции. Объекты, выполняющие единственную функцию высшего уровня, для выполнения своих обязанностей пользуются функциями включенных в себя объектов. Обязанность электродвигателя как отдельного объекта - превращать электрическую энергию в механическую (вращение вала). Объект, например, наждак выполняет свою функцию - предоставляет инструмент для заточки ножей. А троллейбус - другую функцию.И тот, и другой включают в себя электродвигатель в том или другом виде (наследники абстрактного электродвигателя), и используют его для выполнения своей функции. Также у нас бывают объекты типа "швейцарский нож": объект вроде один, но функций как бы много. С этим нужно быть осторожным при проектировании, чтобы не включить "посторонних" функций и не нарушить принцип. Тоже хороший пример композиции.

Регулярное выражение для пароля от 6 символов с использованием цифр, спец. символов, латиницы, наличием строчных и прописных символов

#javascript #регулярные_выражения


Нужно настроить регулярное выражение для "Сложного пароля": от 6 символов с использованием
цифр, спец. символов, латиницы, наличием строчных и прописных символов.
Если введенные символы не соответствуют данному выражению то return false;
    


Ответы

Ответ 1



Необходимо использовать позитивный просмотр вперёд. Он позволит обеспечить все перечисленные вами условия. Вот так выглядит выражение целиком: /(?=.*[0-9])(?=.*[!@#$%^&*])(?=.*[a-z])(?=.*[A-Z])[0-9a-zA-Z!@#$%^&*]{6,}/g Вот пример на regex101. Можете попробовать написать свои пароли и проверить работу регулярного выражения на соответствие своим требованиям. Пояснение: (?=.*[0-9]) - строка содержит хотя бы одно число; (?=.*[!@#$%^&*]) - строка содержит хотя бы один спецсимвол; (?=.*[a-z]) - строка содержит хотя бы одну латинскую букву в нижнем регистре; (?=.*[A-Z]) - строка содержит хотя бы одну латинскую букву в верхнем регистре; [0-9a-zA-Z!@#$%^&*]{6,} - строка состоит не менее, чем из 6 вышеупомянутых символов. Основано на ответе на вопрос: "Javascript regular expression password validation having special characters" Обновление Важно понять, что для того, чтобы проверить обязательное наличие определённых символов в строке, достаточно использовать такой шаблон: (?=.*[%s]), где вместо %s надо указывать требуемый набор символов. Шаблон должен быть в самом начале регулярного выражения и присутствовать столько раз, сколько уникальных правил для проверки строки вы хотите использовать. После отрезка с повторениями этого шаблона необходимо использовать обобщённый набор из всех разрешённых символов. Нам надо склеить «куски» в один общий набор разрешённых символов. Затем к нему надо будет применить ограничение по количеству символов, соответствующее выбранной длине строки. Чтобы в коде легче читалось такое регулярное выражение, и его было легче проверить, в случае опечатки можно использовать такую функцию для генерации итогового выражения: function makePasswordRegExp(patterns, min, max) { var min = min || ''; // Если минимальное число символов не указано, берём пустую строку var max = max || ''; // Если максимальное число символов не указано, берём пустую строку var regex_string = ''; var rules = []; var range = "{" + min + "," + max + "}"; // Разрешённый диапазон для длины строки for (rule in patterns) { // Обрабатываем входящий массив из ВСЕХ правил для строки if (patterns.hasOwnProperty(rule)) { rules.push(patterns[rule]); // Запоминаем правила // Формируем последовательность из шаблонов `(?=.*[%s])` // Она проверит обязательное присутствие всех символов из входящего набора regex_string += "(?=.*[" + patterns[rule] + "])"; } } // Добавляем в хвост набор из ВСЕХ разрешённых символов и разрешённую длину строки regex_string += "[" + rules.join('') + "]" + range; // Собираем всё в одно регулярное выражение return new RegExp(regex_string, 'g'); } Использование: // Набор правил // Имена ключей в этом объекте могут быть любыми // Они для лучшего понимания частей итогового регулярного выражения var patterns = { 'numeric': '0-9', 'special': '!@#$%^&*', 'latin_lower': 'a-z', 'latin_upper': 'A-Z' }; // В вашем случае есть ограничение только по минимальной длине от 6 символов var min = 6; // Передаём правила в функцию и смотрим итоговое выражение console.log(makePasswordRegExp(patterns, min)); // Вывод: /(?=.*[0-9])(?=.*[!@#$%^&*])(?=.*[a-z])(?=.*[A-Z])[0-9!@#$%^&*a-zA-Z]{6,}/g Безусловно, функцию можно улучшить, добавив в неё проверку большего из двух аргументов min и max и тому подобное. Она призвана только показать подход, который может упростить отладку таких сложных регулярных выражений.

Ответ 2



/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^\w\s]).{6,}/ Очень похоже, на ответ @VenZell, только лишено недостатка, что жестко ограничен набор допустимых символов. Неправильно требовать от людей "сложный" пароль и при этом ограничивать набор допустимых символов небольшим списком !@#$%^&*. Если производить брутфорс такого пароля, то нет ничего слаще - 56 букв, 10 цифр и 8 символов, итого 74 символа. Ну "очень сложно". Требуя от людей "сложный" пароль имейте ввиду, что они могут захотеть тогда ввести \0 © ♣ и кучу других символов, которые легко вводятся с помощью ALT+3333 как в случае символа трефы.

Доступ к памяти из кучи после её освобождения

#cpp #память


Почему не ловлю ошибку сегментирования в этом случае?

#include 

int main(int argc, char* argv[])
{
    int* ptr = new int(47);

    std::cout << ptr << " " << *ptr << std::endl;

    /* 
      Память выделенная в heap'e должна вернуться системе.
      Или она будет доступна программе до окончания области
      видимости указателя ?
    */
    delete ptr;

    *ptr = 74;

    std::cout << ptr << " " << *ptr << std::endl;
}

    


Ответы

Ответ 1



Разименовывание невалидного указателя приводит к неопределенному поведению - так на C++ писать нельзя. "Неопределенное поведение" вовсе не означает, что программа выпадет с ошибкой. В таком состоянии, программа может делать все что угодно, в том числе и работать так, как этого хочет программист. Допускать неопределенное поведение в программе запрещено.

Ответ 2



Как обычно работает менеджер памяти? Изначально есть большой кусок памяти, с которым можно работать, и который помечен, как свободный. Менеджер при запросе может, например, выделить кусок памяти и сохранить у себя в своем внутреннем списке пометку - вот эта память выделена, ее не трогать... (на самом деле это отдельная и очень серьезная тема, но для пояснения хватит и такого представления). Освобождается блок - менеждер делает соответствующую отметку у себя. Скорее всего, перед блоком будет находиться небольшой блок со служебной информацией - о занятости, размере занятого блока и т.п. Что происходит при освобождении с самой памятью? Ничего. Она просто помечается как свободная. Меняется только информация в служебных полях. Поэтому вы можете записать туда информацию. Или считать. Но даже если вы делаете это прямо после освобождения, без промежуточных действий - уже никто не может вам гарантировать корректности этого действия: вдруг тот же менеджер именно на это место запишет свою служебную информацию, так что вы просто ее испортите? Или другой поток успеет захватить эту память себе? Что именно произойдет, не знает никто, так что о таких ситуациях так и говорится - "неопределенное поведение". Словом, получить аварийное завершение программы в такой ситуации - это скорее везение. Невезение - не получить никакой ошибки, но запороть память, и потом, через 15 минут работы программы получить неприятности - например, совершенно неверное значение переменной, которое вы вроде бы сохраняли как совсем другое. И потом долго - очень долго искать ошибку, которая расположена совсем в другом месте программы, да еще и не воспроизводится стопроцентно при каждом запуске...

Ответ 3



Когда вы освобождаете блок памяти, а потом работаете с ним, может произойти что угодно. "Что угодно" включает: Блок памяти мог был помещён менеджером памяти в список свободных. Данные в самом блоке при этом обычно не затираются, поэтому при доступе к "удалённой" памяти она всё ещё будет содержать нужную вам информацию. Если вы используете отладочный режим или какой-нибудь защитник от ошибок, то менеджер памяти мог намеренно затереть данные в освобождённом блоке. В результате при чтении данных вы заведомо получите мусор. Освобождённый блок памяти или его часть могли быть отданы менеджером памяти какому-то другому куску вашего кода. Содержимое, соответственно, могло быть перезаписано этим другим куском кода. Данные вы прочитаете, но интерпретируете неверно, поэтому для вас это будет мусором. Менеджер памяти мог решить отдать ненужную страницу памяти вместе с вашим блоком операционной системе. При попытке доступа к "дырке" вы получите ошибку доступа, и ваш процесс рухнет. Нюанс в том, что операционная система работает с большими блоками памяти, а вашей программе хочется мелкие, поэтому менеджер памяти будет запрашивать большие куски у оси, а потом разбираться с раздачей мелких кусков в больших кусках самостоятельно. Когда вы освобождаете мелкий кусок, совсем не факт, что менеджер памяти будет освобождать большой кусок, который содержит ваш мелкий.

Глобальная фильтрация $_GET и $_POST данных в PHP

#php


Здравствуйте.

Как лучше всего глобально фильтровать $_GET и $_POST данные?

Например, определенный код глобальной фильтрации в шапку сайта. 

Буду благодарен за пример.
    


Ответы

Ответ 1



Если код дырявый, то ему и фильтрация не поможет от взлома :) Фильтруйте и валидируйте данные по мере необходимости. Вы напишите универсальное решение которое например будет обрезать html теги, а они Вам пригодятся в другом месте. Если хотите защититься от XSS делайте в HTML какие-то определенные разрешенные теги с помощью специальных библиотек. Используйте, библиотеки для фильтрации данных, которых куча на github. Например, Filterus

Ответ 2



Объясню почему лучше не фильтровать, был у меня проэкт с фиильтрацией глобально на самописной cms и там все запросы переписывались типо того foreach($_POST as $_key => $_value) { $_POST[$_key] = filter_var($_value,FILTER_SANITIZE_SPECIAL_CHARS); } foreach($_GET as $_key => $_value) { $_GET[$_key] = filter_var($_value,FILTER_SANITIZE_SPECIAL_CHARS); } а после этого туча кастылей и багов что бы когда нужно обойти это самое ограничение. Это как объяснение почему не стоит фильтровать так как вы хотите, и дополнение к ответу @Firerepo что нужно фильтровать только когда это необходимо а не всё и вся.

Ответ 3



При возникновении подобных вопросов их очень полезно переводить из непонятного мира программирования в понятные всем реалии обычной жизни. Тогда он будет звучать так: Как глобально защититься от всех болезней? Смешно, да? Еще смешнее будет ответ. Что у нас помогает от болезней? Мыть руки перед едой, носить шарфик от сквозняка, пить таблетки, делать зарядку... Ну ок, допустим познакомился ты на вечеринке с девушкой, слово за слово, пришли к тебе домой. Ты, как правильный пионер, делаешь все, чтобы глобально защититься - идешь мыть руки, надеваешь шарфик, делаешь зарядку... а через недельку начинают вдруг выделяться непонятные жидкости из понятного места. Помогла тебе твоя глобальная защита? Будешь и дальше искать универсальное средство? Теперь в довершение к всему перечисленному будешь ходить на обед в презервативе? Или все-таки будешь применять средства защиты адресно - перед едой мыть руки, перед прогулкой надевать шарфик... ну и так далее. Если все еще не дошло: Взлом бывает разный. И средства защиты от одного никак не помогают от другого. Если у тебя введенные пользователем данные выводятся в HTML, то их надо защищать от XSS. Но если это введенный админом код HTML, то такая защита его испортит. При этом от SQL инъекции она не поможет, но зато испортит данные в БД. При этом ни защита от XSS, ни от SQL инъекции не поможет, если ты инклюдишь файл, имя которого передается из браузера. А пароль для защиты надо хэшировать. но если захэшировать любые другие данные, то они станут бесполезными. Ты все еще хочешь свою глобальную защиту?

Ответ 4



мои пять копеек: вы задаете неправильный вопрос и получаете ответ на неправильный вопрос. 1) Да, не надо глобально, по всему сайту фильтровать POST/GET/REQUEST 2) Проблема в том, что ваша задача сформулирована как "фильтровать POST/GET/REQUEST", хотя на самом деле у вас вопрос "как правильно валидировать входящие данные" 3) При этом вопрос звучит так, будто вы используете POST/GET/REQUEST во всех подрядд функциях/методах (иначе зачем вам глобальная фильтрация) - это - абсолютно недопустимо для большого/сложного/важного сайта. Это приведет к хаосу в коде, к костылям, копипасте, и, в конечном итоге к тем же самым проблемам с безопасностью. Это абсолютно недопустимо с точки зрения культуры разработки 4) примерный ответ на вопрос как правильно валидировать данные - убедиться, что вы не используете POST/GET/REQUEST и не обращаетесь к ним ни в одной функции, ни в одном методе вашего сайта кроме контроллеров (входных точек) или, как в симфони - обработчиков запроса. Для одностраничников - кроме нескольких первых строк сайта. Вы должны - руками или с помощью функции-обработчика запроса создавать переменные из запроса. После этого - если вы не используете движок, а делаете самописный велосипед - сделать (стырить из опенсорса) обработчики типичных для вашего сайта случаев - только строчки, только цифры. без спецсимволов. Формат email, формат даты. и применять их для переменных, которые передаются вашим контроллерам. 5) правильного ответа на вопрос как "фильтровать глобально" быть не может, но ближе всего, если вы упретесь рогом, будет вариант: создать функцию, которая вызывается всегда на всех страницах сайта, анализирует путь, куда пришел запрос и вызывает соответствующие обработчики для нужного пути. Это кровавый велосипед ада, но раз вы действуете без фреймворков и не хотите делать абстракции форм с валидаторами - у вас хотя бы будет изолированное от всего остального кода место, где вы, согласно подходящим для пути правилам валидации фильтруете ввод. После этого вы апдейтите _GET/_POST/_REQUEST и больше никогда их не трогаете нигде, кроме как для вытаскивания переменных.

Как использовать спецификаторы формата?

#c_sharp


Недавно начал изучать C#, сейчас наткнулся на непонятную мини-тему. Перечитал 20
раз, то ли спать хочу, то ли настолько туп. Но не понимаю ничего. Мб кто сможет вкратце
объяснить, или скинуть статью на более понятном языке.

Цитата из учебника Шилдта:


  Но ведь основное назначение спецификаторов формата — управлять внешним видом
  выводимых данных. Чаще всего форматированию подлежат следующие типы данных:
  с плавающей точкой и десятичный. Самый простой способ указать формат данных —
  описать шаблон, который будет использоваться в методе WriteLine(). Для этого указывается
образец требуемого формата с помощью символов #, обозначающих разряды чисел. Кроме
того, можно указать десятичную точку и запятые, разделяющие цифры.
  
  Ниже приведен пример более подходящего вывода результата деления 10 на 3.

Console.WriteLine("Деление 10/3 дает: (0:#.##)", 10.0/3.0);

  
  Выполнение этого оператора приводит к следующему результату.

Деление 10/3 дает: 3.33

  
  В данном примере шаблон #.## указывает методу WriteLine() отобразить два
  десятичных разряда в дробной части числа. Следует, однако, иметь в виду, что метод
WriteLine() может отобразить столько цифр слева от десятичной точки, сколько потребуется
для правильной интерпретации выводимого значения.
  
  Рассмотрим еще один пример. Оператор

Console.WriteLine("{0:###,###.##}", 123456.56);

  
  дает следующий результат.

123,456.56"



Так и не понял, как, когда, почему и зачем используется "#"
    


Ответы

Ответ 1



# - это необязательная цифра. Т. е. если там все нули, то ничего не выводится. 0 - это обязательная цифра. 0.## - выводить не более двух знаков после запятой. 0.00 - выводить ровно 2 знака после запятой. #,##0.0 - выводить с разделителем разрядов каждые 3 цифры и один знак после запятой. 0,000.# - выводить минимум 4 цифры в целой части с разделителем разрядов и не более одного знака после запятой. Пример http://ideone.com/eq1IpM using System; public class Test { public static void Main() { var formats = new string [] {"#", "0", "0.##", "0.00", "#,##0.0", "0,000.#"}; var numbers = new double [] {0.127, 0.17, 0.7, 1, 1.128, 1.16, 1.8, 10, 10.88, 1000000, 1000000.8}; foreach (var f in formats) foreach (var x in numbers) Console.WriteLine("'{0}' as '{1}' is '{0:" + f + "}'", x, f); } } Или в виде таблицы http://ideone.com/31Dkz8 body { margin: 0; } table, th, td { font-family: monospace; text-align: right; border: 1px solid; border-collapse: collapse; padding: .125em .25em; margin: auto; }
#00.##0.00#,##0.00,000.#
0.12700.130.130.10,000.1
0.1700.170.170.20,000.2
0.7110.70.700.70,000.7
11111.001.00,001
1.128111.131.131.10,001.1
1.16111.161.161.20,001.2
1.8221.81.801.80,001.8
1010101010.0010.00,010
10.88111110.8810.8810.90,010.9
10000001000000100000010000001000000.001,000,000.01,000,000
1000000.8100000110000011000000.81000000.801,000,000.81,000,000.8


Ответ 2



Описатель формата "#" - заместитель цифры. Описание Заменяет знак "#" соответствующей цифрой, если такая имеется. В противном случае в результирующей строке не будет цифры. Обратите внимание, что в итоговой строке не будет отображаться цифра, если соответствующей цифрой в строке ввода является незначащий 0. Например, 0003 ("####") -> 3. Описатель настраиваемого формата "#" служит символом-заполнителем для цифр. Если в форматируемом значении на позиции, где в строке формата присутствует символ "#", есть цифра, то эта цифра копируется в результирующую строку. В противном случае в выходной строке на этой позиции ничего не записывается. Обратите внимание, что при использовании этого описателя незначащие нули не отображаются, даже если ноль является единственной цифрой в строке. Ноль отображается только в том случае, если он является значащей цифрой отображаемого числа. Строка формата "##" приводит к округлению значения до ближайшего значения цифры, предшествующей десятичному разделителю, если назначено использование округления от нуля. Например, в результате форматирования числа 34,5 с помощью строки "##" будет получена строка со значением "35". Примеры #1 1234.5678 ("#####") -> 1235 0.45678 ("#.##", en-US) -> .46 0.45678 ("#.##", fr-FR) -> ,46 #2 double value; value = 1.2; Console.WriteLine(value.ToString("#.##", CultureInfo.InvariantCulture)); Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "{0:#.##}", value)); // Displays 1.2 value = 123; Console.WriteLine(value.ToString("#####")); Console.WriteLine(String.Format("{0:#####}", value)); // Displays 123 value = 123456; Console.WriteLine(value.ToString("[##-##-##]")); Console.WriteLine(String.Format("{0:[##-##-##]}", value)); // Displays [12-34-56] value = 1234567890; Console.WriteLine(value.ToString("#")); Console.WriteLine(String.Format("{0:#}", value)); // Displays 1234567890 Console.WriteLine(value.ToString("(###) ###-####")); Console.WriteLine(String.Format("{0:(###) ###-####}", value)); // Displays (123) 456-7890 Полезные ссылки для изучения: Строки настраиваемых числовых форматов Настраиваемый описатель "#" Типы форматирования в .NET Framework P.S: Вся необходимая информация содержится на сайте MSDN и была взята оттуда.

Ассемблерная команда LEA

#cpp #ассемблер


Не знаю почему, но эта ассемблерная команда не дает мне покоя LEA.

C++

int f(int t)
{
  return t+1;
}

int f(int*t)
{ 
  return *t+1;
}

int f(int& t)
{
  return t+1;
}


Ассемблер

f(int):                                  # @f(int)
        lea     eax, [rdi + 1]
        ret

f(int*):                                 # @f(int*)
        mov     eax, dword ptr [rdi]
        inc     eax
        ret

f(int&):                                 # @f(int&)
        mov     eax, dword ptr [rdi]
        inc     eax
        ret


Если команда MOV ясна как день, то команда LEA не ясна!

Я знаю что команда LEA выполняет вычисление адреса второго операнда и записывание
его в первый операнд (это все что мне известно)

В том примере что по ссылке, а именно:  lea eax,[rdi + 1] это явно не вычисление
адреса и не запись в первый операнд, нет запись то будет но скорее всего что-то другое.
Или я что-то не правильно понял? Объясните пожалуйста в соответствии с С++ кодом.

P.S Искал, но исчерпывающего ответа на мой вопрос не нашел, искал даже в книжке Калашникова,
а там даже этой команды не нашел...эхх.
    


Ответы

Ответ 1



lea eax, [rdi+1] Эта команда загружает в eax адрес значения, лежащего по адресу rdi + 1. Т.е. она загружает в eax просто rdi+1. Выглядит странно, и чтобы понять зачем именно нужна lea, и чем она лучше просто аналогичного вызова mov или ручного вычисления адреса, нужно понять как команды записываются в памяти и выполняются процессором. Например, у вас есть команда чтения значения: mov eax, [rdi+1]; взять значение по адресу "rdi + 1" Она компилируется в что-то вроде [опкод mov][флаг что складываем в eax][флаг что берем по адресу rdi][+1] Т.е. в 66 67 8B 47 01 Предположим что вам нужно получить сам адрес rdi+1 в eax Вы можете сделать одно из двух: Высчитать его руками: mov eax, rdi + 1; не работает, move не умеет плюс! и вам придется написать: mov eax, rdi inc eax; 66 05 01 00 00 00 т.е. выполнить две инструкции. Возможно, хороший вариант, но только для простых +1. А для адресов вида [bp+si+4]? mov eax, bp add eax, si add eax, 4; да, некрасиво! или выполнить lea: lea eax, [rdi+1] Сравните с mov: Байткод: 66 67 8D 47 01 Отличается только opcode, 8B -> 8D. В процессоре есть готовый, очень эффективный механизм для базовых операций с адресами. И он уже реализован для операции mov - ведь mov умеет доставать значение по адресу!. При использовании lea процессор делает все, что делает при mov, но пропускает последний шаг - извлечение значения по адресу. Вместо этого он складывает в eax сам адрес. Это гораздо удобнее и быстрее чем считать вещи вроде rdi + 1 отдельными командами. Какое это отношение имеет к вашему примеру? В вашем примере параметр лежит в rdi, а результат вы должны вернуть в eax. По-честному, компилятор должен был написать mov eax, rdi; 66 A1 add eax, 1; 66 05 01 00 00 00 Ну ок, для 1 можно использовать inc: mov eax, rdi; 66 A1 inc eax; 66 40 Но это все еще две команды. Процессор будет выполнять их по очереди. Компилятор умный. Он знает, что процессор умеет складывать значения регистров с небольшими константами при обработке команды lea. И он вставляет одну команду, которая выдаст тот же результат. lea eax, [rdi + 1] Неважно, что никакой адрес на самом деле никуда не загружается - главное что работать будет точно так же, и чуть быстрее - т.к. процессор вычисляет адреса в памяти быстрее, чем складывает числа :)

Ответ 2



Эта команда загружает в eax адрес того, что справа - т.е. rdi+1. Такой хитрый способ объединить mov eax, rdi inc eax Т.е. в каком-то смысле в переводе на C++ это &*(rdi+1) :) Т.е. получение адреса объекта, находящегося по адресу [rdi+1]. См., например, тут.

Ответ 3



Данная команда lea eax,[rdi + 1] заносит в регистр eax значение rdi + 1, где в rdi хранится значение аргумента. В двух остальных случаях, когда аргумент передается по ссылке или передается указатель на исходный аргумент, то регистр rdi содержит адрес аргумента. f(int*): # @f(int*) mov eax, dword ptr [rdi] inc eax ret f(int&): # @f(int&) mov eax, dword ptr [rdi] inc eax ret Поэтому сначала в регистр eax заносится значение аргумента, используя тот адрес, который находится в rdi mov eax, dword ptr [rdi] а затем значение регистра eax увеличивается на 1. inc eax То есть различие между первым определением функции и двумя последующими состоит в том, что в первом случае регистр rdi содержит копию значения аргумента, тогда как в двух последних случаях регистр rdi получает адрес исходного аргумента.

Анимация маршрутов самолётов на растровой карте

#html #css #css3 #svg #анимация


Довольно просто найти   карты стран, городов, аэропортов в растровом формате, но
довольно проблематично отыскать или нарисовать самому в векторном формате, тоже самое
в svg.  

Например,- у меня есть файл *.png карты аэропортов Канады.    

    

Мне нужно сделать визуализацию прилёта самолетов в аэропорты различных городов.  

Я догадываюсь, что анимация траектории полета самолетов будет не прямолинейна, по
какому-то криволинейному патчу. Поэтому использовать СSS animation затруднительно. 

Вопрос:   

Как увязать точки на растровой карте с траекториями SVG, не будет ли рассогласование
расположений при изменении масштаба?

Можно ли совместить растровый формат и анимацию SMIL SVG? 
    


Ответы

Ответ 1



Вариант SVG Чтобы не было рассогласования конечной точки криволинейного пути траектории движения самолётов и города на растровой карте, необходимо добавлять растровую карту не c помощью background свойства CSS или тега HTML , а внутри самого тега svg. Траекторию движения самолёта рисуем в векторном редакторе Сохраняем файл в формате *.svg Копируем из него патч траектории самолета, больше нам из файла ничего не нужно. Выбираем Юникод иконки самолета ✈ ✈ Пишем команду анимации движения этой иконки вдоль криволинейного пути: Собираем всё вместе Ниже код анимации для пяти самолетов: LIVE DEMO Вариант с остановкой анимациии и повторным запуском Добавляем кнопки "GO" и "STOP" старта и остановки анимации, на которые вешаем события onclick=start() и onclick=pause() let flag = 0, svg = document.querySelector('svg'); let start = function(){ if(flag === 1){ Array.from(svg.querySelectorAll('animateMotion')).forEach(e => e.removeAttribute('begin')); start = _ => svg.unpauseAnimations(); start(); } flag++; } const pause = function(){ svg.pauseAnimations(); } GO STOP

Ответ 2



Вариант CSS Анимацию движения по криволинейному пути сделать очень затруднительно, теоритически можно, но это будут простыни кодов. А вот стилизацию объектов SVG вполне легко реализовать. Преимущество этого метода вполне очевидно,- при больших проектах стили заменяются в одном месте и нет необходимости многократно прописывать каждому патчу стили. svg path { fill:none; stroke:none; } #Halifax_Airplan, #Montreal_Airplan, #Montreal_Calgary { font-size:30; } #Halifax_Airplan { fill:gold; } #Montreal_Airplan { fill:black; } #Calgary_Airplan { fill:lightcyan; } Связанные топики по использованию Юникодов в анимации: Как создать pattern или path цепи вдоль линии Использование символов Юникода в анимации Анимация появления карты с прорисовкой границ и с последующей закраской внутри Анимация наполнения карты контентом

Работа с памятью при использовании Git

#git #контроль_версий


Постараюсь быть максимально кратким.
На сайте русской документации по Git дается такое объяснение принципу его работы:


  Git считает хранимые данные набором слепков небольшой файловой
  системы. Каждый раз, когда вы фиксируете текущую версию проекта, Git,
  по сути, сохраняет слепок того, как выглядят все файлы проекта на
  текущий момент. Ради эффективности, если файл не менялся, Git не
  сохраняет файл снова, а делает ссылку на ранее сохранённый файл.




Так вот, если очень грубо допустить, что размер текстового файла стремится к 1Мб,
то следуя логике работы Git при даже самом малом изменении в этом файле(исправили ошибку
в слове), будет создаваться "слепок"(=копия) файла, размером ~1Мб. 

Вопрос: насколько это выгодно с точки зрения используемой памяти, или же текстовой
файл как-то делится на более мелкие файлы, и созданию "слепков" подлежат уже они?
    


Ответы

Ответ 1



Кратко: приведенная схема - это объяснение наблюдаемого поведения GIT. Если вы будете работать с GIT стандартными командами и не будете лезть руками в бинарные файлы - всё будет выглядеть как будто все так и работает. Но это вовсе не означает что на каждую версию файла будет и правда тратиться по мегабайту... Потому что на более низком уровне, гит все-таки использует дельты между файлами. Прочитать про них можно вот тут: Git изнутри - Pack-файлы Однако, упаковка файлов происходит не всегда, а только при достижении некоторого порога числа распакованных объектов или при пушу коммитов в удаленный репозиторий. При наличии больших файлов в некоторых случаях приходится явно выполнять команду git gc для переупаковки всех блобов (нам, к примеру, однажды пришлось добавлять эту команду в билд-скрипт, потому что на билд-сервере отправка в удаленные репозитории никогда не происходит и локальный репозиторий бесконтрольно рос).

Ответ 2



Как раз об этом повествует "Git изнутри - Pack-файлы". Собственно, там и ответ на вопрос: Однако, время от времени Git упаковывает несколько таких объектов в один pack-файл (pack в пер. с англ. — упаковывать, уплотнять) для сохранения места на диске и повышения эффективности. Это происходит, когда "рыхлых" объектов становится слишком много, а также при вызове git gc вручную, и при отправке изменений на удалённый сервер. То есть, через какое-то время в процессе эксплуатации репозитория различающиеся версии файла попадут в один Pack-файл, который представляет разные версии гораздо более эффективным образом: через основу и дельты (разности, изменения).

Ответ 3



или же текстовой файл как-то делится на более мелкие файлы, и созданию "слепков" подлежат уже они? Нет, файлы (как и любой другой объект, например, папка — список пар «имя дочернего объекта — его хэш») сохраняются именно целиком. Это, кстати, повышает «выживаемость» репозитория — если часть объектов по каким-то причинам будет повреждена или стёрта, остаётся крайне высокая вероятность восстановления если и не последнего commit-а, то хотя бы одного из последних. Однако параллельно с объектным хранилищем (./git/objects) есть и сжатое, где все объекты хранятся в одном упакованном файле (.git/objects/pack/) в виде diff-ов. Туда можно поместить любой объект, при этом оригинальный файл объекта удаляется. Так как операция упаковки является достаточно трудоёмкой, она выполняется только при вызове команды git gc --force. При этом любые дальнейшие изменения в репозитории (добавление commit-ов и переписывание истории) продолжат добавление объектов в основное хранилище; во втором же случае в pack-файле также останутся «хвосты», которые будут удалены только при следующем вызове git gc.

Ответ 4



TL;DR Файлы в гит хранятся целиком и не делятся. Это достаточно выгодно. Есть хорошие статьи по git (пример, пример), где раскрывается внутреннее устройство гит. Пользователь запускает git add на data/letter.txt. Происходят две вещи. Во-первых, создаётся новый блоб-файл в директории .git/objects/. Он содержит сжатое содержимое data/letter.txt. Его имя – сгенерированный хэш на основе содержимого. К примеру, Git делает хэш от а и получает 2e65efe2a145dda7ee51d1741299f848e5bf752e. Первые два символа хэша используются для имени директории в базе объектов: .git/objects/2e/. Остаток хэша – это имя блоб-файла, содержащего внутренности добавленного файла: .git/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e. Заметьте, что простое добавление файла в Git приводит к сохранению его содержимого в директории objects. Оно будет храниться там, если пользователь удалит data/letter.txt из рабочей копии. Далее показывается механика того, что происходит когда файл меняется. Когда пользователь создал data/number.txt, он хотел написать 1, а не 1234. Он вносит изменение и снова добавляет файл к индексу. Эта команда создаёт новый блоб с новым содержимым. Можете посмотреть файлы в папке object и через команду git cat-file -p увидеть разжатое представление ваших файлов. Это механика на уровне отдельных файлов. Казалось бы - неоптимальный расход. Тем не менее, есть дополнительный механизм - когда происходит упаковка объектов в более крупные файлы. Проблема с хранением больших файлов также привела к появлению LFS. Дело в том, что большие файлы, которые вы даже ни разу не правили (бинарники типа картинок) сохранять в git реально достаточно тяжело, поэтому их сохраняют на отдельные файловые хранилища.

Как создать hover эффект кнопок сайта animejs

#javascript #css #svg #canvas


Зашел случайно на сайт animejs 

Очень понравились hover эффект их основных кнопок везде искал но подобного эффекта
не нашел даже у них на сайте подобного нету 



.btn {
    width:100px;
    height:40px;
    
    text-align:center;
    padding:5px;
    line-height:2.3;
    border:1px solid red;
}
button
До эффекта После


Ответы

Ответ 1



Вроде похоже. let up = document.getElementById("up"), down = document.getElementById("down"), hoverBlock = document.getElementById("hover-block"); hoverBlock.addEventListener("mouseenter", function(e) { up.beginElement(); console.log }); hoverBlock.addEventListener("mouseout", function(e) { down.beginElement(); }); * { margin: 0; padding: 0; } html, body { width: 100%; height: 100%; background: #222; } #wrap { position: relative; width: 220px; height: 70px; background: transparent; } #hover-block { cursor: pointer; background: rgba(0, 0, 0, 0); position: absolute; top: 20px; left: 20px; width: 180px; height: 30px; z-index: 1111; } p { position: absolute; width: 100%; height: 100%; color: white; text-align: center; line-height: 3.8; letter-spacing: 5px; font-size: 1.2rem; font-family: ''; transition: all .3s; } #hover-block:hover~p { font-size: 1.2rem; transition: all .3s; font-size: 1.6rem; line-height: 2.7; color:orange; }

GitHub



Ответ 2



Анимация срабатывает по hover на красную область: #pt:hover { d: path("m 15,135 c 63,-16 122,-13 179,0 v 54 c -57,11 -117,14 -179,0 z"); d: "m 15,135 c 63,-16 122,-13 179,0 v 54 c -57,11 -117,14.35755 -179,0 z"; }

Ответ 3



Ну вот ребята как то так :) Суть ясно ничего сложного нету Ответил на основе ссылки который дал Other var createBouncyButtons = (function() { function createButton(el) { var pathEl = el.querySelector('path'); var spanEl = el.querySelector('span'); function hover() { anime.remove([pathEl, spanEl]); anime({ targets: pathEl, d: 'M10,10 C10,10 50,7 90,7 C130,7 170,10 170,10 C170,10 172,20 172,30 C172,40 170,50 170,50 C170,50 130,53 90,53 C50,53 10,50 10,50 C10,50 8,40 8,30 C8,20 10,10 10,10 Z', elasticity: 700, offset: 0 }); anime({ targets: spanEl, scale: 1.15, duration: 800, offset: 0 }); } function down() { anime.remove([pathEl, spanEl]); anime({ targets: pathEl, d: 'M10,10 C10,10 50,9.98999977 90,9.98999977 C130,9.98999977 170,10 170,10 C170,10 170.009995,20 170.009995,30 C170.009995,40 170,50 170,50 C170,50 130,50.0099983 90,50.0099983 C50,50.0099983 10,50 10,50 C10,50 9.98999977,40 9.98999977,30 C9.98999977,20 10,10 10,10 Z', elasticity: 700, offset: 0 }); anime({ targets: spanEl, scale: 1, duration: 800, offset: 0 }); } el.onmouseenter = hover; el.onmousedown = down; el.onmouseleave = down; } var buttonEls = document.querySelectorAll('.button'); for (var i = 0; i < buttonEls.length; i++) { var el = buttonEls[i]; createButton(el); } })(); * { margin: 0; padding: 0; } svg { position: absolute; left: 0; top: 0; right: 0; bottom: 0; z-index: -1; } .button { opacity: 0; position: relative; display: flex; -ms-flex-direction: column; flex-direction: column; justify-content: center; align-items: center; width: 180px; height: 60px; text-decoration: none; } .button.blue { color: #5E89FB; } Documentation

Почему функции которые содержат циклы, рекурсию или вызываются по указателю, плохо подвержены встраиванию (inline)

#cpp #функции #циклы #рекурсия #inline


Почему функции которые содержат циклы, рекурсию или вызываются по указателю, плохо
подвержены встраиванию (inline)
    


Ответы

Ответ 1



Вопрос построен на терминологической путанице. Встраивание вызовов функции - это не свойство самой функции, а всегда именно свойство индивидуального вызова функции. Свойства самой функции, разумеется, играют роль в принятии решения о том, будет ли конкретный вызов встроен, но все равно в общем случае это решение принимается для каждого отдельного вызова индивидуально. Одни вызовы функции могут оказаться встроены, в то время как другие вызовы той же функции могут оказаться не встроены. Даже аргументы функции, указанные в конкретном вызове, могут влиять на решение о том, встраивать ли этот конкретный вызов. Утверждение о том, что вызовы функций с циклами не встраиваются, относится к категории "это было давно и неправда". Каждый компилятор реализует какой-то набор эвристических критериев, который дает ему возможность принимать общие решения о том, заслуживают ли вызовы данной функции встраивания. В каком-то из старинных компиляторов этот эвристический критерий также включал проверку тела функции на наличие циклов (Borland или что-то в этом роде). На самом деле циклы не представляют из себя никаких преград для встраивания. Мне не известны никакие современные реализации, которые бы отказывались встраивать вызовы функций с циклами внутри. Утверждение о том, что функции, которые вызываются через указатель, не встраиваются - это как раз яркий пример того терминологической путаницы, о которой я писал выше. Это вызовы, выполненные через указатель, в общем случае не встраиваются. В то же время вызовы той же самой функции, выполненные напрямую, могут прекрасно встраиваться. Почему в общем случае невозможно встроить вызов, сделанный через указатель, очевидно: потому что на стадии компиляции не ясно, какая функция будет вызываться. Однако если компилятор в состоянии сообразить на стадии компиляции, какая функция будет вызвана через указатель, он без проблем встроит и такой вызов. Классический часто задаваемый вопрос, основанный на данной терминологической путанице - вопрос о встраивании вызовов виртуальных методов классов. Они ведь вызываются "через указатель" и значит не могут быть встроены, правда? Не правда. Во-первых, сам пользователь может выполнить вызов виртуального метода напрямую, без использования виртуального механизма. Во-вторых, в огромном количестве контекстов сам компилятор в состоянии сообразить, какой конкретный метод вызывается в данном вызове и, соответственно, встроить этот вызов. Вызовы рекурсивных функций тоже могут встраиваться. Если компилятор на стадии компиляции в состоянии оценить максимальную глубину рекурсии, то он может встроить эти вызовы ("развернуть рекурсию") на всю глубину. Если компилятор не в состоянии выполнить такой оценки, то он может встроить рекурсивные вызовы до определенной фиксированной глубины, после чего выполнить обычный рекурсивный вызов. Встраивание вызовов рекурсивных функции в этом отношении во многом аналогично развертке циклов. Одной из простейших и наиболее очевидных ситуаций целесообразности встраивания вызова функции является, например, ситуация, когда заведомо известно, что некоторая функция вызывается в программе ровно один раз (в пространственном смысле, т.е. исходный код программы содержит ровно одно место с ее вызовом). Такой функции нет никакого смысла существовать в виде отдельной функции, независимо от того, как "тяжела" эта функция. (С этим идет ряд побочных оговорок, но к данной теме они не относятся.) Например, если функция имеет внутреннее связывание и вызывается в своей единице трансляции ровно один раз, то современные компиляторы встроят этот вызов независимо от каких-либо иных критериев. В компиляторе GCC за это отвечает опция -finline-functions-called-once, которая включается уже в режиме -O1. В качестве другого примера, в компиляторе MSVC++ есть #pragma-параметры inline_recursion и inline_depth, которые управляют встраиванием рекурсивных функций и глубиной развертки рекурсии.

Ответ 2



Ну, с рекурсией понятно - если она не преобразована компилятором в цикл, то что получится? встраиваем код, на месте ее вызова опять встраиваем код, в котором на месте вызова... Сколько раз код встраивать? :) С вызовом по указателю - опять же: если встроена, то функция не имеет четкого пролога-эпилога, и не может быть вызвана по адресу - так что при вызове через указатель должен иметься не встроенный экземпляр функции как минимум - чтобы было, что вызывать. В самом месте вызова через указатель встроить ничего нельзя - так как встраивает компилятор, а во время компиляции значение указателя неизвестно. А про циклы - мне кажется, тут вы что-то напутали. Функция с циклом внутри вполне встраиваема - если, конечно, это имеет смысл :) Если вы имеете в виду, что сам цикл раскручивается - то опять же, это делается, но со своими ограничениями - например, компилятор может не знать, сколько реально итераций будет сделано - так как ему цикл разворачивать?..

Запретить поворот Activity и всего приложения

#java #android #activity #view


Добрый день.
Как правильно решить такую ситуацию?
Есть андроид-приложение, которое зависит от одного параметра - диагональ дисплея.
Эту величину, или приблизительную к ней, я нахожу - проблем нету. 
В зависимости от дисплея >5 дюймов - только landscape, <5 - только портрет.
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (ScreenSize<5) {
            setContentView(R.layout.startactivity_p); //отобразить хмл портретный
        }
        else{
            setContentView(R.layout.startactivity_l); // отобразить хмл ландскейп
        }

    }

В каждом xml-layout'е прописал следующее:
android:screenOrientation="landscape"
android:orientation="vertical"

и
android:screenOrientation="portrait"
android:orientation="horizontal"

соответственно.
Но приложение все равно поворачивается. Подскажите, как можно это исправить?    


Ответы

Ответ 1



Для каждого вашего случая setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); и setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); Не поворачивается. Вообще вот хорошая статья.

Ответ 2



Всё же лучше в Манифесте это сделать. android:screenOrientation="portrait" или android:screenOrientation="landscape" Всё вместе:

Как на SVG реализовать анимацию теней и z-index?

#javascript #css #svg #анимация #svg_animation


Как реализовать на SVG подобный пример с игрой теней и изменением z-index элементов?


  Для полного эффекта развернуть на весь экран и кликнуть для просмотра анимации. 




const shadows = document.getElementsByClassName("shadows")[0];
const shadowsChildren = shadows.children;
let count = 0;
let left = false;
shadows.addEventListener("click", function() {
  let interval = setInterval(function() {
    !left ? count++ : count--;
    shadowsChildren[left ? count : count - 1].style.zIndex = left ? count : shadowsChildren.length
- count;
    shadowsChildren[left ? count : count - 1].className = !left ? 'span' : '';

    if (count === shadowsChildren.length) {
      left = true;
      clearInterval(interval);
    }

    if (count === 0) {
      left = false;
      clearInterval(interval);
    }
  }, 100);
});
* {
  margin: 0;
  padding: 0;
}

html,
body {
  width: 100%;
  height: 100%;
  background: #272727;
}

.container {
  position: fixed;
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: orange;
}

.shadows {
  cursor: pointer;
  background: orange;
  position: relative;
  text-shadow: -15px 5px 20px #303030;
  width: 80vw;
  color: orange;
  text-align: center;
  letter-spacing: -20px;
  transition: all 0.25s ease-out .2s;
  font-family: fantasy;
  font-size: 200px;
}

span {
  position: relative;
  margin-left: -10px;
  transition: all 0.25s ease-out;
}

span.span {
  text-shadow: 15px 5px 20px #303030;
  transition: all 0.25s ease-out;
}
C L I C K M E
Вариант css:hover * { margin: 0; padding: 0; } html, body { width: 100%; height: 100%; background: #272727; } .container { position: fixed; width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center; background: orange; } .shadows { cursor: pointer; background: orange; position: relative; text-shadow: -15px 5px 20px #303030; width: 80vw; color: orange; text-align: center; letter-spacing: -20px; transition: all 0.25s ease-out .2s; font-family: fantasy; font-size: 200px; } span { position: relative; margin-left: -18px; transition: all 0.25s ease-out; } .shadows:hover span { text-shadow: 15px 5px 20px #303030; } .shadows:hover span:nth-child(1) { z-index: 8; } .shadows:hover span:nth-child(2) { z-index: 7; } .shadows:hover span:nth-child(3) { z-index: 6; } .shadows:hover span:nth-child(4) { z-index: 5; } .shadows:hover span:nth-child(5) { z-index: 4; } .shadows:hover span:nth-child(6) { z-index: 3; } .shadows:hover span:nth-child(7) { z-index: 2; } .shadows:hover span:nth-child(8) { z-index: 1; }
C L I C K M E


Ответы

Ответ 1



Сложная анимация перемещения Основа этой анимации - перемещение отдельной буквы вместе с фильтрами, создающими тень. На первом этапе все буквы из начальных позиций собираются в середину слова. На втором этапе буквы разбегаются из середины на исходные позиции Все перемещения букв реализуются командой изменения значения атрибута "X", задающего горизонтальную координату буквы: C Чтобы не запутаться в последовательности начала и окончания разных анимаций, необходимо продумать систему присвоения имен уникальных идентификаторов анимаций. В данном примере команда начала анимации для каждой буквы включает название буквы. Например: begin="anC", begin="anL" Для анимаций возврата букв на исходную позицию: begin="anBackC", begin="anBackL" Логика последовательности выполнения анимаций реализуется при помощи конструкции: begin="anBackC.end+0.25s" Словами это звучит так, - анимация возврата буквы "L" начнется после окончания анимации возврата буквы "C", после паузы равной 0.25s (секунды) Смотреть интересней в полно-экраном режиме снипета * { margin: 0; padding: 0; } text { text-align: center; font-family: sans-serif; font-size: 200px; font-weight: 900; fill: #FFA500; -webkit-filter: url(#shadowLetter); filter: url(#shadowLetter); } C L I C K M E

Ответ 2



Анимация тени Анимация достигается изменением атрибута dx фильтра feOffset Команда на выполнение: * { margin: 0; padding: 0; } text { text-align: center; font-family: sans-serif; font-size: 200px; font-weight: 900; fill: #FFA500; -webkit-filter: url(#shadowLetter); filter: url(#shadowLetter); } C L I C K M E

Ответ 3



Тени выполнены с помощью фильтров смещения - feOffset и размытия - feGaussianBlur Чтобы получить эффект наложения одной буквы на другую пришлось для каждой буквы писать отдельную команду с применением фильтра: C К сожалению приложение смотрится немного по разному в FireFox и в Chrome Ниже полный текст приложения * { margin: 0; padding: 0; } text { text-align: center; font-family: sans-serif; font-size: 200px; font-weight: 900; fill: #FFA500; -webkit-filter: url(#shadowLetter); filter: url(#shadowLetter); } C L I C K M E