|
Kurodayooo
Anmeldungsdatum: 21. Januar 2021
Beiträge: Zähle...
|
HAllo,
Ich muss mithilfe eines Shellscriptes in einer cvs Datei auf eine jeweilige Spalte zugreifen. In diesem Fall die Spalte wo die ISBN Nummern stehen. Die Aufgabe lautet:
Es sollen alle in der Datenbank eingetragenen ISBNs geprueft werden. Diese sind ¨entweder zehn- oder dreizehnstellig und dürfen nur Ziffern enthalten (Ausnahme: die letzte Stelle der zehnstelligen ISBN kann ein ’X’ enthalten, das der 10 entspricht) Bindestriche sind zu ignorieren. Für jede zehnstellige ISBN mit den ¨Ziffern z0z1z2...z9 pruefen Sie bitte, ob folgende Bedingung wahr ist: ¨ z0 + 2z1 + 3z2 + 4z3 + 5z4 + 6z5 + 7z6 + 7z7 + 9z8 + 10z9 mod 11 = 0
Bei Verletzung dieser Bedingung geben Sie die fehlerhafte ISBN mit einer deskriptiven Fehlermeldung aus, genauso bei falscher Groeße oder falschen Ziffern. Die ISBN mit falscher Groeße oder falschen Ziffern konnte ich ausgeben. Jedoch macht mir die modulo prüfung probleme, da ich nicht genau weiß wie ich auf die einzelnen Stellen der Zahlen zufgreifen kann. Mein derzeitiger Code ist (Nur der Teil dewsegen kein exit 0):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | if [ "$1" = "isbn" ] && [ "$2" = "" ]; then
isb1="[0-9]{10}"
isb2="[0-9X]"
isb3="[0-9]-[0-9]{2,3}-[0-9]{5,6}-[0-9]" # Alle regulären Ausdrücke für die ISBN
isb4="[0-9]{3}-[0-9]-[0-9]{6}-[0-9]{2}-[0-9]"
isb5="[0-9]{13}"
isb="$isb1|$isb2|$isb3|$isb4|$isb5"
let1="[A-W][Y-Z]"
echo "Error: fehlerhafte ISBN:";
isbn="$isb1|$isb2|$isb3"
awk -v nr=$isb -v b=$let1 -F ';' '{if( $8 ~ nr && length($8)!=10 && length($8)!=13 || $8 ~ b) {print $8}}' bib.csv # ISBN falscher Länge und Zeichen augegeben
awk -v nu=$isbn s=0 -F ';' '{
for (i=1; i<=10; i++) {
if(i==1) sum=0
if($i == "X" ){ sum=sum+(10*i)}
else { sum=sum+($i*i) }
if( sum%11!=0 && i==10 && $8 ~ nu && length($8)==10) {print substr($8,i,1)}
}
}' bib.csv
|
Mit substr will ich jede einzelne Ziffer "ausschneiden". Aber ich bekomme den Fehler: awk: fatal: cannot open file -F' for reading.
Wo liegt der Fehler? oder gibt es noch einen besseren Ansatz?
Entschuldigung für die Formatierung und Vielen Dank im Voraus.
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11278
|
Kannst du mal ein Beispiel für die Zeilen in der CSV-Dateien zeigen? Ist die Aufgabe das als Shell-Skript umzusetzen oder ist es egal, wenn man da externe Tools mit eigener Skriptsyntax wie awk nutzt?
|
|
Kurodayooo
(Themenstarter)
Anmeldungsdatum: 21. Januar 2021
Beiträge: Zähle...
|
seahawk1986 schrieb: Kannst du mal ein Beispiel für die Zeilen in der CSV-Dateien zeigen? Ist die Aufgabe das als Shell-Skript umzusetzen oder ist es egal, wenn man da externe Tools mit eigener Skriptsyntax wie awk nutzt?
Ja es sollte als Shell-Skript umzusetzen sein. Bsp:
Die Datei ist in:
Titel, Autor, Schriftreihe, Kategorie, Jahr, Verlag, Seiten, ISBN unterteilt. Die Spalten werden durch das ; angegeben. Eine Beispiel Zeile:
Ausweitung der Kampfzone; Michel Houellebecq; Erfurter Theologische Schriften; Militaria; 2008; Heyne; 133; 886777631 $8 steht somit für die isbn Spalte.
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11278
|
Sind da wirklich Leerzeichen nach den ; in der Datei? Wer macht denn sowas? Kurodayooo schrieb: Jedoch macht mir die modulo prüfung probleme, da ich nicht genau weiß wie ich auf die einzelnen Stellen der Zahlen zufgreifen kann.
Bei einem Bash String kann man einen Substring an der Position x mit der Länge k so extrahieren - vgl. https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html:
Mit substr will ich jede einzelne Ziffer "ausschneiden". Aber ich bekomme den Fehler: awk: fatal: cannot open file -F' for reading.
Wo liegt der Fehler?
In Zeile 18 fehlt ein -v vor dem s=0. Mal grob rein mit den Mitteln der Bash:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | #!/bin/bash
function checksum_isbn10 {
local isbn="$1"
local -i checksum=0
for n in {0..9}
do
value="${isbn:$n:1}"
[ value == 'X' ] && value=10
checksum+=$(( "$value" * (n + 1) ))
done
[ $(( checksum % 11 )) -eq 0 ]
}
while read -r line
do
IFS=';' parts=($line)
raw_isbn="${parts[7]}"
isbn="${raw_isbn//[ -]/''}"
len="${#isbn}"
echo "checking ISBN '$isbn'"
[[ "$len" -eq 10 || "$len" -eq 13 ]] || {
echo "ISBN '$isbn' hat ungültige Länge $len"
continue
}
[[ "$isbn" =~ ^[0-9]{13}$ || "$isbn" =~ ^[0-9]{9}[0-9X]$ ]] && continue || {
echo "ISBN '$isbn' enthält ungültige Zeichen"
continue
}
checksum_isbn10 "$isbn" || echo "Ungültige Checksumme für ISBN10 '$isbn'"
done < bib.csv
|
|
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17630
|
Eine Handvoll gültiger und 2-3 ungültiger ISBN-Nummern wäre nett.
|
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17630
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 | #!/bin/bash
#
#
isbn=${1:-886777631}
len=${#isbn}
echo Länge $len
fail () {
msg=$1
code=$2
echo $msg
exit $code
}
(( len != 9 && len != 13 )) && fail "Länge stimmt nicht, muss 9 oder 13 sein, ist $len" 1
echo fein, Länge stimmt, ist $len
# entmuellt=$(echo $isbn | sed 's/[^0-9]//g')
entmuellt=${isbn//[^0-9]/}
if [[ x$entmuellt != x$isbn ]]
then
fail "ISBN enthält nicht nur Ziffern etwas mehr Aufmerksamkeit bitte, Parameter war: $isbn" 2
else
echo fein, Nur Ziffern eingereicht
fi
# Prüfziffernalgo:
# z0 + 2z1 + 3z2 + 4z3 + 5z4 + 6z5 + 7z6 + 7z7 + 9z8 + 10z9 mod 11 = 0
digits=($(echo $isbn | sed 's/./ &/g'))
faktor=(1 2 3 4 5 6 7 7 9 10)
# echo digits ${digits[@]}
# echo ${faktor[@]}
sum=0
for i in {0..8}
do
d=${digits[i]}
f=${faktor[i]}
p=$((d*f))
echo $d x $f = $p
((sum+=p))
done
echo $sum
pz=$((sum % 11))
echo Prüfziffer $pz
# (( pz == ${digits[9]} ))
|
Was man mit der Prüfziffer macht - bei mir kommt 10 raus, und ob die Prüfsumme bei 13-stelligen ISBN anders gebildet wird, ist mir nicht klar geworden. Muss es vielleicht, statt "+ 7z6 + 7z7" heißen "+ 8z7"? seahawk1986 hat es, glaube ich, so gemacht. So extrahiert man die ISBN, wenn sie immer der letzte Wert der Zeile ist:
| row="Ausweitung der Kampfzone; Michel Houellebecq; Erfurter Theologische Schriften; Militaria; 2008; Heyne; 133; 886777631"
echo ${row//*;/}
886777631
|
Die Funktion zum Testen einer ISBN gehört von dem Code, der eine bestimmte Datei durchforstet, separiert. Dann muss man sie nicht neu schreiben, wenn man morgen ISBN aus einer Datenbank prüft, oder einzeln von Konsole. Man kann aber natürlich, wenn alles nur eine Übung ist - und wer will schon ISBN kontrollieren? - den ganzen Teil nach der Failfunktion zu einer Funktion testIsbn machen, und unten das Parsen für eine Datei anhängen und von da aus wiederholt den Test aufrufen. Die ganzen echos müssen noch raus.
|
|
Kurodayooo
(Themenstarter)
Anmeldungsdatum: 21. Januar 2021
Beiträge: 5
|
user_unknown schrieb: Eine Handvoll gültiger und 2-3 ungültiger ISBN-Nummern wäre nett.
gültig: 3730306693, 3932180062, 345387322X, 886777631,347835630X, 3889718922 ungültig: Falsche Länge: 71125002750 Falsche Zeichen: OCLC:600948291 Falsche ISBN Rechnung (ausgedacht): 1234567891, 361875624, ... sollte eigentlich mit fast jeder ausgedachten Zahl funktionieren.
|
|
Kurodayooo
(Themenstarter)
Anmeldungsdatum: 21. Januar 2021
Beiträge: 5
|
Was man mit der Prüfziffer macht - bei mir kommt 10 raus, und ob die Prüfsumme bei 13-stelligen ISBN anders gebildet wird, ist mir nicht klar geworden. Muss es vielleicht, statt "+ 7z6 + 7z7" heißen "+ 8z7"? seahawk1986 hat es, glaube ich, so gemacht.
Die 13-stelligen müssen nicht geprüft werden. Ja stimmt, es muss 8z7 heißen!
Ist auch nur als Übung gedacht.
|
|
Kurodayooo
(Themenstarter)
Anmeldungsdatum: 21. Januar 2021
Beiträge: 5
|
Erstmal danke an euch beide, für eure Lösungsvorschläge. Muss nur erstmal schauen wie ich die Lösungen in mein Programm am besten einbinde.
|
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17630
|
Kurodayooo schrieb:
Die 13-stelligen müssen nicht geprüft werden. Ja stimmt, es muss 8z7 heißen!
Ist auch nur als Übung gedacht.
Ah, dann braucht man kein Faktorarray sondern kann gleich mit dem Index arbeiten. Ich habe den Code umgebaut mit einer aufgeilten Testfunktion:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 | #!/bin/bash
#
#
fail () {
msg=$1
code=$2
echo $msg
exit $code
}
pruefsum () {
isbn=$1
digits=($(echo $isbn | sed 's/./ &/g'))
# echo ${digits[@]}
sum=0
for i in {0..8}
do
d=${digits[$i]}
if [[ d == X ]]
then
d=10
fi
f=$((i+1))
p=$((d*f))
# echo -n $d x $f = $p
((sum+=p))
done
# echo -n "sum: " $sum
pz=$((sum % 11))
# echo $pz
return $pz
}
checkIsbn () {
isbn=${1:-886777631}
len=${#isbn}
# echo Länge $len
(( len != 9 && len != 13 )) && fail "Länge stimmt nicht, muss 9 oder 13 sein, ist $len bei isbn: $isbn" 1
# echo fein, Länge stimmt, ist $len
# entmuellt=$(echo $isbn | sed 's/[^0-9X]//g')
entmuellt=${isbn//[^0-9X]/}
if [[ x$entmuellt != x$isbn ]]
then
fail "ISBN enthält nicht nur Ziffern etwas mehr Aufmerksamkeit bitte, Parameter war: $isbn" 2
# else
# echo fein, Nur Ziffern eingereicht
fi
pruefsum $isbn
pz=$?
if (( pz != 0 ))
then
# fail "Prüfsumme stimmt nicht, war: $pz, erwartet: 0" 3
echo "Prüfsumme stimmt nicht, war: $pz, erwartet: 0"
else
echo OK
fi
}
# testfaelle=(3730306693 393218062 345387322X 886777631 347835630X 3889718922 71125002750 OCLC:600948291 1234567891 361875624)
testfaelle=(373030693 393218062 345387322X 886777631 347835630X 388971892 71125002750 OCLC:600948291 1234567891 361875624)
for tf in ${testfaelle[@]}
do
echo -n $tf" "
checkIsbn $tf
done
echo "expected: OK OK OK ..."
|
Für Serientests erweist sich der Ansatz mit fail u. exit als schlecht, als beim ersten Fail dann Schluss ist. Das verwendete return, gilt zu beachten, funktioniert nur im Byterange - man kann es also hier benutzen, um eine Prüfziffer die %11 immer < 11 ist zurückzugeben. Das ein trailing X 10 bedeutet hatte ich überlesen. Die Länge stimmt bei den meisten Testfällen trotzdem nicht.
|