Перое апреля?

Оказывается придумали на свете день тупее чем 8е марта или 23е февраля и назвали его 1е апреля. Ужасный день, все кругом норовят тебя обмануть или изъебным способом постебаться. Даже звонят напрямую с одной единственной нехорошей целью. Даже сайты начинают работать не так, как положено. Они думают, что это смешно, а у кого-то нервы шатаются от очередной дурацкой шутки.

Поэтому напишу-ка я по делу краткую заметку о том, как запустить 32х битный русский LEMMATIZERLib (морфология) на 64х битной машине с английской виндой. Простой запуск дает "Retrieving the COM class factory for component with CLSID {6B23250F-1816-11D3-9CC6-00105A68BAF2} failed due to the following error: 80040154", в этом случае важно не забыть сделать следующие шаги.
1) На 64х битной машине зарегистрировать компонент с консоли командой - "regsvr32 pathtodll"
2) В проекте MSVS, который использует компонент, указать 32х битную компиляцию заместо Any: Target CPU: from "Any CPU" to "x86"
3) Для работы русского языка - выставить в реестре ключи с CodePage (найдете точный путь в гугле) для 1250 и 1252 как "c_1251.nsl"
4) Перезагрузиться. Надеюсь это поможет тем, кто найдет этот пост через поисковики

Метки:   Категории:humor | Code


MySql - обработка поточных данных.

Пост в продолжение обсуждения на Хабре (http://habrahabr.ru/blogs/webdev/54964/). Если у Вас идет большой поток данных, которые нужно сохранять в таблице и потом отображать пользователям, то очень правильно будет разнести все это на 2 таблицы. Одна для выдачи самых частых запросов, другая для медленных. А также таблицы в которых бы были предварительно подсчитанные данные. После такого разнесения все процедуры принимают вид, как показано ниже, на примере для времени:
Copy Source | Copy HTML
begin
DECLARE `datatable` VARCHAR(20) DEFAULT '`data`';
DECLARE `xtab_query` VARCHAR(4096) DEFAULT '';
 
if(DATE(`dfrom`) > DATE_SUB(NOW(), interval 8 DAY)) then
    SET datatable := "`data_week`"; #last week
else
    if(DATEDIFF(`dto`,`dfrom`)>60) then
        SET datatable := "`data_ms`";#months
    else
        if(DATEDIFF(`dto`,`dfrom`)>14) then
            SET datatable := "`data_ws`"; #weeks
        end if;
    end if;
end if;
 
SET xtab_query := CONCAT("select * FROM ",`datatable`," d ........and 1 screen of query...");
 
PREPARE `xtab_query` FROM @xtab_query;
EXECUTE `xtab_query`;
 
end

Метки:   Категории:Code | life


PDF / DOC to TXT

При индексировании архива офисных файлов мне понадобилось извлекать текстовую информацию из различных файлов таких как DOC или PDF. Решение несложное, достаточно воспользоваться несколькими библиотеками. Для PDF - это PDFBox, портированный с Java на CSharp. Для DOC - это Microsoft.Office.Interop.Word.dll из набора OfficeAPI. Для удобства, необходимые библиотеки можно скачать одним архивов по ссылке

Под катом я приведу код, показывающего как с ними работать.
Начнем с PDF, подключим имена:
Copy Source | Copy HTML
using org.pdfbox.pdmodel;//PDF library
using org.pdfbox.util;
После этого документ можно преобразовать в текст 3мя строчками:
Copy Source | Copy HTML
PDDocument pdfFile = PDDocument.load(onefile);
PDFTextStripper stripper = new PDFTextStripper();
String innertext = stripper.getText(pdfFile);

Для DOC больше кода, поэтому я вынес все в отдельный класс, производя конвертирование как innertext = ConvertDoc.GetDocText(onefile);
Copy Source | Copy HTML
using Microsoft.Office.Interop.Word;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
 
/// <summary>
/// объект для операция с Вордовыми документами
/// </summary>    
public class ConvertDoc
{
    // Объект отвечающий за результат конвертации.
    // Ставим .txt - текстовый в досовской кодировке
    static ConvConfig tmpItem = new ConvConfig(".txt", WdSaveFormat.wdFormatDOSText);
 
    /// <summary>
    /// Получение текста из доковского файла
    /// </summary>
    /// <param name="file">Ссылка на документ</param>
    /// <returns>Текст документа</returns>
    public static String GetDocText(String inFileName)
    {
        String retText = String.Empty;
        //создаем объекты для дальнейшей передачи в вордовые функции
        object fileName = inFileName;
        object fileSaveName = Path.GetTempFileName() + tmpItem.itemExtension; //случайное уникальное имя + .txt;
        object vk_read_only = true;
        object vk_visible = false;
        object vk_true = true;
        object vk_false = false;
        object vk_dynamic = 2;
        object vk_saveformat = tmpItem.itemWord;
 
        object missing = Missing.Value;//все параметры, которые мы пропустим
        /////
        object vk_range = missing;
        object vk_to = missing;
        object vk_from = missing;
        /////
        // создаем класс для работы с вордом
        ApplicationClass vk_word_app = new ApplicationClass();
 
        Document aDoc = null;
        try
        {
            // вызываем скрытое открытие документа 
            aDoc = vk_word_app.Documents.Open(
                     ref fileName, ref missing,
                     ref vk_read_only, ref vk_false,
                     ref missing, ref missing,
                     ref missing, ref missing,
                     ref missing, ref missing,
                     ref missing, ref vk_visible,
                     ref missing, ref missing,
                     ref missing);
 
            //Сохраняем в файл в заданном формате
            aDoc.SaveAs(ref fileSaveName, ref vk_saveformat,
                 ref missing, ref missing, ref missing, ref missing,
                 ref missing, ref missing, ref missing, ref missing,
                 ref missing, ref missing, ref missing, ref missing,
                 ref missing, ref missing);
            //корректно закрывает все открытое
            if (aDoc != null)
            {
                aDoc.Close(ref vk_false, ref missing, ref missing);
            }
            //читаем из временного файла в досовской кодировке и удаляем его
            retText = File.ReadAllText(fileSaveName.ToString(), Encoding.GetEncoding(866));
            File.Delete(fileSaveName.ToString());
        }
        catch (Exception err)
        {
            Debug.WriteLine("Ошибка чтения файла: " + inFileName + " - " + err.Message);
        }
        finally
        {
            vk_word_app.Quit(ref vk_false, ref missing, ref missing);
        }
        return retText;
    }
}
 
/// <summary>
/// Настройки для конвертации
/// </summary>
public class ConvConfig
{
    //расширение
    public String itemExtension;
    //тип объекта на выдаче
    public Object itemWord;
 
    //The constructors
    public ConvConfig() { }
 
    public ConvConfig(String inExtension, Object inWordType)
    {
        // This allows us to set the properties 
        itemExtension = inExtension;
        itemWord = inWordType;
    }
}
 

Метки:   Категории:Csharp | Code


.Net - Контексный поиск с учетом русской и английской морфологии

Подведу итог моим экспериментам с библиотекой Lucene.Net, под катом немало кода.

Для чего это: Поиск документов по словам с учетом морфологических модификаций в большой базе документов
Программный язык: CSharp.
Потрачено времени: Двое суток
Использованные продукты: MSVS, Lucene.Net, Lemmanizer с дополнительным английским словарем.
Ограничения и лицензии: Apache2.0 для Lucene и 250y.e. за Lemmanizer или бесплатная демо до конца года.
Дополнительно: Немало полезного есть на CodeProject, а также пример простого поиска описывали в RSDN

Начну с результатов
1) Индекс на 1530 текстовых документов занял 40минут, и 1,4мегабайта на диске. Если отключить морфологию, то те же действия выполняются за 1 минуту.
2) Иллюстрация поиска, который выполняется почти моментально

