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

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](//4programmers.net/Download/207758/513)

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

```csharp 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();
    }
}

}

<h3>Program do wsadowego uruchamiania ekstrakcji</h3>
```csharp
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