R&D - Przykład przygotowań do stworzenia algorytmu. Korzystanie z COM

Artur Protasewicz

Wstęp

Innowacyjność jest motorem gospodarki. Można czasem znaleźć oferty pracy dla programistów w działach R&D. Być może dla współczesnego młodego programisty lub kandydata na studia techniczne R&D to skrót doskonale znany. Skrót pochodzi od angielskiego Research And Development, czyli po polsku Badania i Rozwój. Warto może wspomnieć, że działy badawczo-rozwojowe istniały w Polsce przed transformacją ustrojową w roku 1989. Wiele zakładów przemysłowych je posiadało. Pozostały po nich liczne wynalazki i patenty.

Artykuł powstał na bazie rzeczywistego przykładu, nad którym autor pracował w jednej z firm. Chodziło o wyszukiwanie gotowych, już raz napisanych odpowiedzi email mentora na pytania uczestników szkoleń typu e-learning (internetowych) w oparciu o statystyczną analizę treści pytań i odpowiedzi. Algorytm miał uszeregować najlepiej pasujące (skorelowane) odpowiedzi. Dzięki temu po modyfikacji przez mentora mogły one być udzielane szybciej. Udało się uzyskać bardzo efektywne rozwiązanie dla obszernego zbioru emaili.

Tu przykładowe teksty zaczerpnięto ze zbioru wątków forum 4programmers.net. Autor wybrał nieco ponad 10 wątków z forum Off-Topic. Są one w postaci dokumentów programu MS Word - jeden wątek, jeden dokument. W dokumentach jest wiele tabel, z których trzeba wydobyć tekst i punktację, aby uzyskać formę łatwiejszą do przetwarzania przez potencjalny algorym szukający korelacji pytanie-odpowiedź.

Pokazano dwa programy narzędziowe. Wprowadzają czytelnika w komunikację między programami w modelu COM i ich wsadowe uruchamianie.

Załącznik - zbiór postów z forum w formacie MS Word

zbior_tekstow_z_forum.zip

Dodawanie referencji w C# - podstawy

Menu Visual Studio
add_reference.png

Lista dostępnych bibliotek
word_com.png

Referencje w Solution Explorerze
biblioteki2.png

Wpisanie do sekcji using

// Word 2010, Reference COM: Microsoft Word 14.0 Object Library
using Word = Microsoft.Office.Interop.Word;
 
// Word 2003, Reference COM: Microsoft Word 11.0 Object Library
// using Word; 

Program do wyodrębniania (ekstrakcji) tekstów z tabel Word

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
 
// Word 2010, Reference COM: Microsoft Word 14.0 Object Library
using Word = Microsoft.Office.Interop.Word;
 
// Word 2003, Reference COM: Microsoft Word 11.0 Object Library
// using Word; 
 
namespace Extractor
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.GetLength(0) != 4) // sprawdzenie ilości argumentów programu             
            {
                return;
            }
 
            string docPath = args[0];                   // argument 1: ścieżka folderu z dokumentami źródłowymi Word
 
            string filename = docPath + "\\" + args[1]; // argument 2: nazwa przetwarzanego dokumentu
 
            string topicId = args[2];                   // argument 3: sygnatura grupy postów (dowolna)
 
            string destPath = args[3];                  // argument 4: ścieżka folderu z dokumentami tekstowymi
                                                        // po ekstrakcji
 
            const int MaxPoints = 50;                // założone ograniczenie z góry liczby głosów oddanych na post
 
            char[] separators = { '\r', '\n' };      // separatory oddzielające wiersze
 
            object openFileName = new object();      // nazwa przetwarzanego dokumentu
            openFileName = filename;
            object separator = new object();         // separator komórek tabel
            separator = "\n";
            object nestedTables = new object();      // flaga nakazująca przetwarzanie tabel zagnieżdżonych
            nestedTables = true;
            ArrayList lines = new ArrayList();       // lista wierszy
 
            // KillWordProcesses();                  // zakończenie wszystkich uruchominych procesów aplikacji Word
                                                     // podczas tworzenia programu ta funkcja bywa przydatna
 
            Word.Application word = new Word.Application();       // uruchomienie aplikacji Word
 
            word.Visible = false;                                 // nie wyświetlaj aplikacji Word
 
            Word.Document doc = null;                             // dokument otwierany w aplikacji Word
 
            try
            {
                doc = word.Documents.Open(ref openFileName);      // otwarcie dokumentu
            }
            catch (Exception ex)
            {
                // tu: obsługa wyjątku
            }
 
            foreach (Word.Table t in doc.Tables)                                        // przekształć wszystkie tabele 
                                                                                        // w dokumencie na tekst 
            {
                string s = t.ConvertToText(ref separator, ref nestedTables).Text;       // konwersja tabel na tekst
 
                string[] range = s.Replace(('\v').ToString(), "").Split(separators);    // usunięcie zbędnych znaków 
                                                                                        // i podział na wiersze
 
                lines.AddRange(range);                                                  // dodanie wszystkich wierszy 
            }                                                                           // pojedynczej tabeli do listy
 
            object saveFileName = new object();
 
            FileInfo fi = new FileInfo(filename);
            saveFileName = destPath + '\\' + fi.Name.Substring(0, fi.Name.Length - (".doc").Length) + "-Kopia.doc";
 
            try
            {
                doc.SaveAs(ref saveFileName); // zapisanie dokumetu pod inną nazwą
            }
            catch (IOException ex)
            {
                // tu: obsługa wyjątku
            }
 