Видно, что по слову authority и authorities найдено одинаковое число документов, при отключении морфологии их число разнится
Одинаковое число документов для Бандитко и Чичваркина вызвано тем, что в xml настройке для SynonymFilter я указал эти слова в качестве синонимов.


Итак, наша задача при написании контекстного поиска с учетом морфологии с использованием Lucene.Net сводиться к описанию класса по разбору текста и вставлению в него всех слов, которые как-то коррелируют с текущим. Я написал следующий класс
Copy Source | Copy HTML
  1. namespace Lucene.Net.Analysis.Morphology
  2. {
  3.     using System;
  4.     using System.Data;
  5.     using System.Configuration;
  6.     using System.Web;
  7.     using System.IO;
  8.     using System.Web.Security;
  9.     using System.Web.UI;
  10.     using System.Web.UI.WebControls;
  11.     using System.Web.UI.WebControls.WebParts;
  12.     using System.Web.UI.HtmlControls;
  13.     using Lucene.Net.Analysis.Standard;
  14.     using Lucene.Net.SynonymEngine;
  15.  
  16.     /// <summary>
  17.     /// Анализатор текста для Lucene.Net с использованием морфологического анализа и словаря синонимов
  18.     /// </summary>
  19.     public class MorphologyAnalyzer : Analyzer
  20.     {
  21.         public MorphologyAnalyzer(ISynonymEngine engine)
  22.         {
  23.             //запомнили откуда брать синонимы
  24.             SynonymEngine = engine;
  25.             isSearchQuery = false;
  26.             //грузим словари
  27.             Morph = new MorphENRU();
  28.         }
  29.  
  30.         private MorphENRU Morph;
  31.         private ISynonymEngine _SynonymEngine;
  32.         private Boolean isSearchQuery;
  33.  
  34.         /// <summary>
  35.         /// Объект для перечисления синонимов слова
  36.         /// </summary>
  37.         public ISynonymEngine SynonymEngine {
  38.             get {
  39.                 return _SynonymEngine;
  40.             }
  41.             private set
  42.             {
  43.                 _SynonymEngine = value;
  44.             }
  45.         }
  46.  
  47.         /// <summary>
  48.         /// Выключаем проверку синонимов
  49.         /// </summary>
  50.         /// <returns></returns>
  51.         public MorphologyAnalyzer SetQuerySynonym()
  52.         {
  53.             this.isSearchQuery = true;
  54.             return this;
  55.         }
  56.  
  57.         /// <summary>
  58.         /// Анализуем текст и возвращаем в виде нобора слов для помещения в индекс и подсчета частот
  59.         /// </summary>
  60.         /// <param name="fieldName">Имя индексируемого файла</param>
  61.         /// <param name="reader">Поток для чтнения файла</param>
  62.         /// <returns>TokenStream</returns>
  63.         public override TokenStream TokenStream(string fieldName, TextReader reader)
  64.         {
  65.             //создаем обход слов
  66.             TokenStream result = new StandardTokenizer(reader);
  67.  
  68.             //преобразуем исходную строчку
  69.             result = new StandardFilter(result); // выделение слов при помощи StandardTokenizer
  70.             result = new LowerCaseFilter(result);// Приведение к нижнему регистру
  71.  
  72.             // простой фильтр английских местоимений,
  73.             // русских, к сожалению нету
  74.             result = new StopFilter(result, StopAnalyzer.ENGLISH_STOP_WORDS);
  75.             result = new MorphFilter(result, Morph); // вставляем морфологическую модификацию
  76.             result = new SynonymFilter(result, SynonymEngine, this.isSearchQuery); // вставляем синонимы
  77.  
  78.             //возвращаем набор токенов для помещения в индекс
  79.             return result;
  80.         }
  81.     }
  82.  
  83. }

