Тема: AHK: Cумма прописью (в любом окне)
Классический бухгалтерский функционал: По Ctrl+Shift+W добавляет к выделенной сумме в числовом виде строку, описывающую эту сумму прописью в рублях и копейках. Копейки можно выводить как прописью так и числом (с лидирующим нулём при необходимости).
Работает в окнах любых приложений (как локальных, так и опубликованных через Citrix). Проверен на Win7 и Win10.
; Сумма прописью
^+vk57:: ; {Ctrl Shift W}
tmp := ClipboardAll
Clipboard := ""
Send, ^{Ins}
ClipWait, , 1
ctext := Clipboard ; очищаем текст от форматирования
;Извлекаем число из выделенного текста, например: "ИТОГО: 2 431 234 012, 31 руб."
ctext := RegExReplace(ctext,"=|,", ".")
ctext := RegExReplace(ctext,"[^\d\.]")
ctext := RegExReplace(ctext,"\.$")
If (RegExMatch(ctext,"\d+")) {
Clipboard := " (" . Sum2Words(ctext, 2) . ")"
Send, {Right}
Sleep 200
Send, +{Ins}
Sleep 200
}
Clipboard := tmp
Return
;Использован прототип (VBA) http://www.script-coding.com/SumInWords.zip
;Функция возвращает сумму прописью в указанной валюте.
;Первый аргумент - число (сумма).
;Второй аргумент - формат вывода копеек:
;1 - копейки числом,
;2 - копейки числом с лидирующим нулём при необходимости,
;любое другое число - копейки прописью.
Sum2Words(sum, kop) {
;Округление до двух знаков после запятой
sum := Round(Abs(sum), 2)
words := []
;Миллиард
num := Floor(sum/1000000000)
If (num > 0) {
words.Push(Rest2Words(num, True))
words.Push(["миллиардов","миллиарда","миллиард"][CaseOfWord(num)])
}
sum -= num*1000000000
;Миллион
num := Floor(sum/1000000)
If (num > 0) {
words.Push(Rest2Words(num, True))
words.Push(["миллионов","миллиона","миллион"][CaseOfWord(num)])
}
sum -= num*1000000
;Тысяча
num := Floor(sum/1000)
If (num > 0) {
words.Push(Rest2Words(num, False))
words.Push(["тысяч","тысячи","тысяча"][CaseOfWord(num)])
}
sum -= num*1000
;Рублей
num := Floor(sum)
If (num > 0) {
words.Push(Rest2Words(num, True))
words.Push(["рублей","рубля","рубль"][CaseOfWord(num)])
}
sum -= num
;Копеек
num := Round(sum*100)
If (kop = 1) { ; числом
words.Push(num)
}
Else If (kop = 2) { ; числом с лидирующим 0
StringRight, num, % "0" . num, 2
words.Push(num)
}
Else { ; прописью
words.Push(Rest2Words(num, False))
}
words.Push(["копеек","копейки","копейка"][CaseOfWord(num)])
; Склеиваем все слова в одну строку
words := Join(words, " ")
;С заглавной буквы
StringLeft, firstChar, words, 1
StringUpper, firstChar, firstChar
StringRight, otherChars, words, StrLen(words)-1
Return firstChar . otherChars
}
;Функция возвращает индекс в массиве названий разряда числа.
;Аргумент - целое положительное число, меньшее тысячи.
CaseOfWord(num){
StringRight, tens, num, 2 ;Десятки (два последних знака)
If (tens < 11 Or tens > 19) {
StringRight, unitys, num, 1 ;Единицы (один последний знак)
If unitys = 1
Return 3
Else If unitys between 2 and 4
Return 2
}
Return 1
}
;Функция возвращает число прописью.
;Первый аргумент - целое положительное число, меньшее тысячи.
;Второй аргумент - род числа (True - мужской, False - женский, для тысяч и копеек).
Rest2Words(rest, gender){
rest := Abs(rest)
If ((rest <= 0) Or (rest > 999)) {
Return "ноль"
}
words := []
;Сотни
num := Floor(rest / 100)
If (num > 0) {
words.Push(["сто","двести","триста","четыреста","пятьсот","шестьсот","семьсот","восемьсот","девятьсот"][num])
}
rest -= num*100
;Десятки
num := Floor(rest/10)
If (num > 1) {
words.Push(["двадцать","тридцать","сорок","пятьдесят","шестьдесят","семьдесят","восемьдесят","девяносто"][num-1])
rest -= num*10
}
Else If (num = 1) {
words.Push(["десять","одиннадцать","двенадцать","тринадцать","четырнадцать","пятнадцать","шестнадцать","семнадцать","восемнадцать","девятнадцать"][rest-9])
rest := 0
}
;Единицы
If (rest > 0) {
If (rest = 1) {
words.Push(gender ? "один" : "одна")
}
Else If (rest = 2) {
words.Push(gender ? "два" : "две")
}
Else {
words.Push(["три","четыре","пять","шесть","семь","восемь","девять"][rest-2])
}
}
Return Join(words, " ")
}
; Возвращает склеенный в одну строку массив Array (с использованием разделителя Sep)
Join(array, sep) {
for k, v in array
out .= sep . v
Return SubStr(out, 1+StrLen(sep))
}
В AHK я - новичок. Не понимаю недоделанности некоторых общеупотребительных операторов, например, того же If. Конечно, привыкнуть к этому можно, но такие несуразности бросаются в глаза и создают не решаемые логическим путем глюки...
В общем, буду очень благодарен за советы и рекомендации по скрипту.
Интересует не сколько расширение функциональности, сколько правильность и корректность использованных функций, рефакторинг кода.
2All
Пожалуйста, обратите внимание на начальный маленький блок получения и вставки текста. Нет замечаний? Просто мне кажется это - единственно правильный подход. Все другие (в т.ч. WinAPI) не универсальны (не со всеми окнами работать будут).
Может быть кто придумает как определить нередактируемый текст. Ну чтобы если такое случается, результат вычислений не в текст вставлять, а в MsgBox выводить. Вариант с вырезанием выделенного текста в буфер (если прокатило - значит - редактируемый) и последующей его вставкой знаю, но не нравится, поскольку надо добавлять кучу доп.кода чтобы содержимое буфера обмена не перекорежило.