#pragma warning disable 0467
 
            // ostrzeżenie kompilatora przed możliwością dwojakiego zinterpretowania kodu
            // tu jednak kompilator wybiera poprawnie i ostrzeżenie można wyłączyć
 
            //Warning: Ambiguity between method 'Word._Application.Quit(ref object, ref object, ref object)' 
            //and non-method 'Word.ApplicationEvents4_Event.Quit'.
 
            word.Quit();  // zamknięcie Worda
 
#pragma warning restore 0467
 
            int iPost = 1;    // numer kolejny postu w wątku na forum
 
            int iBegin = 0;   // wiersz z początkiem kolejnego postu w tekście niepodzielonym
 
            int k;            // numer kolejny wiersza w tekście niepodzielonym
 
            // iteracja po wszystkich wierszach dokumentu
 
            for (int li = 0; li < lines.Count; li++)
            {
 
                // znajdowanie tekstu zawierającego liczbę głosów oddanych na post
                // teksty postaci np. "4Głosuj na ten post"
                // ograniczenie MaxPoints = 50 jest przyjęte z bardzo dużym zapasem
                // zwykle oddawanych jest 0, 1, 2, 3 głosy, najczesciej zero
                // iteracja 0..50 byłaby bardzo nieefektywna przy innym rozkładzie ilości
                // oddawanych głosów, tu jest wystarczajaca, przerywana przez break
                // po znalezieniu tekstu
 
                for (int j = 0; j < MaxPoints; j++)
                {
                    if (lines[li].ToString() == j.ToString() + "Głosuj na ten post")
                    {
                        // utworzenie nazwy pliku (pojedynczy post) do dalszego przetwarzania
 
                        string filename2 = destPath + @"\Post_" + topicId + "_" + iPost.ToString("D04") + "_" + j.ToString("D02") + ".txt";
 
                        StreamWriter sw = new StreamWriter(filename2);   // otwarcie strumienia do zapisu pliku
 
                        for (k = iBegin; k <= li; k++)                   // iteracja po wierszach dokumentu bez tabel
                        {
                            string s = lines[k].ToString().Trim();       // obcięcie skrajnych spacji i znaków sterujących wiersza
 
                            // pozostawienie tylko odstępów jednej spacji miedzy słowami
 
                            while (s.Contains("  "))
                            {
                                s.Replace("  ", " ");
                            }
 
                            if (s != j.ToString() + "Głosuj na ten post")
                            {
                                try
                                {
                                    sw.WriteLine(s);
                                }
                                catch (IOException ex)
                                {
                                    // tu: obsługa wyjątku
                                }
                            }
                        }
 
                        sw.Close();
 
                        FileInfo fi2 = new FileInfo(filename2);
 
                        Console.WriteLine(fi2.Name); // po pomyślnym przetworzeniu i zapiasaniu postu kolejne iteracje 
                                                     // wypisz nazwę pliku na ekranie konsoli 
                        iPost++;                      
                        iBegin = k;
                        break;
                    }
                }
            }
 
            // zakończenie wszystkich uruchominych procesów aplikacji Word
            // podczas tworzenia programu ta funkcja bywa przydatna
 
            // KillWordProcesses(); 
        }
 
        // funkcja kończenia wszystkich procesów Worda
        // w fazie tworzenia algorytmu przetwarzania
        // zdarzają sie pomyłki prowadzące do uruchomienia
        // Worda zbyt wiele razy
 
        static void KillWordProcesses()
        {
            Process[] processList = Process.GetProcesses();
 
            foreach (Process process in processList)
                if (process.ProcessName.ToLower() == "winword")
                    process.Kill();
        }
    }
}

