Avatar billede htm Nybegynder
28. juli 2005 - 13:53 Der er 18 kommentarer og
1 løsning

Binarysearch i filer med strenge

Hej

Jeg har lavet en lille binarysearch der søger i en fil efter et tal og returnerer postnummeret.
Filen er sorteret korrekt, og jeg kører med fastfeltformat.

Min lille funktion virker også fint, men kun til tal.

Nu vil jeg gerne have den til at fungere med strenge, og altså bogstaver. Hvordan gør jeg det?

jeg kunne også godt tænke mig kommentarere til min kode.

public int FileBinarySearch(string sFilename, long iRecordsize, int iStart, int iLength, int iSearchFor)
{
    FileInfo fi = new FileInfo(sFilename);
    FileStream fs = new FileStream(sFilename, FileMode.Open);
    BinaryReader br = new BinaryReader(fs);
   
    int iPosts = (int)(fi.Length/iRecordsize);
    int iCurrentPost = (iPosts/2);
    int iIndex = -1;
    int iMin=0,iMax=iPosts;
    if(iCurrentPost%2!=0) iCurrentPost-=1;
    int tmp=0;
    while(iIndex<0)
    {
        tmp=0;
        fs.Seek((iCurrentPost*iRecordsize)-iRecordsize+iStart,SeekOrigin.Begin);
        char[] ch = br.ReadChars(iLength);
        int iFoundInt = atoi(ch);
        if(iFoundInt==iSearchFor)
            iIndex = iCurrentPost;
        else if(iFoundInt > iSearchFor)
        {
            iMax = iCurrentPost;
            tmp = iMax - (iMax - iMin)/2;
            if(iCurrentPost==tmp) iCurrentPost--;
            else iCurrentPost=tmp;
            if(iMin+1 == iMax) break;
        }
        else
        {
            iMin = iCurrentPost;
            tmp = iMin + (iMax - iMin)/2;
            if(tmp==iCurrentPost) iCurrentPost++;
            else iCurrentPost = tmp;
            if(iMin+1 == iMax) break;
        }

        if(iCurrentPost > iPosts || iCurrentPost < 1 ) break;
    }
    br.Close();
    fs.Close();
    return iIndex;
}
Avatar billede arne_v Ekspert
28. juli 2005 - 14:06 #1
mindst kode:

konverter fra byte[] (eller char[] som det ser ud som om du bruger)
til String og sammenlign på dem

hurtigst kode:

lav noget som kan sammenligne byte arrays
Avatar billede arne_v Ekspert
28. juli 2005 - 14:07 #2
jeg tror iøvrigt at koden kan pyntes lidt

(men jeg vil først kunne komme med et forslag i aften)
Avatar billede htm Nybegynder
28. juli 2005 - 14:11 #3
Hvordan kan jeg sammenligne strenge, og finde ud af om det jeg søger ligger før eller efter min nuværende position?

Kan du give et eks. på hurtigst kode?

Det er også vær at bemærke at min fil indeholder en masse 0x00 som fyldstof til felterne så de får den rigtige længde på feltet. Så felternes data er som et chararray med /0.
Avatar billede Slettet bruger
28. juli 2005 - 21:24 #4
Det er lige nøjagtig det her jeg også mangler, glæder mig til arnes eks! :o)
Avatar billede arne_v Ekspert
28. juli 2005 - 21:42 #5
Jeg er først lige startet nu.

:-)
Avatar billede htm Nybegynder
28. juli 2005 - 21:50 #6
Super, jeg glæder mig til resultatet. Kan ikke rigtigt få det til at fungere selv.
Avatar billede arne_v Ekspert
28. juli 2005 - 22:23 #7
Her er et eksempel med string compareordinal:

using System;
using System.IO;
using System.Text;

class TestClass
{
    private const string FILE_NAME = @"C:\bin.dat";
    public static void CreateTestFile()
    {
        BinaryWriter bw = new BinaryWriter(new FileStream(FILE_NAME, FileMode.Create, FileAccess.Write));
        bw.Write(Encoding.Default.GetBytes("AAAA"));
        bw.Write(1);
        bw.Write(Encoding.Default.GetBytes("BBBB"));
        bw.Write(2);
        bw.Write(Encoding.Default.GetBytes("CCCC"));
        bw.Write(3);
        bw.Write(Encoding.Default.GetBytes("DDDD"));
        bw.Write(4);
        bw.Write(Encoding.Default.GetBytes("EEEE"));
        bw.Write(5);
        bw.Close();
    }
    public static int Find2(string s)
    {
        int reclen = 8;
        int nrec = (int)((new FileInfo(FILE_NAME)).Length / reclen);
        BinaryReader br = new BinaryReader(new FileStream(FILE_NAME, FileMode.Open, FileAccess.Read));
        int first = 0;
        int last = nrec - 1;
        while(first <= last)
        {
            int mid = (first + last) / 2;
            br.BaseStream.Position = mid * reclen;
            string fs = Encoding.Default.GetString(br.ReadBytes(4));
            int fi = br.ReadInt32();
            if(String.CompareOrdinal(fs,s) == 0)
            {
                return fi;
            }
            else if(String.CompareOrdinal(fs,s) < 0)
            {
                first = mid + 1;
            }
            else
            {
                last = mid - 1;
            }
        }
        br.Close();
        return -1;
    }
    public static void Main(string[] args)
    {
        CreateTestFile();
        Console.WriteLine(Find2("AAAA"));
        Console.WriteLine(Find2("BBBB"));
        Console.WriteLine(Find2("CCCC"));
        Console.WriteLine(Find2("DDDD"));
        Console.WriteLine(Find2("EEEE"));
        Console.WriteLine(Find2("XXXX"));
    }
}
Avatar billede arne_v Ekspert
28. juli 2005 - 22:23 #8
fil formatet er:

4 byte med bogstaver
4 byte med en integer
Avatar billede arne_v Ekspert
28. juli 2005 - 22:29 #9
Og med noget byte compare:

using System;
using System.IO;
using System.Text;

class TestClass
{
    private const string FILE_NAME = @"C:\bin.dat";
    public static void CreateTestFile()
    {
        BinaryWriter bw = new BinaryWriter(new FileStream(FILE_NAME, FileMode.Create, FileAccess.Write));
        bw.Write(Encoding.Default.GetBytes("AAAA"));
        bw.Write(1);
        bw.Write(Encoding.Default.GetBytes("BBBB"));
        bw.Write(2);
        bw.Write(Encoding.Default.GetBytes("CCCC"));
        bw.Write(3);
        bw.Write(Encoding.Default.GetBytes("DDDD"));
        bw.Write(4);
        bw.Write(Encoding.Default.GetBytes("EEEE"));
        bw.Write(5);
        bw.Close();
    }
    private static int BCompare(byte[] b1, byte[] b2, int n)
    {
        for(int i = 0; i < n; i++)
        {
            if(b1[i] < b2[i])
            {
                return -1;
            }
            else if(b1[i] > b2[i])
            {
                return 1;
            }
        }
        return 0;
    }
    public static int Find2(string s)
    {
        byte[] b = Encoding.Default.GetBytes(s);
        int reclen = 8;
        int nrec = (int)((new FileInfo(FILE_NAME)).Length / reclen);
        BinaryReader br = new BinaryReader(new FileStream(FILE_NAME, FileMode.Open, FileAccess.Read));
        int first = 0;
        int last = nrec - 1;
        while(first <= last)
        {
            int mid = (first + last) / 2;
            br.BaseStream.Position = mid * reclen;
            byte[] fb = br.ReadBytes(4);
            int fi = br.ReadInt32();
            if(BCompare(fb,b,4) == 0)
            {
                return fi;
            }
            else if(BCompare(fb,b,4) < 0)
            {
                first = mid + 1;
            }
            else
            {
                last = mid - 1;
            }
        }
        br.Close();
        return -1;
    }
    public static void Main(string[] args)
    {
        CreateTestFile();
        Console.WriteLine(Find2("AAAA"));
        Console.WriteLine(Find2("BBBB"));
        Console.WriteLine(Find2("CCCC"));
        Console.WriteLine(Find2("DDDD"));
        Console.WriteLine(Find2("EEEE"));
        Console.WriteLine(Find2("XXXX"));
    }
}
Avatar billede arne_v Ekspert
28. juli 2005 - 22:29 #10
Jeg ved dog ikke om den byte compare reelt er så meget hurtigere end string compare, men ...
Avatar billede htm Nybegynder
28. juli 2005 - 22:36 #11
Det ser rigtigt godt ud. Tak Arne.

Der var lige en god string compare funktion, som jeg ikke kendte, den vil jeg kunne bruge.

Jeg prøver at teste de to forskellige metoder, og se hvad der er hurtigst.

Super tak for hjælpen, jeg vender tilbage i morgen.
Avatar billede arne_v Ekspert
28. juli 2005 - 22:51 #12
Den normale Compare metode har detmed at ville være hjælpsom og AA svarer så til Å etc.
Avatar billede htm Nybegynder
28. juli 2005 - 22:59 #13
Ja det kan selvfølgelig godt skabe problemer - men til mit brug tror jeg ikke at det giver problemer.
Avatar billede arne_v Ekspert
28. juli 2005 - 23:01 #14
Fint nok men da jeg valgte "AAAA" som test string så var jeg ligesom nødt til
at vælge CompareOrdinal.
Avatar billede htm Nybegynder
28. juli 2005 - 23:06 #15
Ooooh - nu forstår jeg hvad du siger. Det er meget rart at man kan vælge om den skal være hjælpsom eller ej.
Avatar billede htm Nybegynder
29. juli 2005 - 09:51 #16
Hej Arne

Jeg har kun fået testet string compare. Den fungerer fint bortset fra at den sorterer 10 før 9, kan man lave om på det?
Avatar billede arne_v Ekspert
29. juli 2005 - 11:12 #17
10 er jo før 9 - alfabetisk

hvis du sammenligner int.Parse(s1) med int.Parse(s2) må du få en numerisk sammenligning
Avatar billede htm Nybegynder
01. august 2005 - 09:22 #18
Tak for hjælpen.

Jeg fik den sorteret anderledes, så det var ikke noget problem. Men stringcompare virker ellers fint.

Har desværre ikke fået testet bytecompare.

Vil du smide et svar ?
Avatar billede arne_v Ekspert
01. august 2005 - 09:28 #19
kommer her
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