Avatar billede the_julle Praktikant
04. januar 2012 - 13:31 Der er 16 kommentarer og
1 løsning

Søgning i Word-tabel = laaangsom...

Hej alle.

Jeg skal gennemsøge en masse tabeller i et word 2003 dokument. Jeg har efterhånden en kode som fungerer, men det tager alt for lang tid.

Jeg har eksperimenteret med at skrive den importeret data til Excel med det samme kontra at gemme al dataen i en liste og først skrive til Excel når hele word-dokumentet er gennemlæst.

Uden liste, skrivning direkte til Excel:
http://i162.photobucket.com/albums/t244/the_julle/uden_liste.png

Med liste, importer først alt, skriv så til Excel:
http://i162.photobucket.com/albums/t244/the_julle/med_liste.png


Der er dog ingen forskel i hvor lang tid det tager at eksekvere disse to metoder, hvilket må betyde at det ikke er skrivningen til Excel der er flaskehalsen - det er gennemsøgning af word-dokumentet.

Er der en anden måde at søge i Word-tabeller? Det kan da ikke være rigtig det skal tage så lang tid...

Hvad er lang tid:
I mit test word-dokument har jeg 12 tabeller, hver med ca. 30-40 celler. Jeg importerer tekst fra 2 celler i hver tabel, altså i alt fra 24 celler. Dette tager ca. 20 sekunder!


Håber i kan hjælpe.
Avatar billede the_julle Praktikant
04. januar 2012 - 13:34 #1
Hvis jeg slet ikke skriver til Excel tager det ca. 19 sekunder at gennemløbe koden. Det er altså helt sikkert word-dokumentet der er flaskehalsen.
Avatar billede supertekst Ekspert
04. januar 2012 - 13:53 #2
Du er velkommen til at sende en kopi af dit Word dok., så vil jeg gerne forsøge med en VBA-kode..

@-adresse under min profil.
Avatar billede the_julle Praktikant
04. januar 2012 - 13:56 #3
Mange tak for dit venlige forslag.

Jeg har dog allerede selv lavet en VBA-kode. Jeg ville dog helst lave en windows-applikation i C#, da jeg også skal importere fra andre .txt-filer. Og ikke mindst for at få lidt øvelse i C#.
Avatar billede supertekst Ekspert
04. januar 2012 - 14:02 #4
ok..
Avatar billede janus_007 Nybegynder
04. januar 2012 - 14:09 #5
Nu kender jeg ikke så meget til Word-import og du har ikke postet meget kode.

Kan du poste hvordan wd.Tables bliver til?

Har du prøvet med wd.Tables.ToList() ?

Jeg tænker at den måske læser fysisk fra filen hvergang.
Avatar billede the_julle Praktikant
04. januar 2012 - 14:53 #6
Jeg kan da ige vise hvordan jeg åbner word-dokumentet:

// MS word applikation
Microsoft.Office.Interop.Word._Application wApp = new Microsoft.Office.Interop.Word.Application();

// Bruger angiver fil-placering
openFD.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            openFD.Title = "Vælg word-fil";
            openFD.FileName = "";
            openFD.Filter = "Word 2003|*.doc|Word 2007->|*.docx|Alle filer|*.*";

            if (openFD.ShowDialog() != DialogResult.Cancel)
            {
                chosenFile = openFD.FileName;
            }

// Åbner det angivne dokument.
_Document wd = wApp.Documents.Open(chosenFile); 


Og herefter kører koden, som vist i første indlæg. wd.Tables bliver til i samme omgang som foreach-løkken. Den er med andre ord ikke oprettet andre steder.

Jeg har ikke prøvet wd.Tables.ToList(), det vil jeg kigge lidt nærmere på. Jeg tror du har fuldstændig ret i, at der læses fysisk fra filen hvergang...
Avatar billede the_julle Praktikant
04. januar 2012 - 15:44 #7
Jeg har fundet en måde at indlæse hele word-dokumentet i en string.