Видно, что кроме стандартных фильтров, я добавил MorphFilter - вставка морфологических модификаций и SynonymFilter - для вставки синонимов. Класс MorphENRU используется для работы со словарями морфологии на базе библиотеки Lemmanizer и идет ниже
Copy Source | Copy HTML
  1. namespace Lucene.Net.Analysis.Morphology
  2. {
  3.     using LEMMATIZERLib;//морфология
  4.     using System;
  5.     using System.Collections.Generic;
  6.     using System.Configuration;
  7.     using System.Data;
  8.     using System.Diagnostics;
  9.     using System.IO;
  10.     using System.Text;
  11.     using System.Text.RegularExpressions;
  12.     using System.Web;
  13.     using System.Web.Security;
  14.     using System.Web.UI;
  15.     using System.Web.UI.WebControls;
  16.     using System.Web.UI.WebControls.WebParts;
  17.     using System.Web.UI.HtmlControls;
  18.  
  19.     /// <summary>
  20.     /// Работа с морфологией для анг. и русского языка
  21.     /// </summary>
  22.     public class MorphENRU
  23.     {
  24.         /// <summary>
  25.         /// Регулярное выражение для определения английских слов
  26.         /// </summary>
  27.         private Regex rWordEn = new Regex(@"[a-z0-9]+", RegexOptions.Singleline | RegexOptions.Compiled
  28.             | RegexOptions.ExplicitCapture);
  29.  
  30.         /// <summary>Анализатор английских слов</summary>
  31.         private ILemmatizer Lemmatizer_en;
  32.  
  33.         /// <summary>
  34.         /// Анализатор русских слов
  35.         /// </summary>
  36.         private ILemmatizer Lemmatizer_ru;
  37.  
  38.  
  39.         public MorphENRU()
  40.         {
  41.             //в конструкторе нужно загрузить морфологию
  42.             try
  43.             {
  44.                 Lemmatizer_ru = new LemmatizerRussianClass();
  45.                 Lemmatizer_ru.LoadDictionariesRegistry();
  46.                 Lemmatizer_en = new LemmatizerEnglishClass();
  47.                 Lemmatizer_en.LoadDictionariesRegistry();
  48.                 Debug.WriteLine("Загрузка морфологи успешно завершена.");
  49.             }
  50.             catch (Exception e)
  51.             {
  52.                 Debug.WriteLine("Ошибка при открытиии морфологического словаря: " + e.Message);
  53.                 //ошибка может быть по многим причинам - например,
  54.                 //кто-то удалил файлы словарей или истекла лицензия,
  55.                 //тогда игнорируем морфологию
  56.                 Lemmatizer_ru = null;
  57.                 Lemmatizer_en = null;
  58.             }
  59.         }
  60.  
  61.         /// <summary>
  62.         /// Делаем слово морфологически инвариантным
  63.         /// </summary>
  64.         /// <param name="word"></param>
  65.         /// <returns></returns>
  66.         public List<string> NormalizeWord(string word)
  67.         {
  68.             List<string> WordList = null;
  69.             if (Lemmatizer_ru != null &&
  70.     Lemmatizer_en != null)
  71.             {
  72.                 //если ошибка в словарях, то пропускаем обработку
  73.                 int weight = -1;
  74.                 bool isEng = this.rWordEn.Match(word).Success;//английское или русское слово
  75.                 // ищем варианты в словаре
  76.                 IParadigmCollection ParadigmCollection =
  77.                     isEng ?
  78.                     Lemmatizer_en.CreateParadigmCollectionFromForm(word, 1, 1) :
  79.                     Lemmatizer_ru.CreateParadigmCollectionFromForm(word, 1, 1);
  80.                 // выбираем наиболее тяжелое по весу
  81.                 for (int j = 0; j < ParadigmCollection.Count; j++)
  82.                 {
  83.                     if (ParadigmCollection[j].WordWeight > weight)
  84.                     {
  85.                         if (ParadigmCollection[j].Norm == "ДЛИТЬ") continue;
  86.                         //будем брать всего одно самое весовое слово для морфологии
  87.                         if (WordList == null)
  88.                         {
  89.                             WordList = new List<string>();
  90.                             WordList.Add(ParadigmCollection[j].Norm.ToLower());
  91.                         }
  92.                         else
  93.                         {
  94.                             WordList[0] = ParadigmCollection[j].Norm.ToLower();
  95.                         }
  96.                         weight = ParadigmCollection[j].WordWeight;
  97.                         //графемы нам не нужны
  98.                         //gramma = this.ParadigmCollection[j].SrcAncode;
  99.                     }
  100.                 }
  101.             }
  102.             return WordList;
  103.         }
  104.     }
  105. }