Program do wsadowego uruchamiania ekstrakcji

using System;
using System.Diagnostics;
using System.IO;
 
namespace Batch
{
    class Program
    {
        /// <summary>Parametry użyte podczas testów</summary>
        /// <param name="args[0]">C:\Users\Artur\Desktop\BatchExtractor\Extractor\zbior_tekstow_z_forum</param>
        /// <param name="args[1]">C:\Users\Artur\Desktop\BatchExtractor\Extractor\bin\Release\Extractor.exe</param>
        /// <param name="args[2]">C:\Users\Artur\Desktop\Extracted</param>
        static void Main(string[] args)
        {
            if (args.GetLength(0) != 3) // sprawdzenie ilości argumentów programu
            {
                return;
            }
 
            string docsPath = args[0]; // argument 1: ścieżka do folderu 
                                       // zawierającego dokumenty Word
 
            string execFile = args[1]; // argument 2: ścieżka do programu 
                                       // wyodrębniającego tylko tekst z dokumentów Word
 
            string destPath = args[2]; // argument 3: ścieżka do folderu
                                       // z wyodrębnionymi tekstami
 
            // dodanie sygnatury czasowej do nazwy folderu destPath
 
            DateTime dt = DateTime.Now.ToLocalTime();
            destPath += " " + dt.ToShortDateString() + " " + dt.ToString(@"hh\.mm\.ff");
 
            if (!Directory.Exists(destPath))
            {
                try
                {
                    Directory.CreateDirectory(destPath);
                }
                catch (Exception ex)
                {
                    // tu: obsługa wyjątku
                }
            }
 
            // utworzenie i przetworzenie listy plików Word
 
            string[] docFiles = Directory.GetFiles(docsPath, "*.doc");
            int i = 0;
 
            foreach (string f in docFiles)
            {
                i++;
 
                FileInfo fi = new FileInfo(f);
 
                Console.WriteLine("Podaj sygnaturę dla:\n" + fi.Name);
                string s = Console.ReadLine();
                string topicId = i.ToString("D02") + '_' + s;
 
                Process p = new Process();
                p.StartInfo.UseShellExecute = false;       // nie używaj funcji ShellExecute
 
                p.StartInfo.RedirectStandardOutput = true; // kieruj wyjście programu uruchamianego 
                                                           // wsadowo do programu uruchamiającego
 
                p.StartInfo.CreateNoWindow = true;         // nie twórz okna programu uruchamianego
 
                p.StartInfo.FileName = execFile;           // uruchamiany program
 
                // przekazanie argumentów
 
                p.StartInfo.Arguments = '"' + fi.DirectoryName + "\" \"" + fi.Name + "\" " + i.ToString("D02") + '_' + s + ' ' + '"' + destPath + '"';
 
                try
                {
                    p.Start();                                      // uruchomienie programu 
 
                    string output = p.StandardOutput.ReadToEnd();   // pobranie przekierowanego
                                                                    // strumienia wyjściowego
 
                    p.WaitForExit();                                // oczekiwanie na zakończenie procesu
 
                    Console.WriteLine(output);                      // wypisanie pobranego strumienia wyjścia
                                                                    // w oknie programu uruchamiającego
                }
                catch (Exception ex)
                {
                    // tu: obsługa wyjątku
                }
 
                /*
                // w czasie tworzenia programu warto ograniczyć ilość prztwarzanych plików
 
                if (i == 3) 
                {
                    break;
                }
                 */ 
            }
 
            // usunięcie kopii dokumentów Word
 
            try
            {
                string[] DeleteSet = Directory.GetFiles(destPath, "*Kopia.doc");
                foreach (string f in DeleteSet)
                {
                    File.Delete(f);
                }
            }
            catch (Exception ex)
            {
                // tu: obsługa wyjątku
            }
 
            // zkończenie przetwarzania
 
            Console.WriteLine("Przetwarzanie wsadowe zakończone.");
            Console.WriteLine("Naciśnij dowolny klawisz...");
            Console.ReadKey();
        }
    }
}

0 komentarzy