EMZETT.
Login

Kurz: begin puts teile(10, 2) puts teile(1, 0) rescue ZeroDivisionError => e puts “Fehler: #{e.message}” else puts “kein Fehler” ensure puts “ensure läuft immer” end

Teil des Kurses Ruby

Kapitel 8 von 12 im Kurs Ruby (Abschnitt „Praxis“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Exceptions

def teile(a, b)
  raise ZeroDivisionError, "Division durch null" if b.zero?
  a / b
end
 
begin
  puts teile(10, 2)
  puts teile(1, 0)
rescue ZeroDivisionError => e
  puts "Fehler: #{e.message}"
else
  puts "kein Fehler"
ensure
  puts "ensure läuft immer"
end
 
def versuche
  yield
rescue TypeError, ArgumentError => e
  puts "#{e.class}: #{e.message}"
end
versuche { Integer("x") }
versuche { 1 + nil }
versuche { [1, 2].fetch(9) } rescue puts "IndexError (Modifikator-rescue)"
 
puts (Integer("zzz") rescue -1)
 
versuche = 0
begin
  versuche += 1
  raise "kaputt" if versuche < 3
  puts "klappte im Versuch #{versuche}"
rescue
  retry                       # erneut versuchen
end

Ausgabe:

5
Fehler: Division durch null
ensure läuft immer
ArgumentError: invalid value for Integer(): "x"
TypeError: nil can't be coerced into Integer
IndexError (Modifikator-rescue)
-1
klappte im Versuch 3

Fehlerklassen bilden eine Hierarchie: Exception > StandardError > ArgumentError, TypeError, NameError, ZeroDivisionError, IOError … rescue ohne Klasse fängt StandardError. Fange nie Exception pauschal.

class AppFehler < StandardError; end
class NichtGefunden < AppFehler
  def initialize(was) = super("#{was} nicht gefunden")
end
 
begin
  begin
    raise NichtGefunden, "Nutzer 7"
  rescue AppFehler => e
    raise RuntimeError, "umgepackt"   # Ursache wird automatisch gemerkt
  end
rescue => e
  puts "#{e.message} <- #{e.cause.class}: #{e.cause.message}"
end
 
x = catch(:fertig) do
  10.times { |i| throw :fertig, i if i == 3 }
  :nie
end
puts x

Ausgabe:

umgepackt <- NichtGefunden: Nutzer 7 nicht gefunden
3

Dateien

require "tmpdir"
Dir.mktmpdir do |dir|
  pfad = File.join(dir, "demo.txt")
  File.write(pfad, "Zeile 1\nZeile 2\n")
  File.open(pfad, "a") { |f| f.puts "Zeile 3"; f.print "Zeile 4\n" }
 
  puts File.read(pfad).length
  p File.readlines(pfad, chomp: true)
  File.foreach(pfad) { |z| print z.length, " " }
  puts
  File.open(pfad) do |f|
    puts f.gets
    puts f.readline
    p f.eof?
  end
  p File.exist?(pfad), File.file?(pfad), File.directory?(dir), File.size(pfad)
  p File.basename(pfad), File.extname(pfad), File.basename(pfad, ".txt"), File.dirname("/a/b/c.rb")
  Dir.mkdir(File.join(dir, "unter"))
  File.write(File.join(dir, "unter", "x.rb"), "")
  p Dir.glob("**/*", base: dir).sort
  File.delete(pfad)
  p File.exist?(pfad)
end

Ausgabe:

32
["Zeile 1", "Zeile 2", "Zeile 3", "Zeile 4"]
8 8 8 8
Zeile 1
Zeile 2
false
true
true
true
32
"demo.txt"
".txt"
"demo"
"/a/b"
["demo.txt", "unter", "unter/x.rb"]
false

Das Block-Muster (File.open(...) { |f| ... }) schließt die Datei automatisch.

JSON, CSV und YAML

require "json"
require "csv"
require "yaml"
 
daten = { "name" => "Mia", "alter" => 17, "hobbys" => ["Lesen", "Judo"], "adresse" => nil }
json = JSON.generate(daten)
puts json
puts JSON.pretty_generate(daten)
zurueck = JSON.parse(json)
puts zurueck["hobbys"][1]
sym = JSON.parse(json, symbolize_names: true)
puts sym[:name]
begin
  JSON.parse("{kaputt")
rescue JSON::ParserError
  puts "ungültiges JSON"
end
puts [1, "a", nil, true].to_json, { a: 1 }.to_json
 
csv = "name,punkte\nMia,120\nTom,80\n"
tabelle = CSV.parse(csv, headers: true)
tabelle.each { |z| puts "#{z['name']}: #{z['punkte']}" }
puts CSV.generate { |c| c << ["x", "a,b"]; c << [1, 2] }
 
puts({ "titel" => "Test", "liste" => [1, 2] }.to_yaml)
p YAML.load("a: 1\nb:\n  - x\n  - y\n")

Ausgabe:

{"name":"Mia","alter":17,"hobbys":["Lesen","Judo"],"adresse":null}
{
  "name": "Mia",
  "alter": 17,
  "hobbys": [
    "Lesen",
    "Judo"
  ],
  "adresse": null
}
Judo
Mia
ungültiges JSON
[1,"a",null,true]
{"a":1}
Mia: 120
Tom: 80
x,"a,b"
1,2
---
titel: Test
liste:
- 1
- 2
{"a"=>1, "b"=>["x", "y"]}

Reguläre Ausdrücke

text = "Mia (mia@example.com) und Tom (tom@test.de)"
p text =~ /\w+@\w+\.\w+/
p text.scan(/\w+@\w+\.\w+/)
if (m = text.match(/(?<name>\w+) \((?<mail>[^)]+)\)/))
  puts "#{m[:name]} -> #{m[:mail]}"