В функции NormalizeWord видно, что для каждого слова мы берем только ту его морфологическую модификацию, которая имеет наибольший вес. Можно было бы брать больше слов, но тогда размер индекса сильно бы увеличился. Кроме того, на этапе фильтра MorphFilter мы игнорируем все слова с длинною меньше 4х символов, чтобы ускорить работу. Сам класс MorphFilter приведен ниже
Copy Source | Copy HTML
  1. namespace Lucene.Net.Analysis.Morphology
  2. {
  3.     using System;
  4.     using System.Data;
  5.     using System.Configuration;
  6.     using System.Collections.Generic;
  7.     using System.Web;
  8.     using System.Web.Security;
  9.     using System.Web.UI;
  10.     using System.Web.UI.WebControls;
  11.     using System.Web.UI.WebControls.WebParts;
  12.     using System.Web.UI.HtmlControls;
  13.     using Lucene.Net.Analysis;
  14.  
  15.     /// <summary>
  16.     /// Обход набора слов и вставка морфологических модификаций
  17.     /// </summary>
  18.     public class MorphFilter : TokenFilter
  19.     {
  20.         private Queue<Token> morphTokenQueue
  21.             = new Queue<Token>();
  22.  
  23.         private MorphENRU _MorphEngine;
  24.         public MorphENRU MorphEngine { get { return _MorphEngine; } private set { _MorphEngine = value; } }
  25.  
  26.         public MorphFilter(TokenStream input, MorphENRU morphEngine)
  27.             : base(input)
  28.         {
  29.             if (morphEngine == null)
  30.                 throw new ArgumentNullException("morphEngine");
  31.  
  32.             MorphEngine = morphEngine;
  33.         }
  34.  
  35.         public override Token Next()
  36.         {
  37.             // Если есть слова в очереди, то надо их поместить в поток прежде чем одти дальше
  38.             if (morphTokenQueue.Count > 0)
  39.             {
  40.                 return morphTokenQueue.Dequeue();
  41.             }
  42.  
  43.             //Берем след. слово из текста
  44.             Token t = input.Next();
  45.  
  46.             //если пусто, то конец потока
  47.             if (t == null)
  48.                 return null;
  49.  
  50.             //разбор морфологии только для слов длинною более 4х символов
  51.             if (t.TermText().Length > 4)
  52.             {
  53.  
  54.                 //получение актуальных морфологий
  55.                 IEnumerable<string> mWords = MorphEngine.NormalizeWord(t.TermText());
  56.  
  57.                 //если нет слов то вернем слово просто
  58.                 if (mWords != null)
  59.                 {
  60.                     //Переберем все морфологические формы которые более употребляемы
  61.                     foreach (string word in mWords)
  62.                     {
  63.                         //убедимся, что не дублируем слово
  64.                         if (!t.TermText().Equals(word))
  65.                         {
  66.                             //делаем морфологический токен
  67.                             Token mToken = new Token(word, t.StartOffset(), t.EndOffset(), "<MORPH>");
  68.  
  69.                             // устанавливаем относительное смещение в 0,
  70.                             // это нужно, чтобы отразить то, что добавляемое слово соответствует
  71.                             // старому месту в изначальном тексте
  72.                             mToken.SetPositionIncrement(0);
  73.  
  74.                             //помещаем в очередь на помещение в поток
  75.                             morphTokenQueue.Enqueue(mToken);
  76.                         }
  77.                     }
  78.                 }
  79.             }
  80.             //
  81.             return t;
  82.         }
  83.     }
  84.  
  85. }
Теперь можно использовать объект MorphologyAnalyzer в качестве аргумента для стандартных функций индексации и поиска Lucene.Net библиотеки. Пример, стандартного использования Lucene.Net находится по ссылке на RSDN в начале поста.


Метки: , ,   Категории:Csharp | Code


Завершая месяц

1) Несколько дней буду недоступен в связи с отъездами и пропавшим дома интернетом.
2) Начинаю разбираюсь с библиотекой полнетекстового поиска портированную на Csharp: lucene.net. Очень много разработано для этой библиотеки, почти что полноценный десктопный поисковик написать можно. Очень волнует скорость добавления информации в индекс - может у кого есть опыт работы с ней? Надеюсь с помощью библиотеки покрыть часть функционала тематического поиска, как у продаваемого IBM решения для корпораций, где одна лицензия стоит под 100килобаксов.
3) Опробовал по совету Mads'a плагин для VisialStudio StyleCop 4.3, правда восторга, как у него у меня не появилось. Плагин позволяет следить за правильным оформлением вашего кода. Из полезного могу отметить только возможность проверки правильности составления документации, что все параметры функций описаны и не пусты. Остальное, как проверка на отсутствие двойных пробелов, на наличие копирайтов и т.п. - черезчур.
4) Интересное решение спам проблемы в ЖЖ предлагает Лугастик - как бы лично выступая в роли абуз теам и инспектируя ботов. Проблемой может стать ограничение на размер бан листа в ЖЖ =), а также то, что число ботов в ЖЖ регистрируется за день примерно в 1000раз больше, чем автор успевает просматривать.

Метки:   Категории:Code | life


.Net: Программная печать документов MS Office

На днях решил поменять в одной программе печать документов с простого "webBrowser.ShowPrintDialog();" в браузере, на полноценное открытие MS Word с дальнейшей печатью. Но речь пойдет не о создании простого процесса winword.exe с передачей ему в качестве параметра нужного документа, а о библиотеках, которые созданы для разработчиков предусмотрительным Microsoft'ом. Заявлено, что решение работает под Office2003/2007, WinXP+, MSVS2005+. Краткое описание по работе с библиотекой приводит один из индийских девелоперов, номинированный как MVP (Microsoft Most Valuable Professional). Однако после строчек кода:
Copy Source | Copy HTML
  1. Object true = true;
  2. Object false = false;
...я почувствовал, что все шутки на баше про индийских программистов не были надуманными.
Далее я приведу шаги, с помощью которых, мне удалось осуществить задуманное и кусок кода. Итак, скачайте указанный выше архив с библиотеками. Внутри него будет также инструкция по интеграции этих библиотек в MSVS, но я рекомендую просто подключить нужную библиотеку, в моем случае это "Microsoft.Office.Interop.Word.dll", потому как я работаю только с MSWord, как Refference в проект. Далее, так как я хочу выводить на печать содержимое окна браузера, то я создал наследника от класса WebBrowser. Обратите внимание на функцию OpenInWord. Она создает вначале приложение офиса Application, потом сам документ, с которым работаем, загружая его из файла. Когда загрузка завершена, то делаем отображение самого офиса, выставляя значение Visible в true, так как по умолчанию офис будет работать спрятан:
Copy Source | Copy HTML
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Data;
  6. using System.Text;
  7. using System.Windows.Forms;
  8. using System.Text.RegularExpressions;// регулярные выражения
  9. using System.IO;//работа с файлами
  10.  
  11. namespace BestApplicationEvah
  12. {
  13.     public partial class WebBrowser_I : WebBrowser
  14.     {
  15.         public WebBrowser_I()
  16.         {
  17.             InitializeComponent();
  18.         }
  19.  
  20.         /// <summary>
  21.         /// Временная директория в которую складываются все файлы отправляемые на печать.
  22.         /// </summary>
  23.         private string printdirectory = "Rprint";
  24.  
  25.         /// <summary> Временная директория в которую складываются все файлы отправляемые на печать. </summary>
  26.         public string PrintDir { get { return printdirectory; } set { printdirectory = value; } }
  27.  
  28.         /// <summary>
  29.         /// Функция преобразования html в правильный формат для показа в doc
  30.         /// </summary>
  31.         /// <param name="file">Название файла. Желательно с расширением doc</param>
  32.         /// <param name="HTML">Html для преобразований</param>
  33.         /// <returns></returns>
  34.         private string HtmlToDocFile(ref string file, string HTML)
  35.         {
  36.             /*** много кода ***/
  37.         }
  38.  
  39.         /// <summary>
  40.         /// Функция сохраняет текущую страницу, как указанный файл и открывает его
  41.         /// </summary>
  42.         /// <param name="file">Имя создаваемого файла, должно иметь расширение DOC</param>
  43.         public void OpenAsDoc(string file)
  44.         {
  45.             this.OpenAsDoc(file, false);
  46.         }
  47.  
  48.         /// <summary>
  49.         /// Функция сохраняет текущую страницу, как указанный файл и открывает его
  50.         /// </summary>
  51.         /// <param name="file">Имя создаваемого файла, должно иметь расширение DOC</param>
  52.         /// <param name="newWindow">Открыть файл в новом окне?</param>
  53.         public void OpenAsDoc(string file, bool newWindow)
  54.         {
  55.             string err = this.HtmlToDocFile(ref file, this.DocumentText);
  56.             if (!String.IsNullOrEmpty(err))
  57.             {
  58.                 //вывод сообщения об ошибке
  59.                 MessageBox.Show(err, "Ошибка");
  60.             }
  61.  
  62.             //и отображаем доковский файл в браузере
  63.             this.Navigate(Directory.GetCurrentDirectory() + "/"+ this.printdirectory + "/" + file,newWindow);
  64.         }
  65.  
  66.         /// <summary>
  67.         /// Открытие HTML в MS Word...
  68.         /// </summary>
  69.         /// <param name="file">Название файла для сохранения информации</param>
  70.         public void OpenInWord(string file){
  71.             //создадим из контента файл
  72.             string err = this.HtmlToDocFile(ref file, this.DocumentText);
  73.             if (!String.IsNullOrEmpty(err))
  74.             {
  75.                 //вывод сообщения об ошибке
  76.                 MessageBox.Show(err, "Ошибка");
  77.             }
  78.  
  79.             try
  80.             {
  81.                 //Create an object for missing values. This will be passed whenever we don’t want to pass value
  82.                 Object missing = System.Reflection.Missing.Value;
  83.                 //Objects for true and false to be used in the word document for passing true or false.
  84.                 //Object true = true;
  85.                 //Object false = false;
  86.                 //Creating objects of word and document
  87.                 Microsoft.Office.Interop.Word.Application oWord = new Microsoft.Office.Interop.Word.Application();
  88.                 Microsoft.Office.Interop.Word.Document oWordDoc = new Microsoft.Office.Interop.Word.Document();
  89.                 object fileName = Directory.GetCurrentDirectory() + "/" + this.printdirectory + "/" + file;
  90.                 // You can keep it true if you want to open the file in readonly mode
  91.                 object readOnly = false;
  92.                 // we can keep it false if you want to open the file but not make it invisible
  93.                 object isVisible = true;
  94.                 //открытие файла
  95.                 oWordDoc = oWord.Documents.Open(ref fileName, ref missing, ref
  96.                   readOnly, ref missing, ref missing, ref missing, ref
  97.                   missing, ref missing, ref missing, ref missing, ref
  98.                   missing, ref isVisible, ref missing, ref missing, ref
  99.                   missing);
  100.                 //делаемся видимыми
  101.                 oWord.Application.Visible = true;
  102.                 //oWord.ShowMe();//смысл функции остался неизвестным
  103.                 //автопечать
  104.                 //oWord.PrintOut(ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
  105.                 // ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
  106.                 // ref missing, ref missing);
  107.             }
  108.             catch(Exception WordExc)
  109.             {
  110.                 MessageBox.Show(WordExc.Message, "Ошибка при работа с MS Word");
  111.             }
  112.         }
  113.     }
  114. }
И когда мы всетаки завершим работать с офисом, важно закрыть объекты, иначе процесс winword может остаться висеть:
Copy Source | Copy HTML
  1. //Closing the file
  2. oWordDoc.Close(ref oFalse, ref missing, ref missing);
  3. //Quitting the word application to release the memory.
  4. oWord.Quit(ref missing, ref missing, ref missing);

Остальные возможности работы в документами содержаться в многочисленных методах объекта oWord и требуют дополнительных экспериментов.

Метки: , ,   Категории:Code


YandexUtility 2009 reviewed

С наступлением нового года устарела лицензия на написанной мною 2 года назад программе YandexUtility, поэтому я обновил программу.
Что это? Это программа, написанная на Csharp, для анализа блогосферы на основе данных, получаемых из яндекса.
Что изменено? Исправлено большинство регулярных выражений, которые успели устареть, а также обновлена лицензия на морфологический анализатор до конца года.
Что делает? Позволяет отслеживать рейтинги виртуалов; читать ТОП; читать баш =); анализировать частотное упоминание слов в конкретной ленте, в комментариях к ней и в заданной теме дня; анализировать упоминание слов в темах, на которые выбранный блогер чаще всего пишет комментарии; убирать некоторые известные банеры(реклама в жж) путем автоматического редактирования hosts; делать вставки в блог с результатами анализа;
Есть ли руководство пользователя? Нет. Но внутри программы есть некоторые комментарии. Умные люди должны разобраться.
Установщик последней версии можно взять тут - YandexUtility1.1beta_setup.exe
Пример некоторой аналитики, с использованием программы, под катом:
Для начала я посмотрел упоминания слов в постах Самизнаетекого за текущий год, то есть за последние 10 дней:
Top30 самых используемых слов в ленте tema.livejournal.com/data/rss