string allText;
wd.ActiveWindow.Selection.WholeStory();
wd.ActiveWindow.Selection.Copy();
IDataObject data = Clipboard.GetDataObject();
allText = data.GetData(DataFormats.Text).ToString();

Det tager ca. 0,3 sekund og så er hele dokumentet indlæst i stringen. Men hvordan pokker sortere man så i sådan en mega lang string, så man kun får det man ønsker.

Nogen der sidder inde med brand gode ideer...
Avatar billede janus_007 Nybegynder
04. januar 2012 - 21:14 #8
Kan du smide en kopi af worddokumentet op et sted, så kan jeg lige prøve :)
Avatar billede the_julle Praktikant
05. januar 2012 - 11:37 #9
Jeg har sendt dig en besked.

Den string jeg får ud ser sådan her ud:
\r\n1.\tTEST\t\t\t\t\r\n\tNummer\t(nr)\t10\t\t\t\r\n\tMåske lidt snik snak her\tDa fe\tfvv\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\tHus1\tF 12 C\r\n\tOBS: \t\r\n\t\t\t\t\t\r\n\r\n1.\tTEST\t\t\t\t\r\n\tNummer\t(nr)\t11\t\t\t\r\n\tNoget andet snik snak\tGE\tGG\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\tHus1\tF 13 F\r\n\tOBS: \tF 10\r\n\tLille note \tF 11\t\t\t\r\n\r\n1.\tTEST\t\t\t\t\r\n\tNummer\t(nr)\t\t\t\t\r\n\tOg så videre\tTra\tTro\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\r\n\tHus1\tF 13\t\t\t\r\n\tOBS:\tF 13\t\t\t\r\n\t\tF 14\t\t\t\r\n\r\n

Den er selvfølgelig meget forkortet, men bør stadig vise princippet i tabellerne.

Der jeg så skal bruger er følgende.
10 , F 12 C
11 , F 13 F , F10 , F11
  , F 13 , F13 , F14

hvor hvert komma angiver en ny celle i excel.

Nogen ideer til hvordan pokker man får disse ting ud af stringen?
Avatar billede the_julle Praktikant
05. januar 2012 - 11:38 #10
Desuden kan nævnes, at denne string indeholder 3 tabeller.
Avatar billede the_julle Praktikant
05. januar 2012 - 11:45 #11
Jeg har lavet en funktion(som jeg normalt bruger i en anden sammenhæng), som kan konvertere stringen til følgende:

1. TEST    \n\n Nummer (nr) 10  \n\n Måske lidt snik snak her Da fe fvv  \n\n    \n\n    \n\n Hus1 F 12 C\n\n OBS:  \n\n    \n\n\n\n1. TEST    \n\n Nummer (nr) 11  \n\n Noget andet snik snak GE GG  \n\n    \n\n    \n\n Hus1 F 13 F\n\n OBS:  F 10\n\n Lille note  F 11  \n\n\n\n1. TEST    \n\n Nummer (nr)    \n\n Og så videre Tra Tro  \n\n    \n\n    \n\n Hus1 F 13  \n\n OBS: F 13  \n\n  F 14


Gør det mon tingene nemmere?
Avatar billede bvli Praktikant
06. januar 2012 - 13:02 #12
Hvis du kunne bruge docx/docm kan du kigge på nedenstående kode som inspiration. Det kan godt være at det virker som en stor mundfuld i første omgang. Men find evt. Open XML Productivity tool og lur hvordan et OoXml Wordprocessing Document er bygget op, så er det egentlig ret simpelt.

