Страницы

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

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

Как узнать полный путь до текущей директории в Ubuntu?

#linux


Как узнать полный путь до текущей директории в Ubuntu?    


Ответы

Ответ 1



В терминале выполнить команду pwd

Ответ 2



Кроме команды pwd можно использовать переменную окружения $PWD, которую определяют большинство интерпретаторов командной строки (bash, dash, tcsh и даже busybox sh) и изменяют её динамически при смене каталогов: echo $PWD

Составной ключ в Dictionary

#c_sharp #net


Допустим, есть класс, у которого есть 2 цифровых поля.

Хотелось бы эти 2 поля сделать первичным ключем в Dictionary.

В будущем этот ключ будет использоваться для поиска совпавших значений между 2 мя
словарями.

Как это можно сделать, что бы не потерять в производительности?

У меня была идея, хранить их как строку, но может быть есть решение лучше?

P.S 

Dictionary использую из-за высокой скорости поиска по ключу.

К моему удивлению, DataTable оказался тормознутее=( на 20к строк
    


Ответы

Ответ 1



Как один из вариантов Можно использовать класс Tuple. Например Dictionary, T3>, где T1, T2, T3 значения любых типов. В Tuple сравниваться будет по внутренним значениям. у Tuple переопределены методы GetHashCode и Equals © Grundy Использовать структуру, т.к. у структур идет сравнение по всем имеющимся полям Передавать IEqualityComparer в конструктор, где Т - тип ключа. Сравнение будет производиться с использованием компаратора.

Ответ 2



Есть несколько путей использовать составной ключ для Dictionary (аналогично для HashSet) Использовать структуру. Правила сравнения структур таковы, что сравниваются значения всех полей, а не ссылки на объекты. Использовать класс с переопределенными методами Equals, GetHashCode. Это позволит использовать для вычисления хэша не все поля класса, а только нужные. Использовать класс реализующий IEquatable Использовать при создании словаря перегрузку конструктора принимающую IEqualityComparer

Ответ 3



Привношу пример кода из дублирующего вопроса. Классу Dictionary для того, чтобы обращаться к элементу по ключу, надо сравнивать ключи друг с другом. Поэтому необходимо реализовать операцию сравнения двух объектов составного ключа, например, реализовав интерфейс IEquatable и переопределив метод object.Equals и object.GetHashCode. Пример реализации IEquatable и переопределения Equals и GetHashCode: public class ComplexKey : IEquatable { private int value1; private string value2; public ComplexKey(int value1, string value2) { this.value1 = value1; this.value2 = value2; } public bool Equals(ComplexKey other) { return EqualityComparer.Default.Equals(value1, other.value1) && EqualityComparer.Default.Equals(value2, other.value2); } public override bool Equals(object other) { if (other is ComplexKey) return Equals((ComplexKey)other); return false; } public override int GetHashCode() { var result = 17; unckecked { result = 31 * result + EqualityComparer.Default.GetHashCode(value1); result = 31 * result + EqualityComparer.Default.GetHashCode(value2); } return result; } } Методы Equal просто сравнивают попарно все значения составного ключа и возвращают true только тогда, когда все поля класса совпадают. Метод GetHashCode считает значение хеша способом, который на SO набрал больше всего плюсов.

Ответ 4



Вероятно, Tuple подойдёт для использования в качестве ключа.

Как получить возраст пользователя?

#javascript #data_mining


Яндекс Метрика при добавлении своего кода на сайт позволяет получать информацию о
возрасте, географии и устройстве.
Какие сервисы позволяют получать эту информацию в режиме реального времени при заходе
пользователя на сайт?
    


Ответы

Ответ 1



Вопрос довольно сложный. Для получения таких данных используют сложные алгоритмы основанные на машинном обучении и возможно на искусственном интеллекте использующие большой и актуальный объем информации о пользователях. Все что остается нам, простым смертным, делать это использовать готовые решения продающие нужную информацию. Их на рынке не так много (особенно в русском сегменте), обычно принято называть их DMP. Из контекста вашего вопроса вам нужна информация по соцдему. Я знаю несколько поставщиков информации по соцдему - adfox (косвенно можно получить нужную информацию), aidata, CleverDATA, Facetz.DCA (не все они работают в режиме реального времени). Добавлю еще немного деталей. Все поставщики данных требуют zero pixel matching, т.е. дают ссылку в параметры которой нужно вставить произвольный идентификатор пользователя на стороне площадки, который потом будет использован в api для получения информации о пользователе и в отгружаемых отчетах (по сути это база данных где в качестве ключа фигурирует ваш идентификатор пользователя и вся доступная информация об этом пользователе).

Ответ 2



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

Ответ 3



Google Analytics позволяет получать эти данные в режиме реального времени при заходе пользователя на сайт

Двумерный vector

#массивы #cpp


Подскажите, пожалуйста, как написать, заполнить и вывести двумерный vector. С одномерным
все ясно, а вот двумерные никак не могу понять((    


Ответы

Ответ 1



Если известны размеры (или один): vector > a(n, vector(m, значение)); // два размера vector > b(n, vector()); // один размер Можно указать значение, которым нужно заполнить вектор. Если T — класс, и значение не указано, то будет использован конструктор по умолчанию. Например: vector c(n, -1); // вектор, заполненный -1 vector d(m, my_class("Hello, World!", 42)); Если число элементов заранее не известно, то можно воспользоваться push_back(): vector > e; e.push_back(vector());

Ответ 2



vector< vector > v; v.push_back( vector() ); v[v.size()-1].push_back(100500); int a=v[i][j]; не компилировал... но по памяти вроде так.

Ответ 3



vector< vector > vvi; //... initial for(vector >::iterator it = vvi.begin(); it != vvi.end(); ++it){ for(vector::iterator it2 = (*it).begin(); it2 != (*it).end(); ++it2){ // ...out } } Как то так

Ответ 4



Если массив будет прямоугольным и его размеры будет неизменными, то можно сделать класс, который одномерный массив разворачивает в линейный: template class Array2D { public: Array2D(size_t w, size_t h): width(w), height(h), data(new T[w*h]) {} ~Array2D() {delete[] data;} T* operator[](size_t i) {return data+i*height;} const T* operator[](size_t i) const {return data+i*height;} private: T* data; size_t width, height; } Или, используя std::vector внутри: template class Array2D { public: Array2D(size_t w, size_t h): width(w), data(w*h) {} T* operator[](size_t i) {return data.data()+i*height;} const T* operator[](size_t i) const {return data.data()+i*height;} private: std::vector data; size_t width; //height не храним, так как при желании его можно посчитать, поделив data.size() на width } Он ведёт себя так же, как и обычные двумерные массивы, но его размеры определяются во время выполнения программы, а не во время компиляции, как у обычных статических массивов. Строка кода Array2D array(10, 20); аналогична такому объявлению статического массива int array[10][20]; Заполняются они так же, допустим, так: for(int i=0; i<10; i++) for(int j=0; j<20; j++) array[i][j]=i+j; Преимущество этого класса в том, что мы выделяем память одним непрерывным блоком, как и статический массив в Си. Кроме того, в operator[] можно добавить проверки выхода за пределы массива. Но выход за пределы строки проверить нельзя, не усложняя класс прокси-объектами. Такой выход за пределы строки даст нам элемент следующей строки.

Ответ 5



Правильный ответ (массив n - строк, m - столбцов) vector< vector > matrix(n, vector (m, 0));

Ответ 6



#include // Так надо #include // Для вектора using namespace std; // Чтобы не мучатся int main() { int n, m; // Создание переменных отвечающих за размер вектора cin >> n >> m; // Ввод размеров вектора vector < vector > a(n, vector (m) ); // Объявление вектора на n строк по m элементов for (int i = 0; i < n; i++) // Цикл, который идёт по строкам for (int j = 0; j < m; j++) // Цикл, который идёт по элементам { cin >> a[i][j]; // Заполнение вектора или массива (в данном случае ввод) } for (int i = 0; i < n; i++) // Цикл, который идёт по строкам { for (int j = 0; j < m; j++) // Цикл, который идёт по элементам cout << a[i][j] << ' '; // Вывод элементов i строки вектора cout << endl; } return 0; }

Ответ 7



Инициализация (значения подобрал для примера): std::vector > A = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; Вывод: for (int i = 0; i < A.size(); i++) { for (int j = 0; j < A[i].size(); j++) { std::cout << A[i][j]; } }

Не пойму, что делает функция bind() в коде

#cpp #boost #asio


Здравствуйте, недавно начал изучать Boost.Asio, и в одном из примеров не могу понять,
что делает функция bind() в коде данной программы.
Если кто знает, для чего придназначена эта функция, пожалуйста, объясните. Так как
я недавно начал углубляться в библиотеку STL и Boost. Заранее спасибо.

io_service service;
size_t read_complete(char * buff, const error_code & err, size_t bytes) 
{
    if ( err) return 0;
    bool found = std::find(buff, buff + bytes, '\n') < buff + bytes;
    // we read one-by-one until we get to enter, no buffering
    return found ? 0 : 1;
}

void handle_connections() 
{
    ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(),8001));
    char buff[1024];
    while ( true) 
    {
        ip::tcp::socket sock(service);
        acceptor.accept(sock);
        int bytes = read(sock, buffer(buff), boost::bind(read_complete,buff,_1,_2));
        std::string msg(buff, bytes);
        sock.write_some(buffer(msg));
        sock.close();
    }
}

int main(int argc, char* argv[]) 
{
    handle_connections();
}

    


Ответы

Ответ 1



std:bind (я буду говорить об std::bind, так как эта функция перенесена в стандарт C++ из boost) - это адаптер функциональных объектов, который позволяет адаптировать функциональные объекты под заданное число параметров. Чтобы было более ясно ее назначение, допустим вы решили написать функцию вывода произвольного целочисленного массива в поток std::cout . Ваша программа могла бы выглядеть следующим образом: #include void display( const int a[], size_t n ) { for ( size_t i = 0; i < n; i++ ) { std::cout << a[i] << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; display( a, N ); } Вывод программы на консоль: 1 2 3 4 5 6 7 8 9 10 Но затем вам пришла в голову идея усовершенствовать функцию вывода таким образом, чтобы она не просто выводила элементы массива, как они есть, но и делала какие-то с ними преобразования перед выводом. Поэтому вы решили добавить параметр в функцию, который будет представлять некоторую операцию над элементами массива. Этот третий параметр вы объявили как функцию с одним параметром. Используя эту функцию, вы можете, например, вывести на консоль удвоенные значения элементов массива с помощью лямбда-выражения: #include void display( const int a[], size_t n, int operation( int ) ) { for ( size_t i = 0; i < n; i++ ) { std::cout << operation( a[i] ) << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; auto doubled = []( int x ) { return 2 * x; }; display( a, N, doubled ); } Вывод на консоль будет 2 4 6 8 10 12 14 16 18 20 В данном случае возможно использовать лямбда-выражение doubled, потому что оно может быть неявно преобразовано в функцию с одним параметром, так как не имеет замыкания. Но, допустим, вы решили выводить на печать не только удвоенные значения элементов массива, но и значения элементов, умноженных на некоторый коэффициент, который задается во время выполнения программы. В этом случае ваша программа могла бы выглядеть следующим образом #include void display( const int a[], size_t n, int operation( int ) ) { for ( size_t i = 0; i < n; i++ ) { std::cout << operation( a[i] ) << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int factor = 2; auto multiplies = [&factor]( int x ) { return factor * x; }; display( a, N, multiplies ); factor = 3; display( a, N, multiplies ); } Однако она не будет компилироваться. Проблема заключается в том, что если лямбда-выражение содержит замыкание, auto multiplies = [&factor]( int x ) { return factor * x; }; ^^^^^^^^^ то оно не имеет неявного преобразования в указатель на функцию. Поэтому придется написать шаблонную функцию, которая будет иметь шаблонный параметр операции и в качестве аргумента может принимать не только функции, но и объекты классов, который имеют оператор вызова функции operator (). Ваша программа будет выглядеть следующим образом: #include template void display( const int a[], size_t n, Operation operation ) { for ( size_t i = 0; i < n; i++ ) { std::cout << operation( a[i] ) << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int factor = 2; auto multiplies = [&factor]( int x ) { return factor * x; }; display( a, N, multiplies ); factor = 3; display( a, N, multiplies ); } Вывод на консоль соответственно будет: 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 Но теперь, допустим, вы решили вывести на печать квадраты значений элементов массива. Вы опять бы могли использовать лямбда-выражение, как, например, auto_square = []( int x ){ return x * x; }; И ваша программа успешно бы выполнилась, используя это лямбда-выражение. Но вы знаете, что стандарт уже имеет функциональный объект std::multiplies, который осуществляет операцию x * y для двух аргументов своего оператора вызова функции operator (). Как им воспользоваться? Оператор этого функционального объекта принимает два аргумента, тогда как в нашей функции display функциональный объект принимает только один аргумент. Для этого используются функциональные адаптеры и, в частности, std::bind. Он может "превратить" функциональный объект std::multiplies из объекта, который принимает два аргумента в объект, который принимает один аргумент. Программа будет выглядеть следующим образом: #include #include template void display( const int a[], size_t n, Operation operation ) { for ( size_t i = 0; i < n; i++ ) { std::cout << operation( a[i] ) << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; auto square = bind( std::multiplies(), std::placeholders::_1, std::placeholders::_1 ); display( a, N, square ); std::cout << std::endl; } Вывод на консоль будет 1 4 9 16 25 36 49 64 81 100 Внутри функции display объект square вызывается как square( a[i] ), который в свою очередь делегирует работу объекту класса std::multiplies, вызывая его как std::multiplies()( a[i], a[i] ); Конечно конструктор std::multiplies не вызывается, так как объект данного класса уже был создан, когда создавался объект square auto square = bind( std::multiplies(), std::placeholders::_1, std::placeholders::_1 ); Вызывался только operator () этого объекта. В вашем примере вызов read требует в качестве третьего аргумента функциональный объект, который принимает только два аргумента: int bytes = read(sock, buffer(buff), boost::bind(read_complete,buff,_1,_2)); Однако, вы хотите, чтобы вызывалась функция read_complete, которая принимает три аргумента вместо двух. В этом случае используется адаптер bind, который сам получает два аргумента, как того требует вызов функции read, но делегирует работу функции read_complete, передавая ей три аргумента: два своих, обозначенных как _1 и _2, и которые ей передает внутри своего тела функция read, когда ее вызывает, и один дополнительный, buff, который она сохранила внутри себя, когда использовалась в качестве аргумента вызова read.

Ответ 2



bind это немного интересная функция (не путать с функцией работы сокетами, которая также называется bind!!!). Ее задача создать одну функцию на базе другой, подставив часть аргументов (это называется "частичным применением"). Посмотрим глубже. Функция read требует трех параметров - сокета, буфера для данных и "функции завершения" (вообще то у этой бустовой read всего восемь различных перегруженных вариантов). Эта функция будет вызываться, что бы определить, что данных прочитано достаточно. Там же в справке приведена сигнатура этой функции. У нее два параметра: код ошибки последнего чтения и сколько байт прочитано. Но что делать, если нет подходящей функции? Для этого есть bind. Он первым аргументом принимает функцию (в данном случае read_complete, а дальше аргументы. Так как исходная функция read_complete имела три аргумента, а функции read нужно только два, то один аргумент подставляется явно (первый), а вместо двух других - заглушки специального вида. В результате, когда функции read нужно будет вызвать функцию завершения, она вызовет read_complete, первый аргумент ей подставит bind, а два вторых будут подставлены с аргументов. Если попробовать написать код схематически, то все выглядит где то так io_service service; size_t read_complete(char * buff, const error_code & err, size_t bytes) { if ( err) return 0; bool found = std::find(buff, buff + bytes, '\n') < buff + bytes; // we read one-by-one until we get to enter, no buffering return found ? 0 : 1; } char buff[1024]; size_t new_func(const error_code & err, size_t bytes) { return read_complete(buff, err, bytes); } void handle_connections() { ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(),8001)); while ( true) { ip::tcp::socket sock(service); acceptor.accept(sock); int bytes = read(sock, buffer(buff), new_func); std::string msg(buff, bytes); sock.write_some(buffer(msg)); sock.close(); } } Как видите, мне пришлось сделать буфер глобальным, что бы новоиспеченная функция имела к нему доступ. На самом деле переменная глобальной не становится, но это был самый простой способ эмулировать поведение. Эта функция bind пришла в с++ с функциональных языков вида haskell и лисп, где подобное встречается на каждом шагу.

Java реализация суммы прописью

#java #java_ee


Существует ли библиотечная реализация перевода суммы в слова на русском языке? Если
нет, то как реализовать оптимальным способом?
    


Ответы

Ответ 1



Можете воспользоваться готовой библиотекой Icu4j: RuleBasedNumberFormat nf = new RuleBasedNumberFormat(Locale.forLanguageTag("ru"), RuleBasedNumberFormat.SPELLOUT); System.out.println(nf.format(1234567)); // один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь RuleBasedNumberFormat nf = new RuleBasedNumberFormat(Locale.forLanguageTag("pl"), RuleBasedNumberFormat.SPELLOUT); System.out.println(nf.format(1234567)); // jeden milion dwieście trzydzieści cztery tysiące pięćset sześćdziesiąt siedem RuleBasedNumberFormat nf = new RuleBasedNumberFormat(Locale.forLanguageTag("en"), RuleBasedNumberFormat.SPELLOUT); System.out.println(nf.format(1234567)); // one million two hundred thirty-four thousand five hundred sixty-seven RuleBasedNumberFormat nf = new RuleBasedNumberFormat(Locale.forLanguageTag("de"), RuleBasedNumberFormat.SPELLOUT); System.out.println(nf.format(1234567)); // eine Million zwei-hundert-vier-und-dreißig-tausend-fünf-hundert-sieben-und-sechzig

Ответ 2



Использую этот класс. Можно поискать еще что-то подобное на github package net.sf.lab3f.util; import java.util.Stack; public class RussianMoney { private static enum Ranges {UNITS, DECADES, HUNDREDS, THOUSANDS, MILLIONS, BILLIONS}; private static Stack threeChars; private static class ThreeChar { char h, d, u; Ranges range; } public static String digits2Text(Double d){ if(d == null || d < 0.0) return null; String s = d.toString(); int n = s.length() - s.lastIndexOf('.'); if(n > 3) return null; if(n == 2) s += "0"; String[] sa = s.split("\\."); threeChars = new Stack (); threeChars.push(new ThreeChar()); threeChars.peek().range = Ranges.UNITS; StringBuilder sb = new StringBuilder(sa[0]).reverse(); for(int i = 0; i < sb.length(); i++){ if(i > 0 && i % 3 == 0){ threeChars.push(new ThreeChar()); } ThreeChar threeChar = threeChars.peek(); switch(i){ case 0: threeChar.u = sb.charAt(i); break; case 3: threeChar.range = Ranges.THOUSANDS; threeChar.u = sb.charAt(i); break; case 6: threeChar.range = Ranges.MILLIONS; threeChar.u = sb.charAt(i); break; case 9: threeChar.range = Ranges.BILLIONS; threeChar.u = sb.charAt(i); break; case 2: case 5: case 8: threeChar.h = sb.charAt(i); break; default: threeChar.d = sb.charAt(i); } } StringBuilder result = new StringBuilder(); while(!threeChars.isEmpty()){ ThreeChar thch = threeChars.pop(); if(thch.h > 0 ){ result.append(getHundreds(thch.h)); result.append(' '); } if(thch.d > '0'){ if(thch.d > '1' || (thch.d == '1' && thch.u == '0')) result.append(getDecades(thch.d)); else if(thch.d > '0') result.append(getTeens(thch.d)); result.append(' '); } if(thch.u > '0' && thch.d != '1'){ result.append(getUnits(thch.u, thch.range == Ranges.THOUSANDS)); result.append(' '); } switch(thch.range){ case BILLIONS: if(thch.d == '1' || thch.u == '0') result.append("миллиардов"); else if(thch.u > '4')result.append("миллиардов"); else if(thch.u > '1')result.append("миллиарда"); else result.append("миллиард"); break; case MILLIONS: if(thch.d == '1' || thch.u == '0') result.append("миллионов"); else if(thch.u > '4')result.append("миллионов"); else if(thch.u > '1')result.append("миллиона"); else result.append("миллион"); break; case THOUSANDS: if(thch.d == '1' || thch.u == '0') result.append("тысяч"); else if(thch.u > '4')result.append("тысяч"); else if(thch.u > '1')result.append("тысячи"); else result.append("тысяча"); break; default: if(thch.d == '1' || thch.u == '0' || thch.u > '4')result.append("рублей"); else if(thch.u > '1')result.append("рубля"); else result.append("рубль"); } result.append(' '); } result.append(sa[1] + ' '); switch(sa[1].charAt(1)){ case '1': result.append(sa[1].charAt(0) != '1' ? "копейка" : "копеек"); break; case '2': case '3': case '4': result.append(sa[1].charAt(0) != '1' ? "копейки" : "копеек"); break; default: result.append("копеек"); } char first = Character.toUpperCase(result.charAt(0)); result.setCharAt(0, first); return result.toString(); } private static String getHundreds(char dig){ switch(dig){ case '1': return "сто"; case '2': return "двести"; case '3': return "триста"; case '4': return "четыреста"; case '5': return "пятьсот"; case '6': return "шестсот"; case '7': return "семсот"; case '8': return "восемсот"; case '9': return "девятьсот"; default: return null; } } private static String getDecades(char dig){ switch(dig){ case '1': return "десять"; case '2': return "двадцать"; case '3': return "тридцать"; case '4': return "сорок"; case '5': return "пятьдесят"; case '6': return "шестьдесят"; case '7': return "семьдесят"; case '8': return "восемьдесят"; case '9': return "девяносто"; default: return null; } } private static String getUnits(char dig, boolean female){ switch(dig){ case '1': return female ? "одна" : "один"; case '2': return female ? "две" : "два"; case '3': return "три"; case '4': return "четыре"; case '5': return "пять"; case '6': return "шесть"; case '7': return "семь"; case '8': return "восемь"; case '9': return "девять"; default: return null; } } private static String getTeens(char dig){ String s = ""; switch(dig){ case '1': s = "один"; break; case '2': s = "две"; break; case '3': s = "три"; break; case '4': s = "четыр"; break; case '5': s = "пят"; break; case '6': s = "шест"; break; case '7': s = "сем"; break; case '8': s = "восем"; break; case '9': s = "девят"; break; } return s + "надцать"; } public static void main(String[] args){ System.out.println(new RussianMoney().digits2Text(new Double(args[0]))); } }

Ответ 3



Тема старая, но предлагаю еще один вариант(с рекурсией) : /** * Класс для преобразования double-числа в рубли-копейки прописью * @author Segr88 */ public class MoneyInWords { private static final String dig1[][] = {{"одна", "две", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять"}, {"один", "два"}}; //dig[0] - female, dig[1] - male private static final String dig10[] = {"десять","одиннадцать", "двенадцать", "тринадцать", "четырнадцать", "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать"}; private static final String dig20[] = {"двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят", "восемьдесят", "девяносто"}; private static final String dig100[] = {"сто","двести", "триста", "четыреста", "пятьсот", "шестьсот", "семьсот", "восемьсот", "девятьсот"}; private static final String leword[][] = { {"копейка", "копейки", "копеек", "0"}, {"рубль", "рубля", "рублей", "1"}, {"тысяча", "тысячи", "тысяч", "0"}, {"миллион", "миллиона", "миллионов", "1"}, {"миллиард", "миллиарда", "миллиардов", "1"}, {"триллион", "триллиона", "триллионов", "1"}}; //рекурсивная функция преобразования целого числа num в рубли private static String num2words(long num, int level) { StringBuilder words = new StringBuilder(50); if (num==0) words.append("ноль "); //исключительный случай int sex = leword[level][3].indexOf("1")+1; //не красиво конечно, но работает int h = (int)(num%1000); //текущий трехзначный сегмент int d = h/100; //цифра сотен if (d>0) words.append(dig100[d-1]).append(" "); int n = h%100; d = n/10; //цифра десятков n = n%10; //цифра единиц switch(d) { case 0: break; case 1: words.append(dig10[n]).append(" "); break; default: words.append(dig20[d-2]).append(" "); } if (d==1) n=0; //при двузначном остатке от 10 до 19, цифра едициц не должна учитываться switch(n) { case 0: break; case 1: case 2: words.append(dig1[sex][n-1]).append(" "); break; default: words.append(dig1[0][n-1]).append(" "); } switch(n) { case 1: words.append(leword[level][0]); break; case 2: case 3: case 4: words.append(leword[level][1]); break; default: if((h!=0)||((h==0)&&(level==1))) //если трехзначный сегмент = 0, то добавлять нужно только "рублей" words.append(leword[level][2]); } long nextnum = num/1000; if(nextnum>0) { return (num2words(nextnum, level+1) + " " + words.toString()).trim(); } else { return words.toString().trim(); } } //функция преобразования вещественного числа в рубли-копейки //при значении money более 50-70 триллионов рублей начинает искажать копейки, осторожней при работе такими суммами public static String inwords(double money) { if (money<0.0) return "error: отрицательное значение"; String sm = String.format("%.2f", money); String skop = sm.substring(sm.length()-2, sm.length()); //значение копеек в строке int iw; switch(skop.substring(1)) { case "1": iw = 0; break; case "2": case "3": case "4": iw = 1; break; default: iw = 2; } long num = (long)Math.floor(money); if (num<1000000000000000l) { return num2words(num, 1) + " " + skop + " " + leword[0][iw]; } else return "error: слишком много рублей " + skop + " " + leword[0][iw]; } } Пример использования: System.out.println(MoneyInWords.inwords(67382645834.35)); Вывод: шестьдесят семь миллиардов триста восемьдесят два миллиона шестьсот сорок пять тысяч восемьсот тридцать четыре рубля 35 копеек

Ответ 4



На geektimes есть вариант готовой реализации: import java.util.ArrayList; import java.util.Collections; import java.math.BigDecimal; /** * Класс для работы с деньгами * @author runcore */ public class fwMoney { /** * Сумма денег */ private BigDecimal amount; /** * Конструктор из Long */ public fwMoney(long l) { String s = String.valueOf(l); if (!s.contains(".") ) s += ".0"; this.amount = new BigDecimal( s ); } /** * Конструктор из Double */ public fwMoney(double l) { String s = String.valueOf(l); if (!s.contains(".") ) s += ".0"; this.amount = new BigDecimal( s ); } /** * Конструктор из String */ public fwMoney(String s) { if (!s.contains(".") ) s += ".0"; this.amount = new BigDecimal( s ); } /** * Вернуть сумму как строку */ public String asString() { return amount.toString(); } /** * Вернуть сумму прописью, с точностью до копеек */ public String num2str() { return num2str(false); } /** * Выводим сумму прописью * @param stripkop boolean флаг - показывать копейки или нет * @return String Сумма прописью */ public String num2str(boolean stripkop) { String[][] sex = { {"","один","два","три","четыре","пять","шесть","семь","восемь","девять"}, {"","одна","две","три","четыре","пять","шесть","семь","восемь","девять"}, }; String[] str100= {"","сто","двести","триста","четыреста","пятьсот","шестьсот","семьсот", "восемьсот","девятьсот"}; String[] str11 = {"","десять","одиннадцать","двенадцать","тринадцать","четырнадцать", "пятнадцать","шестнадцать","семнадцать","восемнадцать","девятнадцать","двадцать"}; String[] str10 = {"","десять","двадцать","тридцать","сорок","пятьдесят","шестьдесят", "семьдесят","восемьдесят","девяносто"}; String[][] forms = { {"копейка", "копейки", "копеек", "1"}, {"рубль", "рубля", "рублей", "0"}, {"тысяча", "тысячи", "тысяч", "1"}, {"миллион", "миллиона", "миллионов", "0"}, {"миллиард","миллиарда","миллиардов","0"}, {"триллион","триллиона","триллионов","0"}, // можно добавлять дальше секстиллионы и т.д. }; // получаем отдельно рубли и копейки long rub = amount.longValue(); String[] moi = amount.toString().split("\\."); long kop = Long.valueOf(moi[1]); if (!moi[1].substring( 0,1).equals("0") ){// начинается не с нуля if (kop<10 ) kop *=10; } String kops = String.valueOf(kop); if (kops.length()==1 ) kops = "0"+kops; long rub_tmp = rub; // Разбиватель суммы на сегменты по 3 цифры с конца ArrayList segments = new ArrayList(); while(rub_tmp>999) { long seg = rub_tmp/1000; segments.add( rub_tmp-(seg*1000) ); rub_tmp=seg; } segments.add( rub_tmp ); Collections.reverse(segments); // Анализируем сегменты String o = ""; if (rub== 0) {// если Ноль o = "ноль "+morph( 0, forms[1][ 0],forms[1][1],forms[1][2]); if (stripkop) return o; else return o +" "+kop+" "+morph(kop,forms[ 0][ 0],forms[ 0][1],forms[ 0][2]); } // Больше нуля int lev = segments.size(); for (int i= 0; i1) {// если сегмент ==0 И не последний уровень(там Units) lev--; continue; } String rs = String.valueOf(ri); // число в строку // нормализация if (rs.length()==1) rs = "00"+rs;// два нулика в префикс? if (rs.length()==2) rs = "0"+rs; // или лучше один? // получаем циферки для анализа int r1 = (int)Integer.valueOf( rs.substring( 0,1) ); //первая цифра int r2 = (int)Integer.valueOf( rs.substring(1,2) ); //вторая int r3 = (int)Integer.valueOf( rs.substring(2,3) ); //третья int r22= (int)Integer.valueOf( rs.substring(1,3) ); //вторая и третья // Супер-нано-анализатор циферок if (ri>99) o += str100[r1]+" "; // Сотни if (r22>20) {// >20 o += str10[r2]+" "; o += sex[ sexi ][r3]+" "; } else { // <=20 if (r22>9) o += str11[r22-9]+" "; // 10-20 else o += sex[ sexi ][r3]+" "; // 0-9 } // Единицы измерения (рубли...) o += morph(ri, forms[lev][ 0],forms[lev][1],forms[lev][2])+" "; lev--; } // Копейки в цифровом виде if (stripkop) { o = o.replaceAll(" {2,}", " "); } else { o = o+""+kops+" "+morph(kop,forms[ 0][ 0],forms[ 0][1],forms[ 0][2]); o = o.replaceAll(" {2,}", " "); } return o; } /** * Склоняем словоформу * @param n Long количество объектов * @param f1 String вариант словоформы для одного объекта * @param f2 String вариант словоформы для двух объектов * @param f5 String вариант словоформы для пяти объектов * @return String правильный вариант словоформы для указанного количества объектов */ public static String morph(long n, String f1, String f2, String f5) { n = Math.abs(n) % 100; long n1 = n % 10; if (n > 10 && n < 20) return f5; if (n1 > 1 && n1 < 5) return f2; if (n1 == 1) return f1; return f5; } } Пример использования: fwMoney mo = new fwMoney(«7654321.98»); String money_as_string = mo.num2str(); Результат: "семь миллионов шестьсот пятьдесят четыре тысячи триста двадцать один рубль 98 копеек"

Ответ 5



тоже тренировался и сделал по своему import java.util.Scanner; public class NumberToText { // Перевод числа в диапазоне -999 - 999 в текстовую форму private static int billion; private static int million; private static int thousand; private static int toThousand; private static long numberA; private static long numberMax = 999999999999L ; // private int numericalValue; private static String numText;// число в виде текста //private int index ; private static int indexA; private static int units; // единичные значение private static int decimal; // десятичное значение private static int hundreds; // сотни private static final String[][]sampleText ={ {"","од","дв","три","четыре","пять","шесть","семь","восемь","девять"}, {"", "десять " ,"двадцать ","тридцать ","сорок ","пятьдесят ","шестьдесят ","семьдесят ","восемьдесят ","девяносто "}, {"","сто ","двести ","триста ","четыреста ","пятьсот ","шестьсот ","семьсот ","восемьсот ","девятьсот "} }; private static final String[]sample11to19 = { "десять ", "одинадцать ", "двенадцать ", "тринадцать ", "четырнадцать ","пятнадцать ", "шеснадцать ", "семьнадцать ", "восемьнадцать ", "девятнадцать ", "девятнадцать "} ; private static final String[][] textMillion={{"","","",""}, {"миллиардов ","миллионов ","тысячь ",""}, {"миллиард ","миллион ","тысяча ",""}, {"миллиарда ","миллиона ","тысячи ",""}, {"миллиардов ","миллионов ","тысячь ",""}}; public static String WordsRus (long number) { numberA = number; numText =""; if (numberA < -numberMax || numberA > numberMax ) { return numText = "Число выходит за рамки указанного диапазона";} if (numberA == 0 ) { return numText = "ноль ";} if (number < 0) {numText = "минус "; numberA = -numberA;} //делаем позитивное значение number // разбиваем число на миллиарды,миллионы,тысячи и единицы billion = (int) ( numberA / 1000000000); million = (int) (numberA-(billion*1000000000))/ 1000000 ; thousand = (int) (numberA - (billion*1000000000)-(million*1000000)) / 1000; toThousand = (int)(numberA % 1000) ; // формируем текст числа прописью numText =numText + WordsToThousand (billion , 0)+WordsToThousand (million , 1)+WordsToThousand (thousand , 2)+WordsToThousand (toThousand , 3); return numText ; } private static String WordsToThousand ( int numericalValue , int index ){ //this.numericalValue = numericalValue; //this.index = index; // разбиваем образец числа на составляющие hundreds = numericalValue / 100; decimal = (numericalValue - (hundreds*100) ) / 10; units = numericalValue % 10 ; // формируем число без степени числа numText = ""; if ( decimal == 1 ) numText = sampleText [2] [hundreds] + sample11to19 [units]; else numText = sampleText [2] [hundreds] + sampleText [1][decimal] + sampleText [0] [units]; // формируем окончания в единицах if (index == 2) {if (units == 1 && decimal != 1) numText = numText + "на "; else if (units == 2 & decimal != 1) numText = numText + "е "; if (units > 1 && decimal != 1) numText = numText + " ";} else {if (units == 1 && decimal != 1) numText = numText + "ин "; if (units == 2 & decimal != 1) {numText = numText + "а ";} else if (units != 0 & decimal != 1) numText = numText + " ";} // дописываем степень числа indexA = 0; if (numericalValue != 0 ) { if (units == 0 || decimal == 1 ) indexA = 1; else if (units == 1) indexA = 2; else if (units > 1 & units < 5) indexA = 3; else indexA = 4;} numText = numText + textMillion [indexA][index]; return numText; } } public class Dialog { public static void main(String[] args) { long number;// введенное число Scanner in = new Scanner(System.in); do { System.out.print("Введите целое число в диапазоне -999 999 999 999 до 999 999 999 999, для выхода введите 0: "); number = (long) in.nextDouble(); System.out.println(NumberToText.WordsRus(number)); } while ( number != 0); System.out.println("Введено число /Ноль/ Работа программы завершена"); } }

Как избежать опечатки при выполнении удаления

#linux #bash


Иногда мне необходимо вручную обновить написанные мной программы на своем сервере.
Процесс обновления выглядит следующим образом:


Копирую выполняемый файл на сервер командой scp.
Захожу на сервер через ssh в терминале. 
Перехожу в папку с исполняемым файлом. 
Останавливаю приложение, если необходимо.
Удаляю старую версию программы.
Копирую новую в нужную директорию.
Запускаю обновленное приложение.


Программу удаляю с ресурсными файлами примерно так:

rm -rf ./


Проблема в том, что однажды я опечатался, случайно добавив пробел между точкой и
обратной косой чертой. Соглашусь, что лучше все это автоматизировать, но иногда есть
необходимость сделать что–то подобное руками.

Подскажите, что бы вы сделали, чтобы отловить такую или подобные ошибки и не допустить
выполнение нежелательного удаления?
    


Ответы

Ответ 1



Утилита rm из более новых coreutils такого не допустит: $ rm -rf / rm: it is dangerous to operate recursively on '/' rm: use --no-preserve-root to override this failsafe $ rm -rf . rm: refusing to remove '.' or '..' directory: skipping '.' Проверить man rm: --preserve-root do not remove `/' (default) В этом топике более подробно. Может самое время, запланировать обновление. Чтобы максимально снизить вероятность опечатки, не делайте того, что может к ней привести. Никогда не пользуйтесь неявным указанием путей, таких как ./ или ./*, т.е. как минимум выйти из текущего каталога $ cd ... Всегда проверяйте, что же будет удалено, хотя бы так: $ #rm -rf myfolder /TabTab и если только ничего подозрительного нет, как например: bin/ etc/ proc/ usr/ , то HomeDelEnter.

Ответ 2



Думал, что единственно верным ответом на вопрос будет старый добрый «think before you type», и «дважды подумай, если напечатал rm» — благо, обычно после пары выстрелов в ногу вырабатывается рефлекс стойкой сосредоточенности как и после команд dd if=/dev/zero of=/dev/sdX, mkfs, досовского format c: и т.п. Но вот давеча сам поймал себя на том, что уже давно пользуюсь банальной «корзиной» и mv вместо rm: mkdir -p /tmp/trash mv foo/* /tmp/trash #... mv baz/* /tmp/trash Пара замечаний: Способ хорошо подходит для небольших объёмов не критичных к безопасности данных, а также предохраняет от других опечаток или ошибок. Каталог /tmp сам очищается при следующей загрузке или при отключении (если смонтирован в tmpfs)... Cамо собой, его также можно удалять вручную. Не заставляет играть в «ответь “да” на 1000 вопросов» как rm -i. Не полностью предохраняет от ошибки класса «rm . /*» от root'а, но позволяет всё восстановить парой хитрых команд, если осталась root'овая оболочка... Любители скриптов могут обернуть это в функцию в ~/.bashrc: rms() { mkdir -p /tmp/trash mv -t /tmp/trash -- "$@" } Или даже расширить его до чего-то подобного: rms() { local trash dt trash=/tmp/trash mkdir -p "$trash" for f in "$@"; do if [ ! -e "$trash/$(basename "$f")" ]; then mv -t "$trash" -- "$f" || return else dt="$(date --iso=s)" if [ ! -e "$trash/$(basename "$f")-$dt" ]; then mv -- "$f" "$trash/$(basename "$f")-$dt" || return else mv -- "$f" "$(mktemp -u "$trash/$(basename "$f")-$dt-XXXX")" || return fi fi done } Помимо прочего, если файл с заданным именем в корзине уже существует, этот скриптик попробует скопировать его в файл с текущей датой, а также со случайным именем.* Для любителей всё усложнять также есть консольные менеджеры корзины вроде gio trash, trash-cli и другие. * Параноик скажет, что здесь возможно состояние гонки, если будут работать одновременно несколько экземпляров скрипта, но это всего лишь нудный клачёчек, написанный на коленке за 5 минут.

Ответ 3



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

Ответ 4



Кто про что, а я про функции: remove (){ #Текущий каталог, либо каталог указанный вручную dir=${1:-"$PWD/"} #Запрос подтверждения read -p "Будет удалено `du -sh $dir`, продолжить?: Y/N" -s -n1 rec #Регист ответа res=`sed 's/./\U&/g' <<< $rec` #Проверяем подтверждение if [[ x$res == xY ]]; then #Удаляем rm -rf $dir else echo "Удаление не производилось" fi } Функция записана в bash_profile пользователя. Потом из каталога вызывать remove без ключа - это удалить каталог в котором мы находимся remove /work/all/tt удалить каталог /work/all/tt Это лишь пример, каждый может добавить или изменить под себя.

Ответ 5



Я таки уточнил некоторые детали и теперь могу точно сказать, что имеем классическую ошибку молотка. Соответственно решать надо не как удалять, а как лучше запакетировать, чтобы ничего не сломать. Есть старый, но всё ещё развивающийся проект fpm, который позволяет легко (намного легче, чем писать .spec или шаманить с dh-make) упаковать папку со структурой в deb`rpm` пакет. Я упаковывал готовый бинарник CoreDNS в rpm для CentOS 7, потому что версии. Мой вариант работающий внутри локалки на работе (тавтология, ага): #!/usr/bin/env bash # WORK # LANG=ru_RU.UTF-8 # Собираем ГОТОВЫЙ бинарник coredns в пакет для установки с помощью fpm. Он уже должен быть установлен. # https://github.com/jordansissel/fpm # https://fpm.readthedocs.io/en/latest/installing.html MAIN_SERVER="10.10.10.10" WEB_SERVER="$MAIN_SERVER" WEB_NAME_OF_MODULE_FOR_SOFT="soft" PACKAGE_NAME="coredns" PACKAGE_USER="$PACKAGE_NAME" PACKAGE_GROUP="$PACKAGE_NAME" PACKAGE_VERSION="$(curl http://$WEB_SERVER/$WEB_NAME_OF_MODULE_FOR_SOFT/coredns/latest)" # https://github.com/coredns/coredns/blob/master/LICENSE PACKAGE_LICENSE="Apache License 2.0" PACKAGE_CONFIG_DIR="/etc/coredns" # PACKAGE_HOMEDIR="/var/lib/$PACKAGE_USER" PACKAGE_CONFIG_FILE="Corefile" PACKAGE_ARCHITECTURE="x86_64" PACKAGE_MAINTAINER="don Rumata" # https://github.com/coredns/coredns PACKAGE_DESCRIPTION="CoreDNS is a DNS server/forwarder, written in Go, that chains plugins. Each plugin performs a (DNS) function." PACKAGE_URL="https://github.com/coredns/coredns" PACKAGE_WORKDIR="/tmp/$PACKAGE_NAME" PACKAGE_SCRIPT_BEFORE_INSTALL="$PACKAGE_NAME-before-install.sh" PACKAGE_SCRIPT_AFTER_INSTALL="$PACKAGE_NAME-after-install.sh" PACKAGE_FORMAT="rpm" # Создаём и переходим в папку, где будет твориться магия^W сборка. mkdir -p "$PACKAGE_WORKDIR" && cd "$PACKAGE_WORKDIR" || exit 1 # Для бинарника. mkdir -p ./usr/bin # Для файла настроек. mkdir -p ./"$PACKAGE_CONFIG_DIR" # Для юнита systemd. mkdir -p ./usr/lib/systemd/system # Для временных скриптов, которых пока один. mkdir -p ./"$PACKAGE_WORKDIR" #--- Бинарник ---# cd ./usr/bin wget "http://$WEB_SERVER/$WEB_NAME_OF_MODULE_FOR_SOFT/coredns/coredns_latest_linux_$(arch).tgz" || exit 1 tar -xvf coredns_latest_linux_x86_64.tgz rm coredns_latest_linux_x86_64.tgz cd "$PACKAGE_WORKDIR" #----------------# #--- Создаём скрипт, который будет создавать нам ограниченную системную учётку. ---# cd ./"$PACKAGE_WORKDIR" cat > "$PACKAGE_SCRIPT_BEFORE_INSTALL" << EOF #! /bin/bash adduser --system --shell /sbin/nologin --comment 'CoreDNS user' $PACKAGE_USER # --home-dir $PACKAGE_HOMEDIR groupadd $PACKAGE_GROUP usermod -g $PACKAGE_GROUP $PACKAGE_USER # mkdir $PACKAGE_HOMEDIR # chown $PACKAGE_USER:$PACKAGE_GROUP $PACKAGE_HOMEDIR # chmod 775 $PACKAGE_HOMEDIR EOF chmod +x "$PACKAGE_SCRIPT_BEFORE_INSTALL" cd "$PACKAGE_WORKDIR" #----------------------------------------------------------------------------------# #--- Создаём скрипт, который будет конфигурить сервис сразу после его установки. ---# cd ./"$PACKAGE_WORKDIR" cat > "$PACKAGE_SCRIPT_AFTER_INSTALL" << EOF #!/usr/bin/env bash systemctl enable coredns.service systemctl start coredns.service EOF chmod +x "$PACKAGE_SCRIPT_AFTER_INSTALL" cd "$PACKAGE_WORKDIR" #---------------------------------------------------------------------------------------# #--- Простейший пример конфига, который работает (проверял на 7-м Центе). Да. Начинается с ".:53". ---# cd ./"$PACKAGE_CONFIG_DIR" cat > "$PACKAGE_CONFIG_FILE" << EOF .:53 { forward . 8.8.8.8 1.1.1.1 { } cache { success 5000 denial 2500 } log . {combined} { class denial error } errors cancel } EOF cd "$PACKAGE_WORKDIR" #-----------------------------------------------------------------------------------------------------# #--- SystemD Unit ---# cd ./usr/lib/systemd/system wget --output-document coredns.service "http://$WEB_SERVER/$WEB_NAME_OF_MODULE_FOR_SOFT/coredns/coredns.service" || exit 1 cd "$PACKAGE_WORKDIR" #--------------------# #--- BUILD ---# cd "$PACKAGE_WORKDIR" # Внимание на "./", потому что относительные пути очень важны! # https://www.debian.org/doc/manuals/maint-guide/dother.ru.html#conffiles fpm --force \ --name "$PACKAGE_NAME" \ --version "$PACKAGE_VERSION" \ --license "$PACKAGE_LICENSE" \ --config-files ./"$PACKAGE_CONFIG_DIR"/"$PACKAGE_CONFIG_FILE" \ --architecture "$PACKAGE_ARCHITECTURE" \ --maintainer "$PACKAGE_MAINTAINER" \ --description "$PACKAGE_DESCRIPTION" \ --url "$PACKAGE_URL" \ --before-install ./"$PACKAGE_WORKDIR"/"$PACKAGE_SCRIPT_BEFORE_INSTALL" \ --after-install ./"$PACKAGE_WORKDIR"/"$PACKAGE_SCRIPT_AFTER_INSTALL" \ --no-depends \ --no-auto-depends \ --input-type dir --output-type "$PACKAGE_FORMAT" ./ #-------------# Копия на gist. Дальше можно уже прикручивать aptly и createrepo.