БЫТЬ:8 ЕСТЬ:7 СМОТРЕТЬ:6 TEMALEBEDEV@GMAIL.COM:6 ХОРОШИЙ:5 СДЕЛАТЬ:5 ДЕЛАТЬ:5 БЛЯТЬ:4 ОДИН:4 РАННИЙ:4 РАБОТАТЬ:4 WWW:4 ПИЗДЕЦ:4 НАПИСАТЬ:4 УЗКИЙ:4 СТАТЬ:3 СОВЕРШЕННЫЙ:3 ПРИСЛАТЬ:3 ЗАПОМНИТЬ:3 ЧИТАТЬ:3 МОЧЬ:3 ПИСАТЬ:3 ПЕРЕТЬ:3 ПЕРВЫЙ:3 НАЧАТЬ:3 СЛУШАТЬ:2 ЛЮБИТЬ:2 ИНТЕРЕСНЫЙ:2 ПРИЕЗЖАТЬ:2 ЗНАТЬ:2

Всего обработано 26 постов, найдено 846 слов из них уникально 602
Длина поста(слов): 5(Минимальная),1015(Максимальная),115,5769(Средняя)
(c) flashr и его программа YandexUtility

Потом хотел было проанализировать комментарии в его жжурнале, но, как оказалось, Яндекс забил на комментарии оставляемые в журнале Темы. А Единственная запись, на которую отреагировал согласно Яндексу СамВеликий содержала следующие слова: Оптимус, говноджип, Дэнги. Но так как запись всего одна то и анализировать нечего.

И в завершение разбор слов в теме про газовый конфликт:
Top30 самых используемых слов в ленте 'Газовый конфликт с Украиной'

БЫТЬ:106 ГАЗОВЫЙ:103 МОЧЬ:86 УЗКИЙ:69 ХОТЕТЬ:66 ЕСТЬ:62 СКАЗАТЬ:45 ГОВОРИТЬ:42 ЗНАТЬ:41 ДОЛЖНЫЙ:39 ЮЩЕНКО:37 КОНЕЧНЫЙ:37 ДЕТЬ:36 ПОНИМАТЬ:35 ИДТИ:34 СЧИТАТЬ:33 ИМЕТЬ:33 ОДИН:33 СДЕЛАТЬ:32 ЯВЛЯТЬСЯ:32 ДЕЛАТЬ:32 ДУМАТЬ:31 РОССИЙСКИЙ:31 МНОГО:30 СТАТЬ:30 РОСУКРЭНЕРГО:30 ЭТОТ:29 ВИДЕТЬ:29 ПЛАТИТЬ:28 СВОЙ:28

Всего обработано 200 постов, найдено 14423 слов из них уникально 4793
Длина поста(слов): 6(Минимальная),2588(Максимальная),294,52(Средняя)
(c) flashr и его программа YandexUtility

Метки:   Категории:Code | YandexUtility


Source-Code.ME - HightLight

В предыдущем посте я активно использовал вставки кусков кода в текст поста. Как вы могли видеть, текст был подсвечен и красиво оформлен. Это было сделано не спомощью стандартных средств, а с помощью самописного проекта Source-Code.ME (s-c.me).
При его написании использовалось: ASP.NET,IIS,ASP AJAX Toolkit,Log4net(логирование),URLRewriter(перезапись обращений), inettuts(Перемещаемые виджеты), jQuery(работа с куками), ManoliCode HighLighter(основа для подсветки кода).
Времени потрачено: 4 вечера.
Что нового сделано: Автоматическое определение механизма подсветки в зависимости от куска кода; PHP подсветка; Улучшенный алгоритм подсветки объектов для Csharp кода; Интегрирован inettuts в качестве компонента и виджетов; Возможность подсветки как чистым HTML'ом так и в виде CSS вставки; Добавлено выравнивание по левому краю; Возможность копирования как кода, так и конечного HTML'a по одному клику.
Что в планах: Автоматическое проставление тэгов для кода и поиск по тегам; Обсуждение куска кода при помощи F*Connect; Проверка орфографии(рус-анг) в куске кода с предложением возможных правильных вариантов; Мобильная версия =), позиционированная как Spell-Check.ME с возможностью перевода слов-фраз; Может еще какие мысли прийдут в голову.

Пример подсветки класса URLRewriter под катом, кому не влом - тестируйте =)
Copy Source | Copy HTML
  1. using System.Configuration;
  2. using System.Diagnostics;
  3. using System.Text.RegularExpressions;
  4. using System.Web;
  5. using System.Xml;
  6.  
  7. // nicked from http://www.codeproject.com/aspnet/URLRewriter.asp
  8.     public class URLRewriter : IConfigurationSectionHandler {
  9.         public static log4net.ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  10.         protected XmlNode _oRules = null;
  11.  
  12.         protected URLRewriter() { }
  13.  
  14.         public string GetSubstitution(string zPath) {
  15.             Regex oReg;
  16.  
  17.             foreach (XmlNode oNode in _oRules.SelectNodes("rule")) {
  18.                 // get the url and rewrite nodes
  19.                 XmlNode oUrlNode = oNode.SelectSingleNode("url");
  20.                 XmlNode oRewriteNode = oNode.SelectSingleNode("rewrite");
  21.  
  22.                 // check validity of the values
  23.                 if (oUrlNode == null || string.IsNullOrEmpty(oUrlNode.InnerText)
  24.                     || oRewriteNode == null || string.IsNullOrEmpty(oRewriteNode.InnerText)) {
  25.                     Logger.Warn("Invalid urlrewrites rule discovered in web.config file.");
  26.                     continue;
  27.                 }
  28.  
  29.                 oReg = new Regex(oUrlNode.InnerText, RegexOptions.IgnoreCase);
  30.  
  31.                 // if match, return the substitution
  32.                 Match oMatch = oReg.Match(zPath);
  33.                 if (oMatch.Success) {
  34.                     return oReg.Replace(zPath, oRewriteNode.InnerText);
  35.                 }
  36.             }
  37.  
  38.             return null; // no rewrite
  39.         }
  40.  
  41.         public static void Process() {
  42.             URLRewriter oRewriter = (URLRewriter)System.Configuration.ConfigurationManager.GetSection("urlrewrites");
  43.  
  44.             string zSubst = oRewriter.GetSubstitution(HttpContext.Current.Request.Path);
  45.  
  46.             if (!string.IsNullOrEmpty(zSubst)) {
  47.                 Logger.InfoFormat("Rewriting url '{0}' to '{1}' ", HttpContext.Current.Request.Path, zSubst);
  48.                 HttpContext.Current.RewritePath(zSubst);
  49.             }
  50.         }
  51.  
  52.         #region Implementation of IConfigurationSectionHandler
  53.         public object Create(object parent, object configContext, XmlNode section) {
  54.             _oRules = section;
  55.  
  56.             return this;
  57.         }
  58.         #endregion
  59.     }
