<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[Серый форум &mdash; JScript: расширенная функция поиска и фильтрации файлов по шаблону]]></title>
	<link rel="self" href="https://forum.script-coding.com/extern.php?action=feed&amp;tid=5921&amp;type=atom" />
	<updated>2011-07-16T14:26:43Z</updated>
	<generator>PunBB</generator>
	<id>https://forum.script-coding.com/viewtopic.php?id=5921</id>
		<entry>
			<title type="html"><![CDATA[Re: JScript: расширенная функция поиска и фильтрации файлов по шаблону]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=49908#p49908" />
			<content type="html"><![CDATA[<div class="quotebox"><cite>JSman пишет:</cite><blockquote><p>... нужен каждый элемент массива, но только в подходящее время и для определенной цели...</p></blockquote></div><p>Очень ценное замечание. Действительно, если каждый элемент необходим только здесь и сейчас, то незачем держать память под массив. Модифицировал код с учетом это. Также научил обрабатывать несколько путей поиска. Далее - описание и код функции. <br /></p><div class="quotebox"><blockquote><p>Функция понимает следующие параметры, задаваемые в виде объекта-списка ключей:<br />-- path - строка/массив строк определяет каталоги, в которых будет произведен поиск<br />-- pattern - строка/массив строк определяет шаблоны поиска<br />-- included - строка/массив строк определяет шаблоны для фильтрации требуемых файлов/каталогов<br />-- excluded - строка/массив строк определяет шаблоны для &quot;отбраковки&quot; ненужный файлов/каталогов<br />-- filter - функция, вызываемая для настраиваемой фильтрации файлов/каталогов. Функция принимает имя файла и возвращает <strong>true</strong> если файл следует сохранить в результирующем файле, иначе - <strong>false</strong><br />-- each - функция используется для выполнения заданных действий для каждого найденного объекта. <br />-- folders - логическое, задает поиск каталогов вместо файлов<br />-- recursive -- логическое, задает поиск в глубину для всех подкаталогов данного каталога<br />-- codepage - строка, при необходимости задает другую кодовую страницу для команды DOS &#039;CHCP&#039;. Идея заимствована из поста #12 коллеги <strong>wisgest</strong> в теме <a href="http://forum.script-coding.com/viewtopic.php?pid=31277#p31277">JS: Быстрый поиск файлов с заданными расширениями</a></p><p>Все параметры необязательны. При вызове функции без параметров по умолчанию производится поиск всех файлов в текущем каталоге. По умолчанию функция возвращает массив найденных файлов/каталогов. Если будет указан параметр each, то будет вызвана эта функция, заданная параметров each для каждого найденного элемента. А функция <strong>FileSystem.find</strong> вернет количество найденных элементов. При реализации функции использовались некоторые идеи из вики-статьи <a href="http://en.wikipedia.org/wiki/Glob_(programming)">http://en.wikipedia.org/wiki/Glob_(programming)</a>.</p></blockquote></div><div class="codebox"><pre><code>
var FileSystem = FileSystem || {};

FileSystem.find = function(options)
{
	// Process a list of paths
	if ( Object.prototype.toString.call(options.path) == &#039;[object Array]&#039; ) {
		var opts = {};
		for (var p in options) {
			if ( ! options.hasOwnProperty(p) ) {
				continue;
			}
			opts[p] = options[p];
		}

		// Prepare the storage of commands for debugging reasons
		arguments.callee.cmd = [];

		// Collect all results for each path
		var result;

		var each;
		if ( typeof options.each == &#039;function&#039; ) {
			result = 0;
			each = function(v)
			{
				result += v;
			};
		} else {
			result = [];
			each = function(v)
			{
				result = result.concat(v);
			};
		}

		for (var i = 0; i &lt; options.path.length; i++) {
			opts.path = options.path[i];
			each(arguments.callee(opts));
		}
		return result;
	}

	options = options || {};

	// Normalize a path
	var path = (new ActiveXObject(&#039;Scripting.FileSystemObject&#039;)).GetAbsolutePathName(options.path);
	if ( path.slice(-2) != &#039;:\\&#039; ) {
		path += &#039;\\&#039;;
	}

	// Set auxiliary parameters:
	// f - find folders only
	var f;
	if ( options.folders ) {
		f = &#039; /ad &#039;;
	} else {
		f = &#039; /a-d &#039;;
	}

	// p - path to an object of FS
	// s - recursive search
	// b - left border of a pattern
	var p;
	var s;
	var b;
	if ( options.recursive ) {
		p = &#039;&#039;;
		s = &#039; /s &#039;;
		b = &#039; \\\\&#039;;
	} else {
		p = path;
		s = &#039;&#039;;
		b = &#039; ^&#039;;
	}

	// Construct the shell command that would be executed
	var cmd = &#039;&#039;;
	if ( options.codepage ) {
		cmd += &#039;cmd /c chcp &#039; + options.codepage + &#039; &amp;&amp; &#039;;
	}
	cmd += &#039;cmd /c dir /b &#039; + f + s + &#039;&quot;&#039; + path + [].concat(options.pattern || &#039;*&#039;).join(&#039;&quot; &quot;&#039; + path) + &#039;&quot;&#039;;
	if ( options.included ) {
		cmd += &#039; | findstr /i /e &quot;&#039; + b + FileSystem.wildcard2regex(options.included, true, true).join(b) + &#039;&quot;&#039;;
	}
	if ( options.excluded ) {
		cmd += &#039; | findstr /v /i /e &quot;&#039; + b + FileSystem.wildcard2regex(options.excluded, true, true).join(b) + &#039;&quot;&#039;;
	}

	// Store the command for debugging reasons
	if ( Object.prototype.toString.call(arguments.callee.cmd) == &#039;[object Array]&#039; ) {
		arguments.callee.cmd.push(cmd);
	} else {
		arguments.callee.cmd = cmd;
	}

	// Perform the shell command ...
	var sh = new ActiveXObject(&#039;WScript.Shell&#039;);
	var ex = sh.Exec(cmd);

	// ... and collect each string from the output to the resulting array
	var result;

	var each;
	if ( typeof options.each == &#039;function&#039; ) {
		result = 0;
		each = function(v)
		{
			result++;
			options.each(v);
		};
	}else {
		result = [];
		each = function(v)
		{
			result.push(v);
		};
	}

	var filter;
	if ( typeof options.filter == &#039;function&#039; ) {
		filter = function(v)
		{
			if ( options.filter(v, options) ) {
				each(v);
			}
		};
	} else {
		filter = each;
	}

	while ( ex.Status == 0 ) {
		if ( ex.StdOut.AtEndOfStream ) {
			break;
		}
		filter(p + ex.StdOut.ReadLine());
	}
	while ( ! ex.StdOut.AtEndOfStream ) {
		filter(p + ex.StdOut.ReadLine());
	}

	return result;
};

FileSystem.wildcard2regex = function(wildcard, skipRegexp, strictly)
{
	var convert = arguments.callee[strictly ? &#039;strictly&#039; : &#039;std&#039;];

	var result = [];
	if ( Object.prototype.toString.call(wildcard) == &#039;[object String]&#039; ) {
		result.push(convert(wildcard));
	} else {
		for (var i = 0; i &lt; wildcard.length; i++) {
			result.push(convert(wildcard[i]));
		}
	}

	return skipRegexp 
		? result 
		: new RegExp(&#039;^(&#039; + result.join(&#039;|&#039;) + &#039;)$&#039;, &#039;i&#039;);
};

FileSystem.wildcard2regex.strictly = function(wildcard)
{
	var result = wildcard
		.replace(/([\^\$\\\/\|\.\+\!\[\]\(\)\{\}])/g, &#039;\\$1&#039;)
		.replace(/\?/g, &#039;[^\\\\]?&#039;)
		.replace(/\*/g, &#039;[^\\\\]*&#039;)
		;
	return result;
};

FileSystem.wildcard2regex.std = function(wildcard)
{
	var result = wildcard
		.replace(/([\^\$\\\/\|\.\+\!\[\]\(\)\{\}])/g, &#039;\\$1&#039;)
		.replace(/\?/g, &#039;.?&#039;)
		.replace(/\*/g, &#039;.*?&#039;)
		;
	return result;
};</code></pre></div><p>Примеры использования 1-3 - <a href="http://forum.script-coding.com/viewtopic.php?pid=49208#p49208">в первом сообщении темы</a>. Здесь - примеры, показывающие новые возможности</p><p>4. Поиск всех файлов z* во всех подкаталогах в заданном каталоге C:\Windows\System32 и вывод сообщения &quot;Найден файл: ТАКОЙ-ТО&quot;<br /></p><div class="codebox"><pre><code>
var options = {
	// путь поиска
	path: &#039;C:\\Windows\\System32&#039;, 

	// искать во всех подкаталогах
	recursive: true, 
 
	// только файлы, имена которых соответствуют шаблону z*
	filter: function(filename) { return filename.match(/\\z[^\\]*$/i); }, 

	// для каждого найденного файла вывести сообщение
	each: function(filename) { WScript.Echo(&#039;Найден файл: &#039; + filename); }
};

var f = FileSystem.find(options);

// вывести количество найденных файлов
WScript.Echo(&#039;Найдено файлов: &#039; + f);</code></pre></div>]]></content>
			<author>
				<name><![CDATA[Rumata]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24846</uri>
			</author>
			<updated>2011-07-16T14:26:43Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=49908#p49908</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: JScript: расширенная функция поиска и фильтрации файлов по шаблону]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=49639#p49639" />
			<content type="html"><![CDATA[<div class="quotebox"><blockquote><p>4. Видимо может возникнуть проблема читабельности полученного текста в случае использования другой кодовой страницы (см. описание параметра codepage в начале поста)</p></blockquote></div><p>Кстати, кроме 1251 можно использовать 65001 (utf)</p>]]></content>
			<author>
				<name><![CDATA[JSmаn]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24434</uri>
			</author>
			<updated>2011-07-04T07:38:53Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=49639#p49639</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: JScript: расширенная функция поиска и фильтрации файлов по шаблону]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=49514#p49514" />
			<content type="html"><![CDATA[<p><strong>JSman</strong><br />Совершенно верно - ограничение в 4КБ наблюдается только при работе с потоком. Callback предусмотрен в коде данной функции. Возможно, для большей гибкости, имеет смысл модифицировать код так чтобы предложенная функция поиска возвращала массив или обрабатывала &quot;каждый элемент массива, но только в подходящее время и для определенной цели&quot; без сохранения в массив.</p><div class="quotebox"><cite>JSman пишет:</cite><blockquote><p>Более лаконично все-таки, не правда ли?</p></blockquote></div><p>&quot;Лаконично&quot; - вызов основной функции? Да. Но это хорошо до тех пор пока количество аргументов не более 3-4. При большем количестве передаваемых параметров, передаваемых через отдельные аргументы функции, теряется лаконичность.</p>]]></content>
			<author>
				<name><![CDATA[Rumata]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24846</uri>
			</author>
			<updated>2011-06-29T05:57:01Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=49514#p49514</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: JScript: расширенная функция поиска и фильтрации файлов по шаблону]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=49497#p49497" />
			<content type="html"><![CDATA[<div class="quotebox"><blockquote><p>ограничение буфера вывода в 4 КБ</p></blockquote></div><p>При сохранении вывода в файл ограничения не заметил.</p><p>Вообще, говоря о коде, представленном выше, скажу, что у меня лично идеология другая в настоящий момент. Не устраивает возврат массива строк. Так, недавно работав над одним проектом, где много строчек кода, у меня начало появляться сообщение, что Wscript не хватает памяти (раньше не встречал такого:-) ). Слишком много памяти было захламлено массивом строк. Я подумал, что просто хранить такую кучу в оперативке не имеет смысла, так как мне нужен каждый элемент массива, но только в подходящее время и для определенной цели. Решением проблемы явилось использование замыканий:<br /></p><div class="codebox"><pre><code>function Callback(f)
{
    if (/abc/.test(f)) OperateWithFile(f);
}

FindFiles(What, isDeep, Callback);</code></pre></div><p>Более лаконично все-таки, не правда ли?</p>]]></content>
			<author>
				<name><![CDATA[JSmаn]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24434</uri>
			</author>
			<updated>2011-06-28T18:36:06Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=49497#p49497</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[JScript: расширенная функция поиска и фильтрации файлов по шаблону]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=49208#p49208" />
			<content type="html"><![CDATA[<p><em>Выношу на обсуждение коллег функцию поиска файлов. </em></p><p>Функция понимает следующие параметры, задаваемые в виде объекта-списка ключей:<br />-- path - строка, определяет каталог, где поиск поиска<br />-- pattern - строка/массив строк шаблонов поиска<br />-- included - строка/массив строк шаблонов для фильтрации требуемых файлов<br />-- excluded - строка/массив строк шаблонов для &quot;отбраковки&quot; ненужных файлов<br />-- fiolders - логическое, задает поиск каталогов вместо файлов<br />-- recursive -- логическое, задает поиск в глубину для всех подкатлогов данного каталога<br />-- filter - функция, которая вызывается для более тонкой фильтрации файлов. Функция принимает имя файла и возвращает <strong>true</strong> если файл следует сохранить в результирующем файле, иначе - <strong>false</strong><br />-- codepage - строка, при необходимости задает другую кодовую страницу для команды DOS. Идея заимствована из поста #12 коллеги <strong>wisgest</strong> в теме <a href="http://forum.script-coding.com/viewtopic.php?pid=31277#p31277">JS: Быстрый поиск файлов с заданными расширениями</a></p><p>Все параметры необязательны. При вызове функции без параметров по умолчанию производится поиск всех файлов в текущем каталоге. </p><p>Функция поиска реализована в виде статичного метода объекта <strong>FileSystem</strong>. При реализации функции использовались некоторые идеи из вики-статьи <a href="http://en.wikipedia.org/wiki/Glob_(programming)">http://en.wikipedia.org/wiki/Glob_(programming)</a>. </p><div class="codebox"><pre><code>

var FileSystem = FileSystem || {};

FileSystem.find = function(options)
{
	options = options || {};

	// Normalize a path
	var path = (new ActiveXObject(&#039;Scripting.FileSystemObject&#039;)).GetAbsolutePathName(options.path);
	if ( path.slice(-2) != &#039;:\\&#039; ) {
		path += &#039;\\&#039;;
	}

	// Set auxiliary parameters:
	// f - find folders only
	var f;
	if ( options.folders ) {
		f = &#039; /ad &#039;;
	} else {
		f = &#039; /a-d &#039;;
	}

	// p - path to an object of FS
	// s - recursive search
	// b - left border of a pattern
	var p;
	var s;
	var b;
	if ( options.recursive ) {
		p = &#039;&#039;;
		s = &#039; /s &#039;;
		b = &#039; \\\\&#039;;
	} else {
		p = path;
		s = &#039;&#039;;
		b = &#039; ^&#039;;
	}

	// Construct the shell command that would be executed
	var cmd = &#039;&#039;;
	if ( options.codepage ) {
		cmd += &#039;cmd /c chcp &#039; + options.codepage + &#039; &amp;&amp; &#039;;
	}
	cmd += &#039;cmd /c dir /b &#039; + f + s + &#039;&quot;&#039; + path + [].concat(options.pattern || &#039;*&#039;).join(&#039;&quot; &quot;&#039; + path) + &#039;&quot;&#039;;
	if ( options.included ) {
		cmd += &#039; | findstr /i /e &quot;&#039; + b + FileSystem.wildcard2regex(options.included, true, true).join(b) + &#039;&quot;&#039;;
	}
	if ( options.excluded ) {
		cmd += &#039; | findstr /v /i /e &quot;&#039; + b + FileSystem.wildcard2regex(options.excluded, true, true).join(b) + &#039;&quot;&#039;;
	}

	// Storing the command for debugging reasons
	arguments.callee.cmd = cmd;

	// Perform the shell command ...
	var sh = new ActiveXObject(&#039;WScript.Shell&#039;);
	var ex = sh.Exec(cmd);

	// ... and collect each string from the output to the resulting array
	var result = [];
	while ( ex.Status == 0 ) {
		if ( ex.StdOut.AtEndOfStream ) {
			break;
		}
		result.push(p + ex.StdOut.ReadLine());
	}
	while ( ! ex.StdOut.AtEndOfStream ) {
		result.push(p + ex.StdOut.ReadLine());
	}

	if ( typeof options.filter != &#039;function&#039; ) {
		return result;
	}

	// Perform the filtering function
	var filtered = [];
	for (var i = 0; i &lt; result.length; i++) {
		if ( options.filter(result[i], i, result) ) {
			filtered.push(result[i]);
		}
	}
	return filtered;
};

FileSystem.wildcard2regex = function(wildcard, skipRegexp, strictly)
{
	var convert = arguments.callee[strictly ? &#039;strictly&#039; : &#039;std&#039;];

	var result = [];
	if ( Object.prototype.toString.call(wildcard) == &#039;[object String]&#039; ) {
		result.push(convert(wildcard));
	} else {
		for (var i = 0; i &lt; wildcard.length; i++) {
			result.push(convert(wildcard[i]));
		}
	}

	return skipRegexp 
		? result 
		: new RegExp(&#039;^(&#039; + result.join(&#039;|&#039;) + &#039;)$&#039;, &#039;i&#039;);
};

FileSystem.wildcard2regex.strictly = function(wildcard)
{
	var result = wildcard
		.replace(/([\^\$\\\/\|\.\+\!\[\]\(\)\{\}])/g, &#039;\\$1&#039;)
		.replace(/\?/g, &#039;[^\\\\]?&#039;)
		.replace(/\*/g, &#039;[^\\\\]*&#039;)
		;
	return result;
};

FileSystem.wildcard2regex.std = function(wildcard)
{
	var result = wildcard
		.replace(/([\^\$\\\/\|\.\+\!\[\]\(\)\{\}])/g, &#039;\\$1&#039;)
		.replace(/\?/g, &#039;.?&#039;)
		.replace(/\*/g, &#039;.*?&#039;)
		;
	return result;
};</code></pre></div><p>.<br />Примеры использования.</p><p>1. Поиск всех файлов в текущем каталоге<br /></p><div class="codebox"><pre><code>
var f = FileSystem.find();</code></pre></div><p>.<br />2. Поиск заданных файлов в указанном каталоге<br />вариант 1<br /></p><div class="codebox"><pre><code>
var options = {
	path: &#039;C:\\Windows\\System32&#039;, 

	pattern: &#039;z*&#039;
};

var f = FileSystem.find(options);</code></pre></div><p>.<br />вариант 2<br /></p><div class="codebox"><pre><code>
var options = {
	path: &#039;C:\\Windows\\System32&#039;, 

	pattern: &#039;*&#039;, 
	included: &#039;z*&#039;
};

var f = FileSystem.find(options);</code></pre></div><p>.<br />вариант 3<br /></p><div class="codebox"><pre><code>
var options = {
	path: &#039;C:\\Windows\\System32&#039;, 

	filter: function(filename) { return filename.match(/z[^\\]*]$/i); }
};

var f = FileSystem.find(options);</code></pre></div><p>.<br />3. Поиск всех подкаталогов в указанном каталоге и фильтрация с помощью функции<br /></p><div class="codebox"><pre><code>
var options = {
	path: &#039;C:\\Windows\\System32&#039;, 

	folders: true, 
	recursive: true, 

	filter: function(filename) { return filename.match(/z[^\\]*$/i); }
};

var f = FileSystem.find(options);</code></pre></div><p>.<br />Объемное послесловие<br />В WSH для получения списка файлов/каталогов по заданному шаблону (или нескольким шаблонам) можно воспользоваться одним из трех способов:<br />1. ActiveX - например, <a href="http://forum.script-coding.com/viewtopic.php?id=3091">JScript: Поиск файлов/каталогов по шаблону</a><br />2. WQL-запрос к классу CIM_DataFile (подробности здесь <a href="http://www.script-coding.com/WMI_FileSystem.html">http://www.script-coding.com/WMI_FileSystem.html</a>)<br />3. чтение стандартного вывода внешней команды, например, способ коллеги <strong>JSman</strong> - <a href="http://forum.script-coding.com/viewtopic.php?id=3965">JScript: поиск файлов по расширению</a></p><p>Из всех трех способов третий обладает следующими преимуществами перед остальными:<br />1. наибольшая скорость исполнения<br />2. достаточно гибкая фильтрация необходимых данных по шаблону<br />3. лаконичность алгоритма<br />4. простота использования при получении списка объектов любой глубины вложенности</p><p>Тем не менее, этот способ имеет несколько существенных недостатков:<br />1. ограничение буфера вывода в 4 КБ. Это проблема решается с помощью метода, описанного в теме <a href="http://forum.script-coding.com/viewtopic.php?id=5839">JScript: Проблема с WshScriptExec.Exec</a>, пост №#22, &quot;расширенный&quot; алгоритм<br />2. алгоритм основан на чтении вывода встроенной команды <strong>DIR</strong>, который отличается при указании опции <strong>/S</strong> - поиск файлов из указанного каталога и всех его подкаталогов (поиск в глубину). Проблема заключается в том, что команда <strong>DIR /B /S</strong> возвращает список файлов, включая путь до каждого из них, в то время как <strong>DIR /B</strong> - только имена файлов без пути к ним. Для &quot;нормализации&quot; списка требуется дополнительное действие по добавлению префикса (путь к файлу) во втором случае. Но решая проблему 1 можно решить и проблему 2. <br />3. Для фильтрации определенной группы файлов используется внешняя команда <strong>FINDSTR</strong>, которая отфильтровывает требуемые строки входного потока. Однако, принимая во внимание проблему 2, необходимо учитывать различия вывода команды <strong>DIR</strong> в обоих случаях. <br />4. Видимо может возникнуть проблема читабельности полученного текста в случае использования другой кодовой страницы (см. описание параметра codepage в начале поста)</p><p>В предложенном алгоритме все указанные проблемы были учтены и решены. Текущая реализация не использует сторонние либо внешние компоненты, классы, библиотеки, однако использует удобную (на мой взгляд) функцию конвертации символов-джокеров (wildcards * и ?) в правильное регулярное выражение. Суть этой вспомогательной функции в том, что она принимает на входе строку/массив строк, содержащие джокеры и возвращает правильное регулярное выражение, например:<br /></p><div class="codebox"><pre><code>var re = FileSystem.wildcard2regex([&#039;*.js&#039;, &#039;*.vbs&#039;]); // по умолчанию вернет /^(.*?\.js|.*?\.vbs)$/i</code></pre></div>]]></content>
			<author>
				<name><![CDATA[Rumata]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24846</uri>
			</author>
			<updated>2011-06-20T00:28:39Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=49208#p49208</id>
		</entry>
</feed>