end
p "2025-03-15".match?(/\A\d{4}-\d{2}-\d{2}\z/)
puts "a1b22c333".gsub(/\d+/) { |z| "<#{z.length}>" }
puts "Hallo Welt".sub(/(\w+) (\w+)/, '\2 \1')
p "a, b;c".split(/[,;]\s*/)
p $~.nil?, "x" !~ /y/
if "Datum: 15.03.2025" =~ /(\d+)\.(\d+)\.(\d+)/
  puts "#{$3}-#{$2}-#{$1}"
end

Ausgabe:

5
["mia@example.com", "tom@test.de"]
Mia -> mia@example.com
true
a<1>b<2>c<3>
Welt Hallo
["a", "b", "c"]
true
true
2025-03-15

Zeit und Datum

require "time"
require "date"
t = Time.utc(2025, 3, 15, 14, 30, 0)
puts t, t.year, t.strftime("%d.%m.%Y %H:%M"), t.wday, t.yday, (t + 86400).day
puts Time.parse("2025-03-15 10:00:00 UTC").hour
d = Date.new(2025, 3, 15)
puts d + 20, d >> 1, d.saturday?, d.strftime("%A"), (Date.new(2025, 12, 24) - d).to_i
puts Date.valid_date?(2025, 2, 30), Date.leap?(2024)

Ausgabe:

2025-03-15 14:30:00 UTC
2025
15.03.2025 14:30
6
74
16
10
2025-04-04
2025-04-15
true
Saturday
284
false
true

Merke

  • begin / rescue / else / ensure / end, raise, retry; eigene Fehlerklassen von StandardError
  • Dateien: File.read/write/open/foreach, Block schließt automatisch
  • json, csv, yaml gehören zur Standardbibliothek
  • Reguläre Ausdrücke: =~, match, scan, gsub mit benannten Gruppen
  • Time und Date für Zeitrechnung

Übungsaufgabe

Lies eine CSV mit Namen und Punkten, berechne den Durchschnitt und schreibe das Ergebnis als JSON.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Module, Mixins und Enumerable

Weiter: Metaprogrammierung und Ruby-Idiome

Alle Kapitel: Ruby im Überblick