PS: Исходники с http://source.virtser.net/ не использовались.

Метки:   Категории:news | Code | life


BlogEngine - MultiPost

Как вы знаете, я транслирую свои записи в несколько популярных блогомест в интернете (за исключением ЖЖ). И это я делаю не с помощью встроенных средств копирования постов, а с помощью самописного модуля (Extension) для движка BlogEngine.Net, работающем под ASP.NET. Почему так? Да потому, что часто при задании копирования постов с помощью сторонних средств, таких как трансляции на Ya.ru, посты помещаемые в блог выпадают из поискового индекса и ссылки в них не учитываются.

1) Модуль получился расширяемый, что позволяет дописать копирование постов куда вам только захочется. В вышеуказанный архив я вложил только два класса, которые позволяют копировать посты на Хабр и на Ярушечку. Так как ярушечка имеет херовый интерфейс, и код выглядит очень сложным (и, кстати, не рабовает, если Вы недавно заходили под своим же аккаунтом с другой машины), а также придется добавить следующие строчки в web.config:
  1. <system.net>
  2.    <settings>
  3.      <httpWebRequest useUnsafeHeaderParsing="true" />
  4.      <servicePointManager expect100Continue="false"/>
  5.    </settings>
  6.  </system.net>


2) Далее, в настройках интерфейса у Вас появится такая вот картинка, позволяющая настроить копирование постов:
Отмечу, что все селективные значения прописываются вручную, так как в текущей версии BlogEngine нет возможностей задавать параметр группы настроек модуля в виде выбора из списка. Надеюсь это сделают в следующей версии, которую обещают в конце Января. Итак, первый параметр отвечает за копирование постов (bool=true/false). Второй за название категории, только из которой , если она указана, будут копироваться посты. Третий - BlogType - название класса отвечающего за копирование. Далее логин-пароль в удаленной системе. BlogParam - это дополнительный параметр, который может понадобиться для определенный систем. В частности для Яндекса я передаю номер моего блога, чтобы не извлекать его каждый раз. MaxAttempts(int) - количество попыток кпирования поста, если неуспех, то бросаем это дело. Последний - PostType(Public/Private/OnlyFriends) - тип записи. Как видим, для хабра стоит Private, что означает, что я планирую подредактировать запись и вставить в нее кат, прежде чем делать общедоступной хабрачанцам.

3) Из внутренностей работы модуля рассмотрю класс MultiPost:
  1. namespace MultiPost
  2. {
  3.     /// <summary>
  4.     /// Copy all your posts to blog at http://wow.ya.ru
  5.     /// You have to assign an login and password inside constructor
  6.     /// </summary>
  7.     /// <remarks>
  8.     /// Based on pure code - http://ya.topbot.ru
  9.     /// </remarks>
  10.     [Extension("Copy all your posts to blogs", "0.1.1", "http://ya.topbot.ru")]
  11.     public class MultiPost
  12.     {
  13.         /// <summary>
  14.         /// Name of Module
  15.         /// </summary>
  16.         private static String sExtensionName = "MultiPost";
  17.  
  18.         static protected ExtensionSettings _settings = null;
  19.         /*....*/
  20.     }
  21. }
Как видим к атрибутах класса задаются настройки модуля, чтобы BlogEngine его воспринял как расширение. Конструктор достаточно простой, подписываемся на событие о размещении нового поста:
  1. static MultiPost()
  2. {
  3.     // create settings object. You need to pass exactly your
  4.     // extension class name (case sencitive)
  5.     ExtensionSettings settings = new ExtensionSettings(sExtensionName);
  6.     /*.......*/
  7.     ExtensionManager.ImportSettings(settings);
  8.     _settings = ExtensionManager.GetSettings(sExtensionName);
  9.     Post.Saved += new EventHandler<SavedEventArgs>(Post_Saved);
  10. }