Husk at referere DocumentFormat.OpenXml assembly'et fra OpenXML SDK'et. (http://www.microsoft.com/download/en/details.aspx?id=5124)


using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using DocumentFormat.OpenXml.Packaging;

namespace B.Testing
{
    class Program
    {
        //Fjern mellemrum i http her - de er der da eksperten ellers laver det til et link som ikke kan kopieres.
        private static readonly XNamespace w = "h t t p ://schemas.openxmlformats.org/wordprocessingml/2006/main";

        static void Main(string[] args)
        {
            Program p = new Program();
            Debug.Assert(args.Length == 1);
            p.Run(args[0]);
        }

        private void Run(string fileName)
        {
            Debug.Assert(File.Exists(fileName));
            using (WordprocessingDocument d = WordprocessingDocument.Open(fileName, false))
            {
                //Lad os få indholdet af dokumentet som xml.
                var x = GetXDocument(d.MainDocumentPart);
               
                //Find tabellerne i dokumentet
                var tables = from t in x.Element(w + "document").Element(w + "body").Descendants(w + "tbl")
                            select t;

                int i = 0;
                foreach (var t in tables)
                {
                    Console.WriteLine("Checking table " + ++i);
                    //Find rækkerne i tabellen
                    var rows = t.Elements(w + "tr");
                    foreach (var r in rows)
                    {
                        var cells = r.Elements(w + "tc");
                        //I hver celle hent teksten (bemærk - jeg ignorerer afsnit her - henter kun den rene tekst)
                        foreach (var c in cells)
                        {
                            string text = string.Join(" ", from cellText in c.Descendants(w + "t") select cellText.Value);
                            Console.Write(string.Format("{0, 20}", text));
                        }
                        Console.WriteLine();
                    }
                }
            }
        }

        private static XDocument GetXDocument(OpenXmlPart part)
        {
            using (Stream s = part.GetStream())
            {
                XDocument d = null;
                if (s.Length > 0)
                {
                    using (XmlReader partXmlReader = XmlReader.Create(s))
                    {
                        d = XDocument.Load(partXmlReader);
                    }
                }
                else
                {
                    d = new XDocument();
                }
                return d;
            }
        }
    }
}
Avatar billede the_julle Praktikant
06. januar 2012 - 14:03 #13
@janus_007:
Tak for din mail. Det er pænt af dig at komme med et løsningsforslag! Jeg vender lige tilbage når det er afprøvet.

@bvli:
Tusind tak.

Jeg har endnu ikke prøvet at lave mit eget program ud fra dette, men det skal helt sikkert prøves. Jeg har lige et spørgsmål. Nu hvor jeg bruger Interop, så er min vurdering at grunden til det tager så lang tid at søge igennem tabellerner er, at der hele tiden skiftes mellem word og c#. Gør openXml ikke det? Eller læser du alle tabeller ind på én gang vha. denne kommando:

var tables = from t in x.Element(w + "document").Element(w + "body").Descendants(w + "tbl")

Igen, tak.
Avatar billede bvli Praktikant
06. januar 2012 - 14:29 #14
OpenXml SDK'et bruger slet ikke Word. Så du arbejder direkte på filerne, og kan køre programmet uden at have Word installeret på maskinen.

Jeg har arbejdet en del med interop til Word - og vurderer ikke, at det er interop, der skulle være problemet i forbindelse med dårlig performance. (men selvfølgelig skal der ventes på at Wordprogrammet starter op - og det koster altså tid).

Jeg læser hele dokumentets tekst ind her:

var x = GetXDocument(d.MainDocumentPart);

Resten er ren xml-gymnastik. I øvrigt kan openxml sdk'et også bruges typestærkt, jeg bryder mig bare mest om xml-måden, da jeg hackede rundt i openxml (og wordml) før sdk'et blev frigivet.
Avatar billede the_julle Praktikant
06. januar 2012 - 16:44 #15
I mit program er tidsrøveren de linjer kode, som vises her;
http://i162.photobucket.com/albums/t244/the_julle/uden_liste.png

Mener du, at det burde være en "hurtigt" operation? For så har jeg i hvert fald gjort noget forkert. Jeg kan bare ikke se, at det kan gøres anderledes med interop?

Angående din kode: Aha, det er smart. Og sådan som jeg læser din kode kan du stadig tilgå hver tabel, og læse fra hver enkelt celle. Er det korrekt?
Avatar billede the_julle Praktikant
01. februar 2012 - 08:45 #16
Hej janus_007.

Hvis du opretter et svar får du point :)

Tak for hjælpen til jer alle.
Avatar billede the_julle Praktikant
24. februar 2012 - 10:26 #17
\\ Lukker tråden nu.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester