Как стать автором
Обновить
26
0
Михаил @Mixalych

Пользователь

Отправить сообщение

Такое ощущение, что lorem5000< tab > прочитал. Проспитесь, молодой человек! Апосля - окуните свой чайник в студеную воду, которая так и не кипит уже который вечер от трения монет от монетизации из только премиальных из самых премиальных тредов. А когда проснетесь от брянканья из премиум банков о донатах, акститесь перед зеркалом из красного премиум дуба окантовки с элементами эвкалипта и по соседям шлите QR’ы пикселей в обмен на премиум счастье и почет от соседей и верующих в МММ, ибо никто, кроме сего бота невластен знать о звездах в созвездии Преумеума. Это просто пиз..ц. Аминь! Прости, Господи, и защити от Лукавого Преумима.

ЗЫ: И разберитесь с пунктуацией в свободное время, плиз!

Нечто подобное было на Galaxy S2 черти когда (был такой у меня). Но видимо гнусмас не патентовал.

Из множества перепробованных вариантов для яблочной экосистемы, также остановился на Obsidian - markdown (+ копипаста в youtrack, github/gitlab), хранение - просто файлы (в случае чего, открыть можно чем угодно). Может быть кому-то пригодится: при создании Store отмечаем синхронизацию с iCloud, получаем автоматическую синхронизацию на устройствах. Но стоить помнить, что на бесплатном тарифе всего 5Гб, (у меня 50 - хватает и на фоточки, и на заметки). Для дополнительного резервного копирования ставим Google Drive на мак, указываем, что хотим синхронизировать папку Obsidian из iCloud Drive. Затем уже на своем VPS через rclone синхронизируем заметки из Google Drive. Получаем 3 копии в разных местах. Костыль, но бесплатный и рабочий. Google Drive (и не только) на iOS не имеет возможности добраться до папок из iCloud, поэтому синхронизация настроена через мак, но я за ним работаю 99% из общего рабочего времени. Стандартные заметки используются для личного пользования. Из платных (и всех опробованных), но с возможностью хранить заметки на своем сервере в CouchDB, понравился - Inkdrop (не реклама ни разу, много времени потратил на поиск адекватного редактора), мультиплатформенный.

К сожалению, помимо бэкапов есть еще много чего — эквайринг, системы оповещения (mail+sms) и т. д., часть из которых требуют перезаключения договоров или уведомления о смене провайдера/ip/настроек — а это не быстрый процесс. Легли пять серверов, расположенных в 1, 2, 3. Дев сервер у нас работает — по моему в 4, и то с перебоями. Офисы в России, Украине и Франции без работы. И сколько это все продлится...?
Автор, приведите, пожалуйста, пример лямбда-функции, которая расставляет в тексте запятые и дефисы для наиболее часто встречаемых союзов и частиц.
Статью можно ужать просто до предпоследнего абзаца (аля твитнуть):
Может быть кому-то пригодится такой простой сервер в такое непростое время для облегчения выполнения рутинных задач. Полная документация есть здесь: github.com/eltaline/ctrl

Сухая статья — чтобы понять о чем она, нужно ходить по ссылкам. Тогда смысл статьи какой?

Он есть — скрутка проводов вручную.

Alt+arrow up отлично справляются с задачей выделения логических блоков во всех IntelliJ ide

Проще поставить нормальную IDE и не… себе голову.

Помню в начале 2000-х (3-4 года) за этот символ (или два, точно не помню — давно было) в PE-экзешники дописывали трек номер — чтобы потом читать его при запуске программы и отправлять на сервак для статистики — какая реклама (сайт) лучше работает для распространения. Сначала читали как бинарный файл, но позже были проблемы (уже в 2000 или xp) — считать сам себя экзешник не мог — поэтому открывали его через CreateProcessA и шагали по страницам памяти за переделы — все работало. Поэтому в «штатном» режиме чтения EOF всегда считал состоянием потока — то есть курсор чтения файла вышел за границы размера файла, и проверял это до чтения файла — достигнут ли конец файла и потом пробовал считать данные. Собственно как и константа INVALID_HANDLE_VALUE в винде по умолчанию -1, потому что handle принято считать всегда больше или равным 0, но меньше или равным максимальной разрядности, то есть те самые FF, собственно как и коды символов. А сравниваемые значения всегда приводятся к большей разрядности и получаем, что -1 никогда не будет в диапазоне 0..ff, что удобно при использовании кодов ошибок. По такой же схеме INVALID_HANDLE_VALUE равный -1 не попадающий в 0..0xFF FF FF FF. Это если про символы — но тут холивар разводить можно долго про «символ» vs «признак окончания чтения потока данных». Смотря в каком режиме и контексте используется данная аббревиатура…
Если два вас бигдата — это то, что можно поднять в оперативу — то советую ознакомиться с машиной Тьюринга и его бесконечной лентой (читай — данные). Ну есть у меня в одном проекте база больше 500 Гб — ну так мускул с ней справляется без проблем — на сервере 32 ГБ оперативы. И это еще ни разу не бигдата, да, там есть пару-тройку тяжелых таблиц (гигов по 100 и миллиардами записей), которые нужно джойнить и фильтровать… Но это ладно.

