Page 3 of 8

Posted: Sat Dec 25, 2010 12:18 am
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; });
  }
}

Posted: Sat Dec 25, 2010 8:02 am
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 :)

Posted: Sat Dec 25, 2010 4:09 pm
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.

Posted: Sun Dec 26, 2010 9:37 pm
by Fr0sT
KDJ
nice, respect! :)

Posted: Mon Dec 27, 2010 10:52 am
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 трех недель не достаточно.

Posted: Mon Dec 27, 2010 7:55 pm
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);

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

Posted: Wed Dec 29, 2010 6:39 pm
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.

Posted: Sat Jan 01, 2011 10:13 pm
by Infocatcher
Для функций, работающих со строками, напрашивается обобщающий диалог с кнопками и галочкой «Учитывать регистр символов». Что-то вроде вот такого:

Code: Select all

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

Posted: Sun Jan 02, 2011 12:41 pm
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

Posted: Sun Jan 02, 2011 9:06 pm
by KDJ
I already found the answer to above question.
Can use the system function "kernel32::lstrcmp" to compare strings.

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

Posted: Mon Jan 10, 2011 9:08 pm
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.

Posted: Mon Jan 10, 2011 9:29 pm
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.

Posted: Sat Jan 15, 2011 8:25 pm
by KDJ
Infocatcher
Maybe something like this:

Image