Format plugin

Discuss and announce AkelPad plugins
  • Author
  • Message
KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

Andrey_A_A
To sort the file by line length, you can use the following code in JS:

Code: Select all

if (! AkelPad.GetEditWnd()) WScript.Quit();

AkelPad.SetSel(0, -1);

var bDesc = 0;
var pTxt  = AkelPad.GetSelText(1 /*\r*/);
var aArr  = pTxt.split("\r");

ArrayTextSortByLength(aArr, bDesc);

pTxt = aArr.join("\r");

AkelPad.ReplaceSel(pTxt);

//////////////////////////////
function ArrayTextSortByLength(aArr, bDescending)
{
  if (bDescending)
  {
    aArr.sort(function(a, b) {
      if      (a.length < b.length) return 1;
      else if (a.length > b.length) return -1;
      else return 0; });
  }
  else
  {
    aArr.sort(function(a, b) {
      if      (a.length < b.length) return -1;
      else if (a.length > b.length) return 1;
      else return 0; });
  }
}

Offline
Posts: 876
Joined: Tue Jul 24, 2007 8:54 am

Post by Fr0sT »

KDJ, simple and useful, but it'll take 2*charcount*sizeof(widechar) of RAM during execution if I get things right. May be found out too hard for the system :)

KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

Fr0sT wrote:...but it'll take 2*charcount*sizeof(widechar) of RAM during execution...
I did a test sort lines. On the file 35 MB, about 2.7 million lines.
Using Format plugin, script and UltraEdit.
Here are the results:

Code: Select all

                  max. usage memory     sort time
AkelPad + plugin        600 MB             40 sec
AkelPad + script        600 MB            210 sec
UltraEdit               890 MB            110 min (6600 sec)
So if the script is not so bad.

Offline
Posts: 876
Joined: Tue Jul 24, 2007 8:54 am

Post by Fr0sT »

KDJ
nice, respect! :)

Offline
Posts: 874
Joined: Sat Jan 16, 2010 2:03 pm

Post by opk44 »

Andrey_A_A wrote:В алелпаде большинство написано на js, а к нему привыкнуть и понять надо...
Прошу прощения, если дополнение покажется излишним (с учетом ответа от KDJ) но вдруг тема еще не исчерпана.
1) Отличия JS от VBS не так и велики - надо только точку с запятой в конце строки ставить (шутка). JS требует чуть больше внимательности, т.к. он регистрозависимый.
2) Оговорюсь, что мой "пример" пригоден для обработки НЕюникодных файлов. Но если это не проблема, то здесь можно обойтись вообще без AkelPad.

Я попробую показать, как "пользователь" "пользователю" (где "пользователь" = непрограммист с остаточными знаниями по школьной информатике и при наличии справки), как решить задачу путем "рассуждений":
Вариант 1. "наглядный".
Он вытекает из следующей посылки "а что если сделать все строки одинаковой длины и свести задачу сортировки к тривиальной?". Для этого нужно выровнять строки по длине, отсортировать, и затем вернуть строки к первоначальному виду (до выравнивания).
1) Исходный файл читается дважды. При первом чтении выясняется максимальная длина строки в файле.
2) Читаем файл повторно и генерируем выходной файл, в котором каждая строка (взятая из исходного файла) дополняется спереди пробелами до длины максимальной строки - получаем этакую огромную прямоугольную "простыню" с текстом выравненным по правому краю.
Внимание! Есть опасность того, что генерируемый файл окажется очень большого размера (если исходный файл содержит очень много коротких строк и очень длинную максимальную строку). Т.е. на входе имеем файл, размер которого - сумма длин его строк, а на выходе -файл, размер которого - произведение длины максимальной строки на количество строк.
3) Сортируем сгенерированный файл.
Получаем что-то вроде
(для наглядности пробелы заменены точками)

Code: Select all

.........ff
.........ff
.......ffff
.......ffff
......ffffa
......fffff
......fffff
.....aaaaaa
.....aaaaab
.....ffffff
.....ffffff
fffffffffff

