Du må være registrert og logget inn for å kunne legge ut innlegg på freak.no
X
LOGG INN
... eller du kan registrere deg nå
Dette nettstedet er avhengig av annonseinntekter for å holde driften og videre utvikling igang. Vi liker ikke reklame heller, men alternativene er ikke mange. Vær snill å vurder å slå av annonseblokkering, eller å abonnere på en reklamefri utgave av nettstedet.
  11 5153
Som et lite eksperiment prøver jeg å lage en mod 10-kalkulator. Jeg googlet og lest i hele kveld, uten å finne noen gode eksempler på hvordan det skal gjøres. (Har skummet igjennom det meste som har med Luhn algoritmen å gjøre etc)

Jeg har funnet en nettside som gjør jobben, http://www.dlsoft.com/services/CheckCalc/Default.aspx, men jeg vil altså lage en tilsvarende applikasjon, med kun mod 10-kalkulasjon.

Det jeg finner plenty av, er hvordan kredittkortvalidering fungerer, men de fleste eksempler er basert på prefikser som visse kortfirmaer bruker osv.

Er det noen som har tips til hva jeg bør søke på?

Edit:

Bruker Windows Forms.

Kanskje dette er litt mer forståelig for de som ikke er kjent med Luhn, men C#. Det er dette jeg vil utføre. Tallet tastes inn i en tekstboks, og ved button_click så kalkuleres kontrollsifferet til en annen tekstboks eller label.


1. For this example, we will use a barcode containing the data 12345678912. Starting from the left side of the bar code, add together every other digit, ignoring the check digit. Add the first, third, fifth, seventh, ninth, and eleventh digits:

1+3+5+7+9+2=27

2. Multiply the result from step 1 by 3:

27*3=81

3. Add together the remaining digits. Add the second, fourth, sixth, eighth, tenth, and twelfth digits:

2+4+6+8+1=21

4. Add the results of steps 2 and 3:

81+21=102

5. Find the number which, when added to the result from step 4, will generate a number that is evenly divisible by 10:

102 + n = 110
n = 8

The number 8 is the mod10 check digit for this arrangement of digits.
Vis hele sitatet...
Sist endret av bronsky; 15. februar 2011 kl. 21:53.

Kode

public int GetMod10Digit(string barCode)
        {
            int first = 0;
            int second = 0;
            int i = 0;
            foreach (char num in barCode)
            {
                i++;
                double floating = (double)i / 2;
                int floating2 = i / 2;
                if (floating == floating2)
                {
                    first += int.Parse(num.ToString()); //regner med at barCode er et tall, hvis ikke, får vi Exception
                }
                else
                {
                    second += int.Parse(num.ToString());
                }
            }
            first *= 3;
            int result = first + second;
            bool run = true;
            int n = 0;
            while (run)
            {
                if ((result + n).ToString().Last() == '0') run = false;
                n++;
                if (n> 10000) return -1;
            }
            return n;
        }
Utestet, men bør virke
Det kan lønne seg å teste ja. Fant to feil i koden - den første if-testen første til at first og second ble byttet om, og n-variabelen ble inkrementert en ekstra gang før den ble returnert.

Kode

        // Version by Jannis with two bugfixes by tormaroe
        public int GetMod10Digit(string barCode)
        {
            int first = 0;
            int second = 0;
            int i = 0;
            foreach (char num in barCode)
            {
                i++;
                double floating = (double)i / 2;
                int floating2 = i / 2;
                if (floating != floating2)
                {
                    first += int.Parse(num.ToString());
                }
                else
                {
                    second += int.Parse(num.ToString());
                }
            }
            first *= 3;
            int result = first + second;
            bool run = true;
            int n = 0;
            while (run)
            {
                n++;
                if ((result + n).ToString().Last() == '0') run = false;                
                if (n > 10000) return -1;
            }
            return n;
        }
Jeg synes det er ganske morsomt at du ikke har brukt modulo-operatoren, ettersom det er det det dreier seg om her. På tide å lære noe nytt! Ta en titt på min versjon:

Kode

        // Refactored to use modulo
        public int GetMod10Digit_v2(string barCode)
        {
            int first = 0;
            int second = 0;
            int i = 0;
            foreach (char num in barCode)
                if (++i % 2 != 0)
                    first += int.Parse(num.ToString());
                else
                    second += int.Parse(num.ToString());
            int result = (first * 3) + second;
            int n = 0;
            while (true)
                if ((result + ++n) % 10 == 0) 
                    return n;
        }
Sitat av tormaroe Vis innlegg
Det kan lønne seg å teste ja. Fant to feil i koden - den første if-testen første til at first og second ble byttet om, og n-variabelen ble inkrementert en ekstra gang før den ble returnert.

Kode

        // Version by Jannis with two bugfixes by tormaroe
        public int GetMod10Digit(string barCode)
        {
            int first = 0;
            int second = 0;
            int i = 0;
            foreach (char num in barCode)
            {
                i++;
                double floating = (double)i / 2;
                int floating2 = i / 2;
                if (floating != floating2)
                {
                    first += int.Parse(num.ToString());
                }
                else
                {
                    second += int.Parse(num.ToString());
                }
            }
            first *= 3;
            int result = first + second;
            bool run = true;
            int n = 0;
            while (run)
            {
                n++;
                if ((result + n).ToString().Last() == '0') run = false;                
                if (n > 10000) return -1;
            }
            return n;
        }
Jeg synes det er ganske morsomt at du ikke har brukt modulo-operatoren, ettersom det er det det dreier seg om her. På tide å lære noe nytt! Ta en titt på min versjon:

Kode

        // Refactored to use modulo
        public int GetMod10Digit_v2(string barCode)
        {
            int first = 0;
            int second = 0;
            int i = 0;
            foreach (char num in barCode)
                if (++i % 2 != 0)
                    first += int.Parse(num.ToString());
                else
                    second += int.Parse(num.ToString());
            int result = (first * 3) + second;
            int n = 0;
            while (true)
                if ((result + ++n) % 10 == 0) 
                    return n;
        }
Vis hele sitatet...
Veldig kult. Jeg skjønner at dette kanskje var litt vanskeligere enn jeg først forventet, men jeg ser det som god læring å kunne få til noe slikt på egenhånd.

Siden jeg ennå ikke er en racer på dette (jeg lærer best av eksempler), hvordan vil jeg kunne bruke metoden ved en button_click? Jeg trenger ikke nødvendigvis koden, men hva må jeg lese meg opp på for å forstå hvordan jeg kan bruke snippets som dette i en button click action?
Sitat av bronsky Vis innlegg
hvordan vil jeg kunne bruke metoden ved en button_click?
Vis hele sitatet...
Tja, antar at du har et windows forms prosjekt i Visual Studio, og et Form.
  1. Dra en textbox, en knapp og en label ut på formet
  2. Dobbeltklikk på knappen (du blir da tatt til kodefilen for formet, hvor det har blitt generert en tom metode for deg som vil bli kalt når knappen trykkes på)
  3. Lim inn mod10-metoden nedenfor click-metoden
  4. Inne i click-metoden kaller du den, f.eks. slik:

Kode

Label1.Text = GetMod10Digit(TextBox1.Text);
Done!
Tusen takk for hjelpen så langt!

Jeg får error "Cannot implicitly convert type "int" to "string".

Kode

private void button1_Click(object sender, EventArgs e)
        {

            
            label1.Text = GetMod10Digit(txtNumber.Text);
            
        }


        public int GetMod10Digit(string barCode)
        {
            int first = 0;
            int second = 0;
            int i = 0;
            foreach (char num in barCode)
                if (++i % 2 != 0)
                    first += int.Parse(num.ToString());
                else
                    second += int.Parse(num.ToString());
            int result = (first * 3) + second;
            int n = 0;
            while (true)
                if ((result + ++n) % 10 == 0)
                    return n;
        }


Mangler det en

Kode

Convert.ToInt32
eller noe et sted?

Prøvde dette, men det funket jo ikke som jeg ville, selvfølgelig:

Kode

string test = txtNumber.Text;

            Convert.ToInt32(test);

            GetMod10Digit(test);


            label1.Text = test.ToString();
Sist endret av bronsky; 17. februar 2011 kl. 15:11.
Ah, GetMod10Digit returnerer en int, og da feiler dette:

Kode

label1.Text = GetMod10Digit(txtNumber.Text);
Resultatet fra GetMod10Digit (int) tilordnes Text-propertien til label1, som er en string. Og C# vil ikke konvertere fra int til string implisitt.., man må være eksplisitt. Så da må du gjøre f.eks. slik:

Kode

label1.Text = GetMod10Digit(txtNumber.Text).ToString();
Kaller altså metoden ToString() (som alle objekter har) på resultatet fra GetMod10Digit før verdien tilordnes Text-propertien.

Si fra hvordan det går..

Her er en annen variant av mod10-funksjonen, for dem som har lyst til å utvide sin C#-kunnskap:

Kode

        public int GetMod10Digit_v3(string barCode)
        {
            var digits = barCode.ToCharArray().Select(c => int.Parse(c.ToString()));
            var first = digits.Where((num, index) => index % 2 == 0).Sum();
            var second = digits.Where((num, index) => index % 2 > 0).Sum();
            int result = (first * 3) + second;
            int n = 0;
            while ((result + ++n) % 10 != 0) ;
            return n;
        }
De første linjene som definerer digits, first og second ved hjelp av Linq er deklerative og mye mere elegante enn de tidligere eksemplene. Dette er eksempler på funksjonell programmering.

While-løkken uten body derimot er et eksempel på stygg kode med nesten usynlige sideeffekter. Å kunne forstå den er derimot viktig.

Kode

label1.Text = GetMod10Digit(txtNumber.Text).ToString();
Dette fungerte veldig bra. Men det er noe galt i kalkulasjonen. Jeg får nemlig ikke riktig kontrollsiffer.

Jeg har lagt kilden i min DropBox, hvis noen vil ta en titt:

http://dl.dropbox.com/u/10581843/ModCalc.zip
Kjørte koden din, og kode 12345678912 gir som forventet kontrollsiffer 8 - det samme som mine tre versjoner av algoritmen. Får ikke du det? Har du andre eksempler på koder og sifre?
Sitat av tormaroe Vis innlegg
Kjørte koden din, og kode 12345678912 gir som forventet kontrollsiffer 8 - det samme som mine tre versjoner av algoritmen. Får ikke du det? Har du andre eksempler på koder og sifre?
Vis hele sitatet...
Riktig kontrollsiffer for 12345678912 skal være 2.

2*2= 4
1*1= 1
9*2 = 18= 9
8*1 =8
7*2 = 14 = 5
6*1 = 6
5*2 = 10 = 1
4*1 = 5
3*2 = 6
2*1 = 2
1*2 = 1

= 48 (nærmeste opp til hele 10, altså 50, er 2) ergo kontrollsiffer 2.

Bekreftes også av denne kalkulatoren:

http://bavister.org/tools/genLuhn.php

On second thought:

Nærmeste hele 10 fra 8 er jo også 2, så kan det være et eller annet som mangler på slutten av utregningen?
Sitat av bronsky Vis innlegg
1. For this example, we will use a barcode containing the data 12345678912. Starting from the left side of the bar code, add together every other digit, ignoring the check digit. Add the first, third, fifth, seventh, ninth, and eleventh digits:

1+3+5+7+9+2=27
Vis hele sitatet...
Dette eksempelet ignorer ikke Check digit iallefall (siste sifferet).
Sitat av bronsky
Riktig kontrollsiffer for 12345678912 skal være 2.

2*2= 4
1*1= 1
9*2 = 18= 9
8*1 =8
7*2 = 14 = 5
6*1 = 6
5*2 = 10 = 1
4*1 = 5
3*2 = 6
2*1 = 2
1*2 = 1

= 48 (nærmeste opp til hele 10, altså 50, er 2) ergo kontrollsiffer 2.
Vis hele sitatet...
Her ganger du med to istedenfor 3 som eksempelet i første posten din sier.


Dette er forøvrig en one-liner i Python som er konsistent med forklaringa i første eksempelet (inkludert at siste sifferet ignoreres):

Kode

10-(sum(map(int,list(str(n)))[:-1:2])*3+sum(map(int,list(str(n)))[1::2]))%10