И кода происходит новый пост, то обрабатываем его создавая новый поток для этих целей
  1. /// <summary>
  2.  /// Occurs on new post
  3.  /// <remarks>
  4.  /// It opens a new thread and executes
  5.  /// because it takes some time to complete.
  6.  /// </remarks>
  7.  /// </summary>
  8.  private static void Post_Saved(object sender, SavedEventArgs e)
  9.  {
  10.      if (e.Action != SaveAction.Insert)
  11.          return;
  12.  
  13.      IPublishable item = (IPublishable)sender;
  14.  
  15.      //(HttpContext.Current == null || !HttpContext.Current.Request.IsLocal)
  16.      if (item.IsVisible)
  17.      {
  18.          //getting new thread to do all crap
  19.           ThreadPool.QueueUserWorkItem(delegate {
  20.               PostCycle(item);
  21.           });
  22.      }
  23.  }
  24.  
  25.  /// <summary>
  26.  /// Main process to post
  27.  /// </summary>
  28.  /// <param name="item"></param>
  29.  public static void PostCycle(IPublishable item){
  30.      //перечисляем все блоги, что активны...
  31.      DataTable dt = _settings.GetDataTable();
  32.      //pure code, coz i cant change Categories class
  33.      string catsline = "";
  34.      foreach (Category c in item.Categories)
  35.      {
  36.          catsline += c.Title + ",";
  37.      }
  38.      for (int i = dt.Rows.Count - 1; i >= 0; i--)
  39.      {
  40.          SettingsCollection sets = new SettingsCollection(dt.Rows[i].ItemArray);
  41.          if (!sets.Enabled ||
  42.              (!String.IsNullOrEmpty(sets.CopyOnTags.Trim())
  43.              && catsline.Contains(sets.CopyOnTags))
  44.              )
  45.          {//not allowed to post
  46.              dt.Rows.RemoveAt(i);
  47.          }
  48.      }
  49.      //lets finish the others
  50.      for (int i = dt.Rows.Count - 1; dt.Rows.Count > 0; i--)
  51.      {
  52.          Thread.Sleep(5000);
  53.          if (i < 0)//new cycle...bad...bad
  54.          {
  55.              i = dt.Rows.Count;
  56.          }
  57.          try
  58.          {
  59.              SettingsCollection sets = new SettingsCollection(dt.Rows[i].ItemArray);
  60.              Type tBlog = Type.GetType("MultiPost.Blogs."+sets.BlogType);
  61.              Object theBlog = Activator.CreateInstance(tBlog,
  62.                  new object[1] { sets });
  63.              Type[] paramTypes = {
  64.                      typeof(IPublishable)
  65.                        };
  66.              Object[] ps = new object[1] { item };
  67.              MethodInfo mDefaults = tBlog.GetMethod("ProcessItem", paramTypes);
  68.              if ((Boolean)mDefaults.Invoke(theBlog, ps))//if success
  69.              {
  70.                  dt.Rows.RemoveAt(i);
  71.              }
  72.              //failed, lets continue
  73.          }
  74.          catch(Exception e)//toomanyattempts
  75.          {
  76.              dt.Rows.RemoveAt(i);
  77.          }
  78.  
  79.      }
  80.  }
В основном цикле, мы создаем объект класса, и вызываем нужную функцию размещения поста, передавая ей настройки. Как видно попытка разместить пост происходит каждые 5 секунд. Обработка объектов идет на объекте Rows, что некрасиво, но просто. Остальное Вы можете найти внутри выложенного архива. PS: Чтобы иметь возможность править настройки рекомендую заменить компонент admin/Extension Manager/Settings.ascx.cs на файл, вложенный архив. Там исправлена некоторая ошибка в функции редактирования настроек.


Метки: ,   Категории:BlogEngine | Code | life


Кто я?

Программист. Я слежу за блогосферой и знаю, как будет развиваться интернет. Когда у меня есть время я даже прилагаю для этого усилия. Подробнее

Последние комментарии

Не отображать

Topbot at FeedsBurner

Мои Твиты

Twitter февраля 19, 06:57
Диоксин угрожает планете! http://dlvr.it/QGwPKJ https://twitter.com/f1ashr/status/965480472095555584/photo/1

Twitter февраля 11, 18:36
@typographera @lustforcookies Но Москва на 70 лет моложе и была основана при Кутузове, когда начали железную дорогу прокладывать.

Twitter февраля 11, 17:24
@lustforcookies @typographera А ничего, что Коломна основана от силы 270 лет назад?

Twitter февраля 11, 14:07
Украина готовит на Евровидение песню с быками http://dlvr.it/QFw6Kb

Twitter февраля 9, 14:07
Чем белые отличаются от черных? http://dlvr.it/QFh4j8 https://twitter.com/f1ashr/status/961964675469426690/photo/1

Twitter февраля 6, 09:57
Победа свободы над здравым смыслом http://dlvr.it/QFD2S4 https://twitter.com/f1ashr/status/960814600802377731/photo/1

Twitter февраля 4, 15:32
В Твиттере начался обвал фолловеров http://dlvr.it/QDzJLg

Twitter февраля 3, 08:34
Суммарная капитализация криптовалют http://dlvr.it/QDqJyj

Twitter февраля 2, 10:52
Музыка про прилет Американцев на Марс http://dlvr.it/QDhYq7 https://twitter.com/f1ashr/status/959378893017726977/photo/1

Twitter января 28, 17:04
Разбор трилогии Матрицы (The Matrix Trilogy) http://dlvr.it/QD3h9W https://twitter.com/f1ashr/status/957660569674199040/photo/1

Twitter января 24, 13:20
Google Lunar X Prize - за 10 лет никто не смог запуститься на Луну http://dlvr.it/QCYSRW https://twitter.com/f1ashr/status/956154641753911298/photo/1

Twitter января 24, 04:03
Разбор сериала Туман или 4 всадника Апокалипсиса http://dlvr.it/QCVWXz https://twitter.com/f1ashr/status/956014586666287104/photo/1

Twitter января 23, 19:50
Пасха в Москве 2 сентября 2017 года http://dlvr.it/QCS7cD https://twitter.com/f1ashr/status/955890398899007488/photo/1

Twitter января 23, 19:50
Пользователи умудрились поломать гео-локацию в Инстаграм http://dlvr.it/QCS7hp

Twitter января 23, 16:29
В бюджете Америки появились лишние 2 трлн. долларов http://dlvr.it/QCQpPr

Twitter января 22, 14:45
Добродел обновился http://dlvr.it/QCGHwl https://twitter.com/f1ashr/status/955451259439407105/photo/1

Twitter января 22, 13:32
Рейтинг каналов и ботов Телеграмм http://dlvr.it/QCFt9v https://twitter.com/f1ashr/status/955432885657395200/photo/1

Twitter января 21, 16:15
Битва за Мосул в 360 с вертолета http://dlvr.it/QC84Xk https://twitter.com/f1ashr/status/955111520337248256/photo/1

Twitter января 21, 16:15
3 февраля стартует год огненного Петуха http://dlvr.it/QC84XQ

Twitter января 21, 16:15
Независимая Подмосковия http://dlvr.it/QC84X3

Мой твиттер

Копирайт

Все мысли, высказанные в блоге, являются моим мнением и за это мнение меня никто не забанит! Кроме того, никто не имеет право копировать материалы блога без использования ctrl+C/V!

© Copyright 2008