4) Отсекаем лидирующие пробелы (прижимаем текст к левой границе).
Внимание! Если исходный файл содержит значимые пробелы в начале строк, то они также будут потеряны. Чтобы этого не происходило, можно предварительно добавлять в начало каждой строки некий символ-маркер (например '#') и только затем пробелы. При отсечке же удалять пробелы до маркера и сам маркер.
Подход примитивный и затратный. Много минусов. Один плюс - наглядный (как мне кажется). Собственно, только ради этого он и приведен.
Взгляните на картинку с "отсортированным файлом". Начальные пробелы являются просто частью ключа сортировки. Очевидное неудобство - эта часть разной длины. ЭТО плюс слово "ключ" и подводит нас к варианту номер два.
Вариант 2.
Можно улучшить предыдущий вариант и избавиться от его недостатков, сделав его не только менее ресурсоемким но и более простым в кодировании, если совместить с использованным ранее (при сортировке по длине строк) методом (универсальность). Для этого нужно доработать фрагмент кода

Code: Select all

	TXT_SIZE = TXT.length;	// получить длину строки
А именно, преобразовывать числа в строки ФИКСИРОВАННОЙ ширины (1 -> '00000001', 2654 -> '00002654', ...). Тогда Вы получите возможность сортировки такой, какой хотели.

Code: Select all

while (!file_in.atEndOfStream)
{
	TXT = file_in.ReadLine();
	TXT_SIZE = TXT.length;	// получить длину строки
	TXT_SIZE = TXT_SIZE.toString(10);	// получить строковое представление числа в 10-тичной системе
//	дополнить нулями слева до восьми** символов ('123' --> '00000123'). 
	while (TXT_SIZE.length < 8)
	{
		TXT_SIZE = '0' + TXT_SIZE;
	}
//	записать в генерируемый файл
	file_out.WriteLine(TXT_SIZE + '\t' + TXT);
}
// **Примечание: 8 выбрано для удобства, как число, обычно равное, либо кратное размеру табуляции,
//   а не для обработки строк до 100 миллионов символов ;)


Здесь уже можно остановиться и подумать "а можно ли не загружать файл в AkelPad для сортировки?".
Чтобы избавиться от необходимости загружать полученный файл в редактор, сортировать его и затем выделять и отрезАть левый блок с ключами, Вы можете передоверить и всю эту работу скрипту.
Так, сортировку НЕюникодного файла можно поручить системной утилите SORT (см. справку по командной строке в Windows).
Если предполагается наличие в файле строк длиннее 4096, то с параметром "/rec символы" примерно так:
SORT /rec 65535 d:\WORK\notes_NUM.txt /o d:\WORK\notes_NUM_SORT.txt
для обратного порядка, с ключом "/r":
SORT /r /rec 65535 d:\WORK\notes_NUM.txt /o d:\WORK\notes_NUM_SORT.txt

Запуск же самой команды, см. справку Windows Script Host по методам RUN или EXEC.

Например

Code: Select all

var WshShell = WScript.CreateObject ("WScript.shell");
// запуск в скрытом окне командной строки, и выполнение сортировки с ожиданием завершения ее работы.
WshShell.run ('cmd.exe /c "SORT /rec 65535 d:\\WORK\\notes_NUM.txt /o d:\\WORK\\notes_NUM_SORT.txt"', 0, "TRUE");
// собщение о выполнении сортировки (само закроется через 5 секунд)
WshShell.Popup ("Сортировка завершена. Создан отсортированный файл.", 5,"Сообщение от скрипта",0);
или так:

Code: Select all

//var WshShell = new ActiveXObject("WScript.Shell");
//var oExec = WshShell.Exec("SORT /rec 65535 d:\\WORK\\notes_NUM.txt /o d:\\WORK\\notes_NUM_SORT.txt");
//
//while (oExec.Status == 0)
//{
//     WScript.Sleep(1000);
//}
//WshShell.Popup ("Сортировка завершена. Создан отсортированный файл.", 5,"Сообщение от скрипта",0);

(для JS не забывайте "удваивать слеши" в путях и "закавычивать строки").
Для удаления в отсортированном файле колонки с ключами подойдет метод substr (для JScript, а для VBScript функция Right)
stringvar.substr(start [, length ])
"Выкусываем" 8 символов ключа + 1 символ табулятора.
Поскольку отсчет индексов ведется с нуля, то стартовая позиция (одна и та же) = 9, а раз нужен весь "хвост" строки, то второй параметр опускаем.
Таким образом TXT.substr(9) даст то, что нужно.
Кусок кода записи в файл:
TXT = file_in.ReadLine();
file_out.WriteLine(TXT.substr(9));