Edit: så på kalkulatoren du postet i siste innlegg Bronsky: den ganger annenhvert tall med to, og fjerner ikke siste tallet. Samme som din utregning i siste innlegget, men ikke samme som du postet i første innlegget.

Sitat av bronsky Vis innlegg
2*2= 4
1*1= 1
9*2 = 18= 9
8*1 =8
7*2 = 14 = 5
6*1 = 6
5*2 = 10 = 1
4*1 = 5
3*2 = 6
2*1 = 2
1*2 = 1
Vis hele sitatet...
Denne er heller ikke konsekvent. Du kjører modulo 9 på alle resultat tydeligvis, men 18%9 er faktisk 0, ikke 9.
Sist endret av DumDiDum; 20. februar 2011 kl. 11:40.
Når jeg leser igjennom tråden min, ser jeg at det har vært mye slurv, og at jeg rett og slett brukte feil utgangspunkt for å spørre om hjelp. La meg korrigere dette med ny kilde og noen flere eksempler.

Dette er pythonkode fra Wikipedia:

Kode

def luhn_check(cc):
    """Source: http://en.wikipedia.org/wiki/Luhn_algorithm"""
    num = [int(x) for x in str(cc)]
    res = sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[-1::-2]]) % 10
    if res <> 0:
        res = 10 - res
    return res


Eksempel 1:

Tallet vi vil finne kontrollsiffer til, er 12345. Vektingen av tallene er 2,1,2,1 osv. og man begynner fra høyre:

12345
21212

Gang opp, og ta tverrsummen av tallene:

5 * 2 = (1 + 0) = 1
4 * 1 = 4
3 * 2 = 6
2 * 1 = 2
1 * 2 = 2

= 15

La tallet til høyre stå igjen, og trekk dette fra 10.

10 - 5 = 5

Kontrollsifferet er 5.

_______

Eksempel 2:

5498524181
1212121212

1 * 2 = 2
8 * 1 = 8
1 * 2 = 2
4 * 1 = 4
2 * 2 = 4
5 * 1 = 5
8 * 2 = (1 + 6) = 7
9 * 1 = 9
4 * 2 = 8
5 * 1 = 5

= 54

La tallet til høyre stå igjen, og trekk dette fra 10.

10 - 4 = 6

Kontrollsifferet er 6.

_______

Eksempel 3:

865489
121212

9 * 2 = (1 + 8) = 9
8 * 1 = 8
4 * 2 = 8
5 * 1 = 5
6 * 2 = (1 + 2) = 3
8 * 1 = 8

= 41

La tallet til høyre stå igjen, og trekk dette fra 10.

10 - 1 = 9

Kontrollsifferet er 9.


Jeg håper dette gjør oppgaven litt klarere. Takk for alle innspill så langt.

Edit:

Jeg har nå funnet et oppsett som funker. Problemet er bare det, at den returnerer 10 istedenfor 0. Ellers funker alt.

Hvor legger jeg inn dette?

Kode

public static int GetMod10Digit(string data)
            {
                int sum = 0;
                bool odd = true;
                for (int i = data.Length - 1; i >= 0; i--)
                {
                    if (odd == true)
                    {
                        int tSum = Convert.ToInt32(data[i].ToString()) * 2;
                        if (tSum >= 10)
                        {
                            string tData = tSum.ToString();
                            tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString());
                        }
                        sum += tSum;
                    }
                    else
                        sum += Convert.ToInt32(data[i].ToString());
                    odd = !odd;
                }
                return (((sum / 10) + 1) * 10) - sum;
            }
Løsningen er nå endelig klar:

private void button1_Click(object sender, EventArgs e)
{
textBox2.Text = GetMod10Digit(textBox1.Text).ToString();

}

public static int GetMod10Digit(string data)
{
int sum = 0;
bool odd = true;
for (int i = data.Length - 1; i >= 0; i--)
{
if (odd == true)
{
int tSum = Convert.ToInt32(data[i].ToString()) * 2;
if (tSum >= 10)
{
string tData = tSum.ToString();
tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString());
}
sum += tSum;
}
else
sum += Convert.ToInt32(data[i].ToString());
odd = !odd;
}

int result = (((sum / 10) + 1) * 10) - sum;
return result % 10;
}