1

Тема: JScript: удаление повторов строк в файле

Юниксовая команда uniq сжимает идущие подряд повторы строк, оставляя в файле повторяющиеся строки, если они идут не подряд. Чтобы она выводила только уникальные строки, приходится сортировать входной файл, что зачастую неприемлемо.
Этот скрипт оставляет в выходном файле только одну уникальную строку на весь файл. Порядок строк при этом не нарушается. Файлы целиком в память не грузятся, а обрабатываются построчно. Пустые строки учитываются так же, как и все остальные. Есть 2 режима:
/1 - записывать в выходной файл первую встретившуюся строку, прочие дубли отбрасывать
/2 - наоборот, записывать последнюю строку из всех повторов, а первые дубли отбрасывать
Параметр /i - обработка без учета регистра символов. При запуске без параметров скрипт выдает справку. Разбор командной строки самый простой, поэтому важен порядок следования параметров, и нельзя опускать промежуточные параметры.

var
   WSH = new ActiveXObject ("WScript.Shell"),
   ARG = WScript.Arguments,
   FSO = new ActiveXObject("Scripting.FileSystemObject"),
   ForReading = 1,
   ForWriting = 2,
   ForAppending = 8,
   buf = new Array(),
   fCountAll=0,
   fCount=0,
   fNumLine,
   fBufLine,
   fInFile,
   fOutFile,
   fInFileName,
   fOutFileName;

// Если скрипт запущен через WScript, перезапускаем его в консоли
if (WScript.FullName.toLowerCase() == (WScript.Path+'\\wscript.exe').toLowerCase()) {
    var args='';
    for (var i=0; i<ARG.length; i++) {
        args+='"'+ARG(i)+'" ';
    }
    WSH.Run ('cscript.exe "'+WScript.ScriptName+'" '+args);
    WScript.Quit();
}

if ( ARG.length == 0 ) {
   WScript.Echo("Искусно расширенный и грамотно дополненный аналог UNIX команды uniq.\n");
   WScript.Echo("Удаляет из входного файла повторяющеся строки и записывает результат в\nвыходной файл.\n");
   WScript.Echo("Файл может быть несортированным.\n\n");
   WScript.Echo(WScript.ScriptName+" файл [выходной файл [/(1|2) [/i]]]");
   WScript.Echo("/1\t1 вариант - записывать первую встретившуюся строку, а прочие дубли\n  \t            отбрасывать");
   WScript.Echo("/2\t2 вариант - записывать последнюю строку из всех повторов, а первые\n  \t            дубли отбрасывать");
   WScript.Echo("/i\tБез учета регистра символов");
   WScript.Echo("\nPress Enter to exit");
    WScript.StdIn.ReadLine();
   WScript.Quit();
}

fInFileName  = ARG(0);
fOutFileName = ( ARG.length >=2 ) ? ARG(1) : ARG(0)+".out"
fCond = ((ARG.length >=3 ) ? ((ARG(2) == "/1" ) ? 1 : ((ARG(2) == "/2" )? 2 : 1 ) ) : 1 )
isCaseSensitive = ((ARG.length >=4 ) ? ((ARG(2).toLowerCase() == "/i" ) ? true : false ) : true )

fInFile = FSO.OpenTextFile (fInFileName, ForReading );
fOutFile = FSO.OpenTextFile(fOutFileName, ForWriting, true);


if(fCond == 1) {
   WScript.Echo('1 вариант - записывать первую встретившуюся строку, а прочие дубли отбрасывать');

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   //
   // здесь записанные строки запоминаются в памяти
   //
   //while (!fInFile.AtEndOfStream) {
   // fBufLine = fInFile.ReadLine();
   // fCountAll++;
   // if (!IsPresent (fBufLine)){      // если текущая строка еще не была записана в выходной файл
   //    fOutFile.WriteLine (fBufLine); // то пишем ее туда
   //    buf.unshift (fBufLine);        // запоминаем что записали
   //    fCount++;
   // }
   //}
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   //
   // здесь строки проверяются на присутствие в файле (медленно, но меньше требования к памяти)
   //
   while (!fInFile.AtEndOfStream) {
      fBufLine = fInFile.ReadLine();
      fCountAll++;
      if (!IsPresentInOutput (fBufLine, isCaseSensitive)){      // если текущая строка еще не была записана в выходной файл
         fOutFile.WriteLine (fBufLine); // то пишем ее туда
         fCount++;
      }
      WScript.StdOut.Write(fCountAll+'\r');
   }
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   WScript.Echo('Уникальных записей: '+fCount+', всего: '+fCountAll);

} else if(fCond == 2) {

   WScript.Echo('2 вариант - записывать последнюю строку из всех повторов, а первые дубли отбрасывать');

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   //
   // здесь строки проверяются на присутствие в файле (медленно, но меньше требования к памяти)
   //
   while (!fInFile.AtEndOfStream) {
      fNumLine = fInFile.Line;      // WScript.Echo("fNumLine = "+fNumLine);
      fBufLine = fInFile.ReadLine();// WScript.Echo(fBufLine);
      fCountAll++;
      if (!IsPresentInFile (fInFile, fBufLine, isCaseSensitive)) { // если текущая строка уже не встречается в остатке файла
         fOutFile.WriteLine (fBufLine);             // то пишем ее туда
         fCount++;
      }
      fInFile.Close();
      fInFile = FSO.OpenTextFile (ARG(0), ForReading );
      for (var i=0; i<fNumLine; i++) fInFile.SkipLine();
      WScript.StdOut.Write(fCountAll+'\r');
   }
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   WScript.Echo('Уникальных записей: '+fCount+', всего: '+fCountAll);

}



function IsPresentInOutput(fLine, isCaseSens) {
var
   res;
   fOutFile.Close();                                              // закрываем выходной файл, открытый на запись
   fOutFile = FSO.OpenTextFile(fOutFileName, ForReading, true);   // открываем выходной файл на чтение
   res = IsPresentInFile (fOutFile, fLine, isCaseSens);                       // ищем в нем строку
   fOutFile = FSO.OpenTextFile(fOutFileName, ForAppending, true); // открываем выходной файл на добавление
   return res;
}


// проверяет файл от текущей позиции до конца на предмет наличия в нем строки
function IsPresentInFile(fFile, fLine, isCaseSens) {
var
   fTmpStr;
   while (!fFile.AtEndOfStream) {
      //fTmpStr = fFile.ReadLine()
      if (isCaseSens){
         if (fLine == fFile.ReadLine()) return true;
      } else {
         if (fLine.toLowerCase() == fFile.ReadLine().toLowerCase()) return true;
      }
   }
   return (false);
}



function IsPresent(fLine) {
   for (var i = 0; i < buf.length; i++) {
     if (buf[i] == fLine) return (true);
   }
   return (false);
}

Автор скрипта - delpher.

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