Это все. Осталось только собрать все вместе и добавить, если хотите, интерактивности (чтобы не модифицировать скрипт всякий раз, когда меняется имя файла или потребуется изменить порядок сортировки). А также предусмотреть обработку промежуточных файлов. Итоговый скрипт будет никак не привязан к AkelPad, что тоже неплохо.
P.S.
Не за чем "невротизировать себя недостижимой целью". Стремление к изучению хороших скриптов похвально - я от этих слов не отказываюсь и не вкладываю в них никакого сарказма, только это не годится для "первых шагов". Стремясь сделать с первого раза "идеально", можно вообще никогда не начать (всегда будет казаться, что нужно "еще что-нибудь почитать"). Начните с примитивных и корявых решений. Это лучше чем ничего.
Оценивая же итоговые затраты, берусь утверждать, что для самостоятельного написания первого СВОЕГО скрипта ТАКОГО уровня, Вам потребуется не более 2-3 дней на ознакомление с JS либо VBS и еще пол-дня на собственно работу по "программированию" и тестированию. Для скриптов уровня KDJ трех недель не достаточно.

KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

Maybe this script will solve the problem.

Code: Select all

// Lines sort by length and string
// Call("Scripts::Main", 1, "LinesSortByLengthAndString.js")
// The script sorts all lines by two keys:
// key 1 - line length
// key 2 - string containing entire line

if (! AkelPad.GetEditWnd()) WScript.Quit();

AkelPad.SetSel(0, -1);

var bDesc1 = 0; // = 1 if key 1 descending
var bDesc2 = 0; // = 1 if key 2 descending
var pTxt   = AkelPad.GetSelText(1 /*\r*/);
var lpArr  = pTxt.split("\r");

lpArr.sort(function(a, b) {
  if      (a.length < b.length) return (bDesc1 ?  1 : -1);
  else if (a.length > b.length) return (bDesc1 ? -1 :  1);
  else if (a < b) return (bDesc2 ?  1 : -1);
  else if (a > b) return (bDesc2 ? -1 :  1);
  else return 0; });

pTxt = lpArr.join("\r");

AkelPad.ReplaceSel(pTxt);

Offline
Posts: 670
Joined: Thu Jun 03, 2010 8:47 am
Location: Сочи, Хоста
Contact:

Post by Andrey_A_A »

Спасибо, всё шикарно работает, добавил
var nResult=AkelPad.Call("Format::LineRemoveDuplicates");
перед AkelPad.SetSel(0, -1);
теперь сразу удаляет дубликаты и сортирует!!!

KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

Andrey_A_A wrote:...добавил
var nResult=AkelPad.Call("Format::LineRemoveDuplicates");
перед AkelPad.SetSel(0, -1);
AkelPad.Call(Format::LineRemoveDuplicates") should be placed after AkelPad.SetSel(0, -1);
This will ensure, that the removal of duplicates and sorting is performed on the same range. In this case, the entire text.

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

Для функций, работающих со строками, напрашивается обобщающий диалог с кнопками и галочкой «Учитывать регистр символов». Что-то вроде вот такого:

Code: Select all

 - Сортировать ---------------------------------------------------------
| ([Иконка] По возрастанию          )  ([Иконка] По убыванию          ) |
| ([Иконка] По числовому возрастанию)  ([Иконка] По числовому убыванию) |
| ([Иконка] Инвертировать порядок   )                                   |
 -----------------------------------------------------------------------
 - Дублирующиеся строки ------------------------------------------------
| ([Иконка] Получить дублирующиеся  )  ([Иконка] Удалить дублирующиеся) |
| ([Иконка] Получить уникальные     )                                   |
 -----------------------------------------------------------------------
[+] С учётом регистра
А скрипт делать лень. :P
Тем более, что сейчас одновременно может быть запущен только один скрипт.

KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

I have a question.
How do I sort of regard for local alphabet, in a script?
For example, normal sort gives the result: ć > z.
And it should be: c < ć < d < z

KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

I already found the answer to above question.
Can use the system function "kernel32::lstrcmp" to compare strings.

KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

Infocatcher wrote:Для функций, работающих со строками, напрашивается обобщающий диалог с кнопками и галочкой «Учитывать регистр символов».
...
А скрипт делать лень.
But it's all in Format plugin.
You can do it yourself in ContexMenu plugin.

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

KDJ wrote:You can do it yourself in ContexMenu plugin.
It's very huge because of combinations with «case sensitive» option on and off.

KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

Infocatcher wrote:It's very huge because of combinations with «case sensitive» option on and off.
Well, actually in Format plugin is not option "case sensitive".
May partially solve the problem ColumnsSort.js script.

KDJ
Offline
Posts: 1949
Joined: Sat Mar 06, 2010 7:40 pm
Location: Poland

Post by KDJ »

Infocatcher
Maybe something like this:

Image
Post Reply