Вы используете массивы как множество — это разные понятия, и пытаетесь притянуть за уши свой пример для «решения» задачи — хештаблицы и просто массив разные понятия. Если вы случайно сделали для себя открытие — поздравляю, мир об этом уже знает сто лет!
И ваше же решение для вашей же задачи не подходит:
Иногда нужно найти строку в массиве строк

Вы не находите строку, а просто говорите о наличииесть она там или нет. Чтобы найти строку есть array_search (для поиска первого вхождения) и array_keys для поиска всех вхождений — на выходе получаете ключ/массив ключей, которые содержат искомую строку.
Хм, странно… это же принципиально разные вещи — поиск по хешу или по значению. Это все равно, что повесить индекс к столбцу в базе данных и удивляться, что стало быстрее… Или не делить на два, а битовый сдвиг вправо, или проверить на четность как a & 1, а еще можно использовать как битовые флаги.

Притянутая за уши ситуация, когда сделал для себя «открытие».
Как это разглядеть обратно?! Мои глаза… Такого вброса давно не было. Хотя на первое апреля самое то!
Может быть кому пригодится, оставлю это здесь.

В одном проекте использую как раз эти базы — проблемы начинаются, когда нужно определять оператора для сотен тысяч номеров или миллионов за раз и быстро. Для этого подготовим базу:

SQL
CREATE TABLE `lb_phone_code` (
  `id` int(11) NOT NULL,
  `operator` varchar(30) NOT NULL COMMENT 'Код оператора',
  `operator_name` varchar(200) NOT NULL COMMENT 'Имя оператора',
  `region` int(11) NOT NULL COMMENT 'Регион',
  `code_start` bigint(20) NOT NULL COMMENT 'Начало номерной емкости',
  `code_end` bigint(20) NOT NULL COMMENT 'Конец номерной емкости',
  `code_count` bigint(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


ALTER TABLE `lb_phone_code`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `lb_phone_code`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;


В code_count записываем разницу между code_end — code_start + 1, сами номера храним в международном формате — 11 чисел, начиная с 7 (для России в частности) без всяких плюсов и пр., как того требует smpp-протокол и как принято у операторов. Получаем что-то подобное:

Screenshot


Интересным является поле Код оператора — это логическое разбиение операторов по признаку — тут уже каждый сам для себя определяет — в открытом доступе я этого не нашел (если у кого есть инфа — буду благодарен, мы же вычисляем по своей базе, опыту, запрашиваем информацию у других источников через api/вручную). Именно оно в проекте и важно (для определения цены :)

Php-класс для определения оператора для списка номеров:

PHP
<?php

namespace app\admin\modules\phonecode\models;

use Yii;
use app\admin\modules\utils\Module as Utils;

/**
 * This is the model class for table "lb_phone_code".
 *
 * @property int $id
 * @property string $operator Код оператора
 * @property string $operator_name Имя оператора
 * @property int $region Регион
 * @property int $code_start Начало номерной емкости
 * @property int $code_end Конец номерной емкости
 */
class LbPhoneCode extends \yii\db\ActiveRecord
{

    static private $list = null; // кеш емкости телефонов и операторов

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'lb_phone_code';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['operator', 'operator_name', 'region', 'code_start', 'code_end'], 'required'],
            [['region', 'code_start', 'code_end'], 'integer'],
            [['operator'], 'string', 'max' => 30],
            [['operator_name'], 'string', 'max' => 200],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'operator' => 'Код оператора',
            'operator_name' => 'Имя оператора',
            'region' => 'Регион',
            'code_start' => 'Начало номерной емкости',
            'code_end' => 'Конец номерной емкости',
        ];
    }

    /**
     * @inheritdoc
     * @return LbPhoneCodeQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new LbPhoneCodeQuery(get_called_class());
    }

    public static function getInfo($phone) {

        /*if (empty($phone = Utils::getPhone($phone, -2))) {

            return null;
        }

        $query = LbPhoneCode::find();

        $query->andWhere(['<=', 'code_start', $phone]);
        $query->andWhere(['>=', 'code_end', $phone]);

        return $query->one();*/

        // используем кеш, а также новый формат телефона с кодом страны

        if (empty($phone = Utils::getPhone($phone))) {

            return null;
        }

        $id = false;
        /*$operator =*/ self::getOperator($phone, '', $id);

        if ($id !== false) {

            return self::findOne($id);
        }

        return null;
    }

    private static function initPhoneCodeList() {

        if (self::$list === null) {

            $query = "SELECT code_start s, code_end e, operator o, id i FROM lb_phone_code order by code_count DESC";
            $l = Yii::$app->db->createCommand($query)->queryAll();

            $min = $max = 0;
            self::$list = [];

            foreach ($l as $item) {

                $item['s'] = (int) $item['s'];
                $item['e'] = (int) $item['e'];

                // вычисляем сразу лимиты, что бы не искать вне диаппазона
                if ($min === 0 || $min > $item['s']) { $min = $item['s']; }
                if ($max === 0 || $max < $item['e']) { $max = $item['e']; }

                // начало диаппазона делаем ключом, чтобы не хранить лишнюю информацию,
                // т. к. ключ все равно создается
                self::$list['range'][$item['s']] = [
                    $item['e'],
                    $item['o'],
                    $item['i'], // добавим связь по id, чтобы найти полную инфу, какая строка сработала
                ];
            }

            self::$list['min'] = $min;
            self::$list['max'] = $max;
        }

        return self::$list;
    }

    /**
     * @param $phone - номер телефона, оператора которого ищем, не валидируется на корректность
     * @param string $default_operator
     * @param null $return_id - будет помещен id найденного оператора, либо false
     * @return string
     */
    public static function getOperator($phone, $default_operator = '', & $return_id = null) {

        self::initPhoneCodeList();
        $phone = (int) $phone;

        if ($phone >= self::$list['min'] && $phone <= self::$list['max']) {

            foreach (self::$list['range'] as $start => $item) {

                if ($phone < $start || $phone > $item[0]) {

                    continue;
                }

                if ($return_id !== null) { $return_id = $item[2]; }

                return $item[1];
            }
        }

        if ($return_id !== null) { $return_id = false; }

        return $default_operator;
    }

    /**
     * @param $phone_list - список телефонов (будет изменен полученными результатами)
     * @param string $default_operator - оператор по умолчанию, если не найден в базе
     * @param bool $use_keys - телефоны указаны как ключи или как значение
     * @param null $field_name - в какое поле помещать результат, если null, то заменят значение
     */
    public static function getOperatorByList( & $phone_list, $default_operator = '', $use_keys = false, $field_name = null) {

        if (empty($phone_list)) {

            return;
        }

        if ( ! is_array($phone_list)) {

            $phone_list = (array) $phone_list;
        }

        self::initPhoneCodeList();

        // если нам передан просто массив телефонов, то делаем их ключами, чтобы
        // напротив них разместить результат (оператора)
        if ( ! $use_keys) {

            $phone_list = array_flip($phone_list);
        }

        foreach ($phone_list as $key => $_) {

            $phone = (int) $key;
            $not_found = true;

            if ($phone >= self::$list['min'] && $phone <= self::$list['max']) {

                foreach (self::$list['range'] as $start => $item) {

                    if ($phone < $start || $phone > $item[0]) {

                        continue;
                    }

                    if ($field_name === null) {

                        $phone_list[$key] = $item[1];
                    } else {

                        $phone_list[$key][$field_name] = $item[1];
                    }

                    $not_found = false;

                    break;
                }
            }

            if ($not_found) {

                if ($field_name === null) {

                    $phone_list[$key] = $default_operator;
                }
                else {

                    $phone_list[$key][$field_name] = $default_operator;
                }

            }
        }
    }
}




Подгружаем номерные емкости в оперативу — сортируем по номерной емкости по убыванию, перебираем все емкости с учетом границ — просто сравнивая номера как числа.

При обновлении базы — номер сменил оператора — ищем старый диапазон и разбиваем его на два — до номера и после номера. Затем создаем новый — левая и правая границы равны номеру, емкость — 1. Релоадим кеш.

P. S.: Тот самый нужный код оператора при обновлении базы — ищем по названию нового оператора в базе — если есть таковой, то присваиваем его, в противном случае — помечаем как 'none' — и отправляем уведомление админу и менеджеру, что требуется вмешательство.
И как всегда — в статьях такого плана, самое интересное в конце — есть еще вебсокеты. Вот есть у меня сейчас задача — написать мессенджер, почитал много статей, и везде сову нужно дорисовать самому. А собственно, интересны такие вопросы:
1. Сами же написали, что есть вебсокеты, socket.io. Там протокол бинарный и создан именно для этого — у автора статьи — текстовый. Ну да ладно, не о протоколах речь. О том, что в предложенных протоколах уже есть двухсторонний обмен — emit, broadcast, ping и пр.
2. Онлайн обмен даже не вызывает вопросов, хотя… Если во время отправки сообщения отвалился инет — как работать с очередями, сложить все, что нужно доотправить в sqlite и по alert'ам от календаря отправлять повторно? Сколько раз…
3. Как получать новые сообщения — от последнего id или по дате создания (с датой не совсем наверное идея, ибо зависит от локали устройства)?
4. Как быть в ситуации, когда коннектов от одного юзера несколько — яблоко и андроид, для примера? Понимаю, что что-то вроде socket.in_room(id).emit()… Но хочется подробностей.
5. В чем хранить сообщения (и очередь) на серваке — rabbit, mysql (ща полетят помидоры), mongo? Забирать все это по модному graphql или не прыгать выше головы, чтобы понимать как там все под капотом?!
6. Уведомление пушем, когда клиент не в сети — тоже момент так себе — tcp может жить и при физическом обрыве линии согласно выставленному таймауту, да даже пинги согласно протокола (socket.io, про него речь, tcp «гарантирует» доставку) не дают гарантии, что сокет клиента живой, соответственно, нам нужен ask-пакет доставки, и уже по таймауту, если ответа нет, слать пуш. Слать пуш — в канал firebase — не сложно. Интересно, как хранить все токены от одного пользователя.

И таких вопросов очень много, и больше они интересуют — техническая сторона, которую сложно найти на просторах инета. Да, есть вотсап с измененным xmpp на erlang серверах, но вот именно хочется внутрянки, у телеги вроде бы исходники открыты, но большая часть техно-«вкусняшек» скомпилено в mit_proto.

Если у хабрасообщества есть ссылки на полезную инфу в этом направлении — спасибо заранее.

Зы: Для себя вижу так — чтобы развить немного себя — берем flutter, soket.io третьей версии, firebase и sqlite, пытаемся делать все асинхронно, запрашиваем все от последнего id сообщения, на сервере — для начала берем workerman для протокола, redbean — orm для мускула с отключенным frooze (чтобы сам столбцы таблицы создавал). Закладываем сразу функционал комнат (room id), изначально только двое в диалоге. Но что-то подсказывает, что не все правильно в архитектурном смысле. Вот тут и не хватает инфы/знаний/бубна...
Танк, как в том анекдоте.
«Apple не» только «в состоянии перенести производство своих устройств в США», но и даже мизерную часть расходников относительно партии, которую один китаец в подвале сделает за один день, причем за треть от того, что «попросит» американец, чтобы начать думать — пойти на эту работу или нет. Причем китаец делать будет это с закрытыми глазами, без кондёра.

«Несмотря на все заявления Apple, корпорация вовсе не стремится переводить производство в США, да и в ряд ли она сможет это сделать в ближайшем обозримом будущем, даже если очень заходит. Дело не только в винтиках, но и во многих других факторах, ситуация слишком сложная.» — Статью до этого абзаца можно было сократить…

Если память не изменяет, то это было на CDMA телефоны начале 2000-х, я тогда только поступал в универ. И Билайн в нашем регионе тогда назывался вроде бы NcGSM. Модно было его иметь — у «шишек» он был, но его регистрировать надо было действительно. А я как студент мечтал хотя бы просто о мобильном — sgh-x450…

Информация

В рейтинге
Не участвует
Откуда
Ставрополь, Ставропольский край, Россия
Дата рождения
Зарегистрирован
Активность