From fabe7642fa9cb01ee76b74b4f2e6ef18c7863b2a Mon Sep 17 00:00:00 2001 From: Elex Date: Thu, 4 Apr 2024 13:44:51 +0900 Subject: [PATCH] 2024-04-04 --- .project | 11 ++ doc/class.md | 20 +++ src/01_literal.rb | 113 +++++++++++++++++ src/02_variable.rb | 16 +++ src/03_control_flow.rb | 115 +++++++++++++++++ src/04_method.rb | 88 +++++++++++++ src/05_module.rb | 81 ++++++++++++ src/06_exception.rb | 56 +++++++++ src/10_number.rb | 60 +++++++++ src/11_string.rb | 233 ++++++++++++++++++++++++++++++++++ src/12_array.rb | 92 ++++++++++++++ src/13_hash.rb | 108 ++++++++++++++++ src/14_range.rb | 38 ++++++ src/15_object.rb | 128 +++++++++++++++++++ src/16_set.rb | 61 +++++++++ src/17_time.rb | 151 ++++++++++++++++++++++ src/18_regexp.rb | 62 +++++++++ src/19_random.rb | 45 +++++++ src/21_file.rb | 263 +++++++++++++++++++++++++++++++++++++++ src/22_io.rb | 147 ++++++++++++++++++++++ src/23_http.rb | 38 ++++++ src/24_kernel.rb | 51 ++++++++ src/25_process_thread.rb | 108 ++++++++++++++++ src/31_cgi.rb | 8 ++ src/32_security.rb | 9 ++ src/33_logger.rb | 45 +++++++ src/34_socket.rb | 13 ++ src/35_compress.rb | 7 ++ src/36_base64.rb | 22 ++++ src/37_json.rb | 29 +++++ src/38_csv.rb | 98 +++++++++++++++ src/39_yaml.rb | 35 ++++++ src/41_dbi.rb | 12 ++ src/data_type.rb | 49 ++++++++ src/my.log | 1 + src/sample.csv | 3 + src/sample.json | 1 + src/sample.txt | 1 + src/sample.yaml | 4 + src/sample2.txt | 1 + 40 files changed, 2423 insertions(+) create mode 100644 .project create mode 100644 doc/class.md create mode 100755 src/01_literal.rb create mode 100755 src/02_variable.rb create mode 100755 src/03_control_flow.rb create mode 100755 src/04_method.rb create mode 100755 src/05_module.rb create mode 100755 src/06_exception.rb create mode 100755 src/10_number.rb create mode 100755 src/11_string.rb create mode 100755 src/12_array.rb create mode 100755 src/13_hash.rb create mode 100755 src/14_range.rb create mode 100755 src/15_object.rb create mode 100755 src/16_set.rb create mode 100755 src/17_time.rb create mode 100755 src/18_regexp.rb create mode 100755 src/19_random.rb create mode 100755 src/21_file.rb create mode 100755 src/22_io.rb create mode 100755 src/23_http.rb create mode 100755 src/24_kernel.rb create mode 100755 src/25_process_thread.rb create mode 100755 src/31_cgi.rb create mode 100755 src/32_security.rb create mode 100755 src/33_logger.rb create mode 100755 src/34_socket.rb create mode 100755 src/35_compress.rb create mode 100755 src/36_base64.rb create mode 100755 src/37_json.rb create mode 100755 src/38_csv.rb create mode 100755 src/39_yaml.rb create mode 100755 src/41_dbi.rb create mode 100755 src/data_type.rb create mode 100644 src/my.log create mode 100644 src/sample.csv create mode 100644 src/sample.json create mode 100644 src/sample.txt create mode 100644 src/sample.yaml create mode 100644 src/sample2.txt diff --git a/.project b/.project new file mode 100644 index 0000000..a890df1 --- /dev/null +++ b/.project @@ -0,0 +1,11 @@ + + + ruby-examples + + + + + + + + diff --git a/doc/class.md b/doc/class.md new file mode 100644 index 0000000..4a276f6 --- /dev/null +++ b/doc/class.md @@ -0,0 +1,20 @@ +# 클래스 + +```ruby +#!/usr/bin/ruby + +class Person + def initialize(first_name, last_name, age) + @first_name = first_name + @last_name = last_name + @age = age + end + + def name + "#@first_name #@last_name" + end +end + +steve = Person.new("Steve", "McQueen", 37) +puts steve.name +``` \ No newline at end of file diff --git a/src/01_literal.rb b/src/01_literal.rb new file mode 100755 index 0000000..8f15187 --- /dev/null +++ b/src/01_literal.rb @@ -0,0 +1,113 @@ +#!/usr/bin/ruby + +# 진위형 + +v = nil # `nil`은 null 또는 unknown입니다. +v = false #`nil`과 `false` 모두 거짓입니다. +v = true # `true`와 그 외의 값들은 모두 참입니다. + + +# 숫자형 + +## 정수형 + +i = 1234 +i = 1_234 # 읽기 쉽게 끊어 쓰기도 할 수 있어요. + +h = 0xaa # 16진수 +o = 0o252 # 8진수 +b = 0b10101100 # 2진수 +d = 0d170 # 0d로 시작하면 10진수입니다. + + +## 부동소수형 + +f = 12.34 +f = 1.234e-2 # 지수 표현 + +## 유리수형: 값 뒤에 'r'을 븥입니다. + +r = 2/3r # 2/3 +r = 1.5r + +## 복소수형: 허수 값 뒤에 'i'를 붙입니다. + +c = 1i # 0 + 1i + + +# 문자열 + +s = "이것은 문자열이다.\n" # 이스케잎 문자도 처리됩니다. +s = '이것도 문자열입니다.' # 이스케잎 문자는 처리 안됨 + +s = %q{이 역시도 문자열입니다.} # 이스케잎 문자는 처리 안됨 +s = %Q{또 문자열입니다.} # 이스케잎 문자도 처리됨 +s = %{그리고 또 문자열입니다.} # 이스케잎 문자도 처리됨 + +s = <1, "two"=>2, "three"=>3} # 해시는 키/값 쌍을'{}'로 묶어서 표현합니다. +hash = {one:1, two:2, three:3} # 이렇게도 쓸 수 있습니다. + + +# 레인지 + +(1..10) # 1 ~ 10까지 +(1...10) # 1 ~ 9까지 + +# 정규표현식 + +re = /foo/ # 정규표현식은 '/'로 묶어서 표현합니다.ARGF + +%r{foo} + +# 람다 + +-> {1 + 1} +-> (v) { 1 + v } + + +# 백틱 + +%x{echo Hello} # 쉘 명령을 실행할 때 사용됩니다. + +END{ + # https://ruby-doc.org/3.2.2/syntax/literals_rdoc.html +} diff --git a/src/02_variable.rb b/src/02_variable.rb new file mode 100755 index 0000000..7f1c78a --- /dev/null +++ b/src/02_variable.rb @@ -0,0 +1,16 @@ +#!/usr/bin/ruby + +v = 5 + + +=begin +* 지역 변수 : 지역 변수의 이름은 소문자와 '_'로 구성합니다. +* 인스턴스 변수 : 인스턴스 변수는 '@'으로 시작합니다. +* 클래스 변수 : 클래스 변수는 '@@'으로 시작합니다. +* 전역 변수 : 전역 변수는 '$'로 시작합니다. + +=end + +END{ + # https://ruby-doc.org/3.2.2/syntax/assignment_rdoc.html +} diff --git a/src/03_control_flow.rb b/src/03_control_flow.rb new file mode 100755 index 0000000..e569fdb --- /dev/null +++ b/src/03_control_flow.rb @@ -0,0 +1,115 @@ +#!/usr/bin/ruby + +a = 100 + +# if + +if a == 100 then + puts "It's 100." +end + +if a == 100 # 'then'은 생략해도 됩니다. + puts "It's 100." +end + +if a == 100 then # if - else + puts "It's 100." +else + puts "It's not 100." +end + +if a == 100 then # if - elsif - else + puts "It's 100." +elsif a == 200 then + puts "It's 200." +else + puts "It's not 100 nor 200." +end + + +# 삼항연산자 ?: + +v = (a==100) ? 1 : 2; + + +# unless + +unless a==10 then # 물론 then은 생략할 수 있고요. else문도 함깨 사용할 수 있습니다. + puts "not 10!" +end + +# if / unless 모디파이어 + +a += 1 if a.zero? # if나 unless를 표현식의 뒤에 붙여 사용할 수 있습니다. +a -=1 unless a.zero? + + +# case - when + +case a # switch-case 가 아니라 case-when입니다. + when 1,2 then puts "Need more" + when 3 then puts "Ok." + else puts "Enough." +end + + +case "1234" + when /^1/ then # 정규표현식을 사용해봅시다. + puts "It starts with one." + else + puts "It's not start with one." +end + + +# while + +while a<10 do # 'do'는 생략해도 됩니다. + a++ +end + +# until + +until a>10 do # 'do'는 생략해도 됩니다. + a++ +end + + +# for-in + +for value in [1,2,3] do # 'do'는 생략해도 됩니다. + puts value +end + + +# while / until 모디파이어 + +a += 1 while a<10 + +a -=1 until a<10 + +begin + a += 1 +end while a<10 # begin-end 블록으로 묶으면 가독성이 향상됩니다. + + +# break / next / redo + +while true do + # do something + next if a.even? # next를 사용해서 다음 차례로 진행합니다. + redo if b=100 # redo는 현재 차례를 다시 한 번 반복합니다. + break if a<10 # break를 사용해서 루프를 벗어납니다. +end + + +# loop + +loop do + # do something +end + + + +END{ + # https://ruby-doc.org/3.2.2/syntax/control_expressions_rdoc.html +} diff --git a/src/04_method.rb b/src/04_method.rb new file mode 100755 index 0000000..d4f94a5 --- /dev/null +++ b/src/04_method.rb @@ -0,0 +1,88 @@ +#!/usr/bin/ruby + +# 메서드 + +def do_something + puts "Hello" +end + +=begin +* 메서드 이름은 소문자와 숫자, 밑줄문자등을 사용합니다. 유니코드 문자도 괜찮습니다. +* '!' : 뭔가 주의가 필요한 메서드 뒤에는 '!'를 붙입니다. +* '?' : true/false를 반환하는 메서드 뒤에는 '?'를 붙입니다. +* '=' : 값을 할당하는 셋터 메서드 뒤에는 '='를 붙입니다. +=end + +def plus_something(x, y=1) # 매개변수를 받을 수 있습니다. y에는 기본값을 줘봤습니다. + x + y # 'return'을 사용하지 않아도, 제일 마지막 문장의 값이 반환됩니다. +end + +def get_something(*args) # 변수 이름 앞에 '*'을 붙여서 임의 길이의 매개변수를 전달 받을 수 있습니다. + puts "Hello" +end + +def get_block(&block) # 변수 이름 앞에 '&'을 붙여서 블록을 전달 받을 수 있습니다. + block.call(self) +end + +# 예외 처리 + +def my_method + begin + # 여기서 예외가 발생하면, + rescue + # 여기에서 예외를 처리합니다. + end +end + +def my_method # 이렇게도 작성할 수 있습니다. + # 여기서 예외가 발생하면, + rescue + # 여기에서 예외를 처리합니다. + else + # 예외가 발생하지 않았을 때 처리할 내용 + ensure + # 예외가 발생했든 말든 처리할 내용 +end + + +# 메서드 호출 + +my_method() + +my_method # 괄호는 없어도 됩니다. + +my_method(x,y) +my_method x, y + +array = [1,2,3] +my_method(*array) # my_method(1,2,3)과 같습니다. + +hash = {a:1,b:2} +my_method(**hash) # 해시에는 '**'를 사용합니다. + +my_method do # 메서드에 매개변수로 블록을 전달합니다. + # do something +end + +my_method { # 메서드에 매개변수로 블록을 전달합니다. + # do something +} + + + +a = [1,2,3] +b = ["a","b","c"] +c = a.append(*b).reverse # 메서드 호출을 연결 +puts c + +reg = /foo/ +"Hello".match(reg)&.values_at(1,2) # '&'를 붙이면 결과가 nil일 때에 이후 메서드가 실행되지 않도록 합니다. + + + + +END{ + # https://ruby-doc.org/3.2.2/syntax/methods_rdoc.html + # https://ruby-doc.org/3.2.2/syntax/calling_methods_rdoc.html +} diff --git a/src/05_module.rb b/src/05_module.rb new file mode 100755 index 0000000..ca9766c --- /dev/null +++ b/src/05_module.rb @@ -0,0 +1,81 @@ +#!/usr/bin/ruby + +# 모듈 + +module MyModule + def my_method + end +end + +module MyModule # 같은 이름으로 여러 번 정의해도 하나의 모듈로 통합됩니다. + alias aliased_method my_method +end + +module MyModule + remove_method :my_method +end + + +module OuterModule + module InnerModule + # 중첩도 됩니다. + end +end + +module OuterModule::InnerModule::GrandsonModule + # 상위 모듈이 이미 정의되어 있는 경우에는, 이렇게 정의할 수도 있습니다. +end + + + +# 클래스 + +class MyClass + # 클래스입니다. +end + +class AnotherClass < MyClass + # MyClass를 상속하는 클래스입니다. 부모 클래스를 지정하지 않으면, 기본적으로 Object를 상속합니다. + # 뭔가 새로운 상속 구조를 만들려면, BasicObject를 상속합니다. +end + + +## 상속 + +class A + def value + @value + end + def value=(value) + @value = value + end +end + +class B < A + def value + # super는 부모 클래스의 메서드에 현재 메서드의 매개변수를 전달해서 호출합니다. + # 전달할 매개변수 값을 지정하려면 super(1)와 같이 사용합니다. + 1 + super + end +end + +c = B.new +c.value = 1 +puts c.value + + + +## 싱글턴 패턴 + +class S + class << self + # ... + end +end + + + + +END{ + # https://ruby-doc.org/3.2.2/syntax/modules_and_classes_rdoc.html +} diff --git a/src/06_exception.rb b/src/06_exception.rb new file mode 100755 index 0000000..bd73676 --- /dev/null +++ b/src/06_exception.rb @@ -0,0 +1,56 @@ +#!/usr/bin/ruby + + +begin + # do something +rescue + # handle exception +end + +# 메서드 내에서 예외를 처리하려는 경우, +def my_method + # do something + rescue + # handle exception +end + + +begin + # ... +rescue => exception # 예외를 지역 변수로 받습니다. + warn exception.message + raise # re-raise the current exception +end + +begin + # ... +rescue ArgumentError # 예외를 종류 별로 처리할 수 있습니다. + # handle ArgumentError +rescue NameError + # handle NameError +rescue + # handle any StandardError +end + +begin + # ... +rescue ArgumentError, NameError # 여러 종류의 예외를 묶어서 처리할 수도 있습니다. + # handle ArgumentError or NameError +end + + +begin + # ... +rescue + # ... +else + # this runs only when no exception was raised +ensure + # ... +end + + + +END{ + # https://ruby-doc.org/3.2.2/syntax/exceptions_rdoc.html +} diff --git a/src/10_number.rb b/src/10_number.rb new file mode 100755 index 0000000..d547f67 --- /dev/null +++ b/src/10_number.rb @@ -0,0 +1,60 @@ +#!/usr/bin/ruby + +## Numeric + +## Integer + +i = 3 +p i.class # => Integer + +p i.anybits?(0b00000000) + +## Float + +f = 3.141592 +p f + +## Rational + +r = 1/3r +p r +puts "#{r.numerator} / #{r.denominator}" + +## Complex + +c = 10 + 3i +p c +puts "#{c.real} + #{c.imaginary}i" + + +## BigDecimal +require 'bigdecimal' + +decimal = (BigDecimal("1.2") - BigDecimal("1.0")) +p decimal # 0.2e0 + + +## Math + +p Math::PI +p Math::E + +p Math.sin(0.5) +p Math.log2(10) + + + +END { + # https://ruby-doc.org/3.3.0/Numeric.html + # https://ruby-doc.org/3.3.0/Integer.html + # https://ruby-doc.org/3.3.0/Float.html + # https://ruby-doc.org/3.3.0/Rational.html + # https://ruby-doc.org/3.3.0/Complex.html + # https://ruby-doc.org/3.3.0/exts/bigdecimal/BigDecimal.html + + # https://ruby-doc.org/3.3.0/Math.html + # https://ruby-doc.org/3.3.0/exts/bigdecimal/BigMath.html + + # https://ruby-doc.org/3.3.0/gems/matrix/Matrix.html + # https://ruby-doc.org/3.3.0/gems/matrix/Vector.html +} diff --git a/src/11_string.rb b/src/11_string.rb new file mode 100755 index 0000000..fb1b3bc --- /dev/null +++ b/src/11_string.rb @@ -0,0 +1,233 @@ +#!/usr/bin/ruby + +# 문자열 + + + + +## Substitution + +s = "Hello, there" +str = s.sub(/[aeiou]/, '*') # 패턴과 일치하는 첫 번째 부분을 다른 문자열로 대체합니다. +str = s.gsub(/[aeiou]/, '*') # 패턴과 일치하는 모든 부분을 다른 문자열로 대체합니다. +puts str + +## 공백 제거 +str = s.strip +str = s.lstrip +str = s.rstrip + +### 문자열을 배열처럼 + +str = "foo bar" +p str[0] +p str[0,3] + + +=begin +* length / size : 문자의 갯수 +* bytesize +* empty? +* count : 일치하는 부분 문자열의 갯수를 반환 + +* index +* rindex +* include? +* match +* match? +* start_with? +* end_with? + + +* encoding +* unicode_normalized? +* valid_encoding? +* ascii_only? + +* sum +* hash + + +* insert +* << + +* sub +* gsub +* succ +* next +* replace +* reverse +* setbyte +* tr +* tr_s +* dump +* undump + + +* capitalize +* downcase +* upcase +* swapcase + + +* encode +* unicode_normalize +* scrub +* force_encoding + + +* clear +* slice +* squeeze +* delete +* delete_prefix +* delete_suffix +* strip +* lstrip +* rstrip +* chomp +* chop +* chr +* byteslice + + + +* center +* concat +* prepend +* ljust +* rjust + +* to_s + +* bytes +* chars +* getbyte + +* lines +* partition +* rpartition +* split + +* scan +* unpack + + +* hex +* oct +* to_i +* to_f + + + + +* each_byte +* each_char +* each_codepoint +* each_line + + + +=end + + + +## String IO + +require 'stringio' + +io = StringIO.new +io.close # 열었으면 닫아야 합니다. + + +StringIO.open {|io| + # do something + # 블록이 끝나면 클로즈됩니다. +} + +str = < 3 +} +array.reject { + |a| a< 3 +} +array.delete_if { + |a| a> 4 +} +array.keep_if { + |a| a< 4 +} + + +array.at(0) +array.first() +array.last() +array.min() +array.max() +array.take(3) +array.drop(3) +array.shuffle() + + + + +END{ + # https://ruby-doc.org/3.3.0/Array.html +} diff --git a/src/13_hash.rb b/src/13_hash.rb new file mode 100755 index 0000000..eb59efa --- /dev/null +++ b/src/13_hash.rb @@ -0,0 +1,108 @@ +#!/usr/bin/ruby + +# 해시 + +## 해시 만들기 + +hash = {x: 100, y: 200} + +person = {name: 'Charlie', age: 13} + +hash = Hash.new +hash = Hash[] +hash = {} +hash = Hash[x: 100, y: 200] + + +## 아이템에 접근 +hash[:x] + +hash.delete(:x) + + + +def my_method(hash) + p hash +end +my_method(hash) +my_method(x: 100, y: 200) # 괄호 없이도 해시를 매개변수로 전달할 수 있습니다. + + +=begin +* any? +* empty? +* has_value? +* include? / has_key? / menber? / key? +* length / size +* value? + +=end + + + +=begin +* assoc +* fetch +* fetch_values +* key +* keys +* values +* values_at + +=end + + +=begin +* store +* merge +* update +* replace + + +* clear +* compact +* delete +* delete_if +* filter / select +* keep_if +* reject +* shift +* except +* slice + +=end + + +=begin +* each +* each_pair +* each_key +* each_value + +=end + + +=begin +* to_s +* to_a +* to_hash +* to_proc + +=end + + +=begin +* flatten : 배열로 변환합니다. +* invert : 키/값을 서로 바꿉니다. + +* transform_keys +* transform_values + +=end + + + + +END{ + # https://ruby-doc.org/3.3.0/Hash.html +} diff --git a/src/14_range.rb b/src/14_range.rb new file mode 100755 index 0000000..2b60dba --- /dev/null +++ b/src/14_range.rb @@ -0,0 +1,38 @@ +#!/usr/bin/ruby + +# 레인지 + +(1..4).to_a + +(1...4).to_a + +Range.new('a'..'d').to_a + + +=begin +* begin : 시작값 +* count +* end +* exclude_end? +* first +* last +* max +* min +* minmax +* size + +* include? + +=end + +=begin +* each +* step + +=end + + + +END{ + # https://ruby-doc.org/3.3.0/Range.html +} diff --git a/src/15_object.rb b/src/15_object.rb new file mode 100755 index 0000000..194985f --- /dev/null +++ b/src/15_object.rb @@ -0,0 +1,128 @@ +#!/usr/bin/ruby + +# 객체 + +class Person + def initialize(first_name, last_name, age) + @first_name = first_name + @last_name = last_name + @age = age + end + + def name + "#{@first_name} #{@last_name}" + end +end + + +steve = Person.new("Steve", "McQueen", 37) +puts steve.name + + +## Object + +=begin + +* kind_of? +* instance_of? +* instance_variable_defined? +* method +* methods +* nil? +* object_id +* private_methods +* protected_methods +* public_method +* public_methods +* singleton_class +* single_method +* singleton_methds +* define_singleton_method +* extend +* send +* public_send + + +* instance_variables +* remove_instance_variable + + + + + + +=end + + +=begin +* clone +* define_singleton_method +* display +* dup +* freeze +* itself + + +=end + + + + +## Struct + +Customer = Struct.new("Customer", :name, :age, :address) +charlie = Customer.new('Charlie', 13, 'Seoul') +charlie.address = 'Busan' +p charlie.address + +=begin +* to_h +* to_a +* to_s + +* values + +* each +* each_pair + + +=end + + + +## Basic Object + +class YetAnotherClass < BasicObject + +end + +## Singleton +require 'singleton' + +class MyClass + include Singleton + def initialize + @something = 'unknown' + end + def something + @something + end + def something=(s) + @something = s + end +end + + +a = MyClass.instance +b = MyClass.instance +a.something = 'Awesome' +p b + +END{ + # https://ruby-doc.org/3.3.0/Object.html + # https://ruby-doc.org/3.3.0/Struct.html + # https://ruby-doc.org/3.3.0/stdlibs/ostruct/OpenStruct.html + # https://ruby-doc.org/3.3.0/BasicObject.html + + # https://ruby-doc.org/3.3.0/stdlibs/singleton/Singleton.html +} diff --git a/src/16_set.rb b/src/16_set.rb new file mode 100755 index 0000000..cf00ad7 --- /dev/null +++ b/src/16_set.rb @@ -0,0 +1,61 @@ +#!/usr/bin/ruby + +# Set +require 'set' + +set1 = Set[1,2] +set1 = Set.new([1,2]) + +p set1 + +set2 = [1,2,3].to_set # 배열을 셋트로 변환 + +=begin + +* length +* empty? +* include? +* subset? +* superset? +* disjoint? +* intersect? + +=end + + +=begin + +* add / add? +* merge +* replace + +* clear +* delete / delete? +* subtract +* delete_if +* keep_if +* select +* reject + +* | / union +* & / intersection +* - / difference +* ^ +=end + +=begin + +* collect / map +* divide +* flatten +* join + +* each + +=end + + + +END{ + # https://ruby-doc.org/3.3.0/stdlibs/set/Set.html +} diff --git a/src/17_time.rb b/src/17_time.rb new file mode 100755 index 0000000..a1fd679 --- /dev/null +++ b/src/17_time.rb @@ -0,0 +1,151 @@ +#!/usr/bin/ruby + +# 시간 + +## Time +### 시간 인스턴스 생성 + +now = Time.now +now = Time.new + +date = Time.new(2021,11,22) +date = Time.new(2021,11,22,13,1,0, '+09:00') # 년, 월, 일, 시간대 + +date = Time.local(2021,11,22,13,1,0) +date = Time.utc(2021,11,22,13,1,0) + +date = Time.at(0) # Epoch second +date = Time.at(now, in: '-04:00') +p date + + +### 인스턴스 메서드 + +another = now + 3600 # 초단위 숫자와 함께 '+'나 '-' 연산도 가능합니다. + +=begin +* year +* month +* day / mday +* hour +* min +* sec +* usec : 마이크로 초 +* nsec : 나노 초 +* wday : 주에서 몇 번째 날인지. 일요일 == 0 +* yday : 연에서 몇 번째 날인지. 1월 1일 == 1 +* utc_offset : UTC 시간과 몇 초 차이가 나는지. +* to_f : Epoch 초 +* to_i : Epoch 초 +* to_r : Epoch 초 +* zone : 타임존 문자열 + +=end + +=begin +* utc? +* dst? +* sunday? +* monday? +* tuesday? +* wednesday? +* thursday? +* friday? +* saturday? + +=end + +=begin +* acstime : 문자열로 반환 +* strftime : 문자열로 반환 +* to_a : 배열 형식으로 반환 +* to_s : 문자열로 반환 +* getutc : 새로운 UTC 시간을 반환 +* getlocal : 새로운 로컬 시간을 반환 +* utc : UTC 시간으로 변환 +* localtime : 로컬 시간으로 변환 +* deconstruct_keys : 해시 형식으로 반환 + +=end + + +## Date + +### 시간 인스턴스 생성 + +date = Date.new(2021,11,22) +date = Date.ordinal(2024,100) # 2024년의 100번째 날 + +date = Date.jd(2451083) # 줄리안 데이 +date = Date.commercial(2024,24,5) # 2024년의 24번째 주, 5번째 날 + +date = Date.parse('2024-03-31') +date = Date.strptime('2024-03-31', '%Y-%m-%d') + +### 클래스 메서드 + +=begin +* _httpdate +* _iso8061 +* _rfc2822 +* _rfc3339 +* _xmlschema + +* httpdate +* iso8061 + +* commercial +* jd +* gregorian_leap? +* julian_leap? + + + +=end + +### 인스턴스 메서드 + +=begin +* httpdate +* iso8061 +* rfc2822 +* rfc3339 +* xmlschema + +* year +* month +* day + +* monday? ... + + +* next +* next_day +* next_month +* next_year +* prev_day +* prev_month +* prev_year + + +* to_datetime +* to_s +* to_time + +=end + + + +=begin + +=end + +=begin + +=end + +END{ + # https://ruby-doc.org/3.3.0/Time.html + # https://ruby-doc.org/3.3.0/exts/date/Date.html + # https://ruby-doc.org/3.3.0/exts/date/DateTime.html +} diff --git a/src/18_regexp.rb b/src/18_regexp.rb new file mode 100755 index 0000000..e391021 --- /dev/null +++ b/src/18_regexp.rb @@ -0,0 +1,62 @@ +#!/usr/bin/ruby + +# 정규표현식 + + +re = /red/ # 정규표현식은 '/'로 묶어서 표현합니다. +p re.match?('alfred') # 일치 여부만 확인할 경우 +p re.match('alfred') # 일치 문자열 정보가 필요한 경우 + +'alfred'.match?(re) # 문자열의 메서드를 사용해도 됩니다. + +i = (re =~ 'alfred') # '=~' 연산자를 사용하면 문자열에서 일치하는 위치의 인덱스를 반환합니다. +p i + + +## 글로벌 변수 + +=begin +* $~ : MatchData 객체 +* $& : 일치하는 부분 문자열 +* $` : 일치 하는 문자열의 앞 부분 +* $' : 일치하는 문자열의 뒷 부분 +* $+ : 그룹 매치 +* $1, $2, .. : 일치하는 그룹 + +=end + + +## 클래스 메서드 + +=begin +* last_match +* timeout +* union + +=end + + + +## 인스턴스 메서드 + +=begin +* case_fold? +* fixed_encoding? +* match +* match? +* name_captures +* names +* options +* source +* timeout +* to_s + + + +=end + + + +END{ + # https://ruby-doc.org/3.3.0/Regexp.html +} diff --git a/src/19_random.rb b/src/19_random.rb new file mode 100755 index 0000000..40544b9 --- /dev/null +++ b/src/19_random.rb @@ -0,0 +1,45 @@ +#!/usr/bin/ruby + +# 랜덤 + +## 클래스 메서드를 사용 + +r = Random.bytes(8) # 8바이트 길이의 랜덤 바이트 +p r.size +r = Random.urandom(8) # 8바이트 길이의 랜덤 바이트 +p r.size + +r = Random.rand # 부동소수 +p r +r = Random.rand(5) # 0부터 최대 값이 5 사이인 임의의 수 +p r +r = Random.rand(1..6) # 범위 지정 +p r + +## 인스턴스 메서드를 사용할 수도 있습니다. + +prng = Random.new +r = prng.bytes(8) +r = prng.rand(1..6) + + +## 시큐어 랜덤 + +require 'securerandom' + +r = SecureRandom.rand # Random과 마찬가지 방식으로 사용할 수 있습니다. +r = Random.rand(1..6) + + +r = SecureRandom.alphanumeric(10) # 문자와 숫자로 구성된 랜덤 문자열 +r = SecureRandom.hex(8) # 16진 문자열 형식으로 +r = SecureRandom.base64(16) # babse64 형식의 랜덤 문자열 +r = SecureRandom.urlsafe_base64(16) +r = SecureRandom.uuid # UUID + +p r + +END{ + # https://ruby-doc.org/3.3.0/Random.html + # https://ruby-doc.org/3.3.0/stdlibs/securerandom/SecureRandom.html +} diff --git a/src/21_file.rb b/src/21_file.rb new file mode 100755 index 0000000..cb861e3 --- /dev/null +++ b/src/21_file.rb @@ -0,0 +1,263 @@ +#!/usr/bin/ruby + +# 파일과 디렉토리 + +## File + +file = File.new('sample.txt', File::RDWR | File::CREAT, 0644) # 읽기및쓰기 모드로 파일이없으면 생성하고, 퍼미션은 644. +file.write("Hello, there!") +file.close # 열었으면 닫아야죠 + +File.open('sample.txt', 'r'){|file| + p file.readline +} + +File.write('sample2.txt', "Hahaha") # 이렇게도 쓰는 것도 가능합니다. + + +### 클래스 메서드 +=begin + +* link : 하드 링크를 생성합니다. +* symlink +* mkfifo + +* absolute_path? +* absolute_path +* base_name +* dir_name +* expand_path +* ext_name +* fn_match? +* join +* path +* readlink +* realdirpath +* realpath +* split + + +* atime +* birthtime +* ctime +* mtime + + +* blockdev? +* chardev? +* directory? +* executable? +* executable_real? +* exist? +* file? +* ftype +* grpowned? +* identical? +* lstat +* owned? +* pipe? +* readable? +* readable_real? +*setgid? +* setuid? +* socket? +* stat +* sticky? +* symlink? +* umask +* world_readable? +* worls_writable? +* writable? +* writable_real? + +* empty? +* size + +* chmod +* chown +* lchmod +* lchown +* utime +* lutime +* rename +* flock + +* truncate +* unlink + +=end + +### 인스턴스 메서드 +=begin + +* path + +* atime +* birthtime +* ctime +* mtime + +* lstat + +* size + +* truncate + +=end + + + +## Dir + +dir = Dir.new('.') +p dir.read +p dir.read +dir.close + +### 클래스 메서드 + +=begin +* chdir +* chroot +* children +* empty? +* entries +* exist? +* getwd : 'pwd' +* glob : 패턴에 일치하는 파일 패스를 배열로 반환 +* home : 사용자 홈 디렉토리 + +* each_child : '.'과 '..'는 포함되지 않는다. +* for_each : '.'과 '..'도 포함 + +* mkdir +* open +* unlink / delete / rmdir + +=end + +### 인스턴스 메서드 +=begin +* read +* close +* rewind +* seek +* pos + +* children +* fileno : 정수 형태의 파일 디스크립터를 반환 +* path +* tell + +* each +* each_child + +=end + + + + +## 임시 디렉토리 + +require 'tmpdir' + +Dir.tmpdir # +Dir.mktmpdir{|dir| + puts "Location is #{Dir.tmpdir}/d" +} + +## 임시 파일 + +require 'tempfile' + +file = Tempfile.new('foo') +begin + # ... +ensure + file.close + file.unlink +end + +Tempfile.create('foo') {|file| + # ... +} + + + +## File Utils + +FileUtils.pwd + +=begin +* mkdir +* mkdirs +* link_entry +* link / ln +* symlink / ln_s + +* remove_dir +* remove_entry +* remove_file +* remove / rm +* safe_unlink / rm_f +* rm_r +* rm_rf / rmtree +* rmdir + +* pwd +* uptodate? + +* chdir / ch +* chown +* chmod +* touch + + +* compare_file +* compare_stream + +* copy_entry +* copy_file +* copy_stream +* copy / cp +* cp_r +* cp_rl +* install +* move / mv + + +=end + + +## Find + +require 'find' # 주어진 디렉토리 및 자식 디렉토리를 탐색한다. + +Find.find("#{ENV['HOME']}/Workspace") {|path| + if (File.directory?(path)) + if File.basename(path).start_with?('.') + Find.prune # 더 깊이 들어가지 않는다. + else + next + end + else + puts path + end +} + + + +END{ + + # https://ruby-doc.org/3.3.0/File.html + # https://ruby-doc.org/3.3.0/FileTest.html + # https://ruby-doc.org/3.3.0/Dir.html + + # https://ruby-doc.org/3.3.0/stdlibs/tmpdir/Dir.html + # https://ruby-doc.org/3.3.0/stdlibs/tempfile/Tempfile.html + + # https://ruby-doc.org/3.3.0/stdlibs/fileutils/FileUtils.html + # https://ruby-doc.org/3.3.0/stdlibs/find/Find.html + + +} diff --git a/src/22_io.rb b/src/22_io.rb new file mode 100755 index 0000000..74da9a4 --- /dev/null +++ b/src/22_io.rb @@ -0,0 +1,147 @@ +#!/usr/bin/ruby + +# IO + +## 클래스 메서드 +=begin + +* open +* pipe +* popen + +* binread +* read +* readlines + +* binwrite +* write + +* foreach + +=end + +## 인스턴스 메서드 + +=begin +* getbyte +* getc +* gets +* pread +* read +* read_nonblock +* readbyte +* readchar +* readline +* readlines +* readpartial + + +=end + +=begin +* << +* print +* printf +* putc +* puts +* pwrite +* write +* write_nonblock + +=end + +=begin +* lineno +* pos +* reopen +* rewind +* seek + +=end + +=begin +* each / each_line +* each_byte +* each_char +* each_codepoint + +=end + +=begin +* fdatasync +* flush +* fsync + +=end + + +###### ====================================== + + +=begin + +* read +* write + + +* tell +* pos +* seek +* rewind + +* close +* cose_read +* close_write +* closed? + +* eof? + +=end + + +=begin + +* each_line +* gets : 다음 한 줄 읽기 +* readline : 다음 한 줄 읽기 +* readlines : 모두 읽어서 배열로 반환 + +* lineno + +=end + + +=begin + +* getc +* readchar +* ungetc +* putc +* each_char + + +=end + + +=begin + +* getbyte +* readbyte +* ungetbyte +* each_byte + + +=end + + +=begin + +* each_codepoint + + +=end + +END{ + # https://ruby-doc.org/3.3.0/IO.html + # https://ruby-doc.org/3.3.0/exts/io/nonblock/IO.html +} diff --git a/src/23_http.rb b/src/23_http.rb new file mode 100755 index 0000000..73bdf97 --- /dev/null +++ b/src/23_http.rb @@ -0,0 +1,38 @@ +#!/usr/bin/ruby + +# Http + +## Open URI + +require 'open-uri' + +URI.open("https://www.naver.com") {|resp| + resp.each_line {|line| + p line + } +} + +URI.open("https://www.naver.com", "User-Agent"=>"Ruby", "Authentication"=>"Bearer abcdef") {|resp| + resp.each_line {|line| + p line + } +} + +uri = URI.parse("https://www.naver.com") # +uri.open {|resp| + # ... +} + + + + +END{ + + + # https://ruby-doc.org/3.3.0/stdlibs/net/Net/HTTP.html + # https://ruby-doc.org/3.3.0/stdlibs/net/http/Net/HTTP.html + # https://ruby-doc.org/3.3.0/stdlibs/uri/URI.html + + # https://ruby-doc.org/3.3.0/stdlibs/open-uri/OpenURI.html + +} diff --git a/src/24_kernel.rb b/src/24_kernel.rb new file mode 100755 index 0000000..887f0b8 --- /dev/null +++ b/src/24_kernel.rb @@ -0,0 +1,51 @@ +#!/usr/bin/ruby + +# 커널 + +=begin + +* caller +* class +* frozen? +* global_variables +* local_variables +* test + +=end + +## IO + +=begin + +* gets +* open +* p +* print +* printf +* putc +* puts +* readline +* readlines +* select + +=end + + + + +=begin + +* eval + +=end + +a = eval "1 + 2" # 문자열을 스크립트 명령문으로 처리합니다. +p a # 3 + + + + +END{ + # https://ruby-doc.org/3.3.0/Kernel.html + # https://ruby-doc.org/3.3.0/ENV.html +} diff --git a/src/25_process_thread.rb b/src/25_process_thread.rb new file mode 100755 index 0000000..a04227d --- /dev/null +++ b/src/25_process_thread.rb @@ -0,0 +1,108 @@ +#!/usr/bin/ruby + +# 프로세스와 스레드 + + +## 프로세스 + +=begin + +* gid : 그룹 아이디 +* pid : 프로세스 아이디 +* ppid : 부모 프로세스 아이디 +* uid : 사용자 아이디 + +* abort : 현재 프로세스를 즉시 종료합니다. +* daemon : 프로세스를 백그라운드 프로세스로 전환시킵니다. +* at_exit +* exit : 프로세스를 종료시키고, SystemExit 예외를 던집니다. +* exit! : 프로세스를 즉시 종료시킵니다. + +=end + +exec('ls -al') # 현재 프로세스가 새로운 프로세스로 대체됩니다. + + + + +## 서브 프로세스 + +=begin + +* detach : 자식 프로세스가 좀비 프로세스가 되지 않도록 합니다. +* fork : 자식 프로세스를 만듭니다. +* kill : 프로세스에 신호를 보냅니다. +* spawn : 자식 프로세스를 만듭니다. +* wait, waitpid : 자식 프로세스가 종료될 때까지 기다립니다. +* wait2, waitpid2 +* waitall : 모든 자식 프로세스가 종료될 때까지 기다립니다. + +=end + +o = `ls -al` # 쉘 명령을 실행합니다. 표준 출력의 결과가 반환됩니다. +puts o + + +fork do # 현재 프로세스를 두 개로 나눕니다. 부모 프로세스는 wait()이나 detach()를 사용해야할 수도 있습니다. + # do something +end + +pid = spawn('pwd') # 자식 프로세스를 만들고, 즉시 프로세스 아이디를 반환합니다. + +system('pwd') # 자식 프로세스를 만들고, 실행 결과를 true/false/nil 중 하나로 반환합니다. + + + + +### Shellwords + +require 'shellwords' + +argv = Shellwords.split('one, two, three') # 문자열을 쉘 명령에 쓰기 좋은 형태의 배열로 쪼갭니다. +argv = 'one, two, three'.shellsplit + +filename = Shellwords.escape("it's a file.txt") # 특수문자를 처리합니다. +filename = "it's a file.txt".shellescape + +dir = "My Documents" +argv = %W[ls -lta -- #{dir}] +system(argv.shelljoin + " | less") # 배열을 문자열로 합칩니다. + + + + +## 스레드 + +thread = Thread.new { + # do something + "Job done." +} +thread.join # 스레드가 종료될 떄까지 메인 스레드를 멈춥니다.A +thread.value # 스레드의 마지막 값을 확인할 수 있습니다. + +Thread.kill(thread) # 스레드를 종료시킵니다. +thread.exit + +thread.status # 스레드의 동작 상태를 확인할 수 있습니다. +thread.alive? +thread.stop? + +Thread.current # 현재 스레드를 반환합니다. +Thread.main +Thread.list + + +sleep(1.2) # 1.2초 동안 스레드를 대기시킵니다. + + + + + + +END{ + # https://ruby-doc.org/3.3.0/Kernel.html + # https://ruby-doc.org/3.3.0/stdlibs/shellwords/Shellwords.html + + # https://ruby-doc.org/3.3.0/Process.html + # https://ruby-doc.org/3.3.0/Thread.html +} diff --git a/src/31_cgi.rb b/src/31_cgi.rb new file mode 100755 index 0000000..88ea877 --- /dev/null +++ b/src/31_cgi.rb @@ -0,0 +1,8 @@ +#!/usr/bin/ruby + + + +END{ + # https://ruby-doc.org/3.3.0/exts/cgi/CGI.html + # https://ruby-doc.org/3.3.0/stdlibs/cgi/CGI.html +} diff --git a/src/32_security.rb b/src/32_security.rb new file mode 100755 index 0000000..201759f --- /dev/null +++ b/src/32_security.rb @@ -0,0 +1,9 @@ +#!/usr/bin/ruby + + + +END{ + # https://ruby-doc.org/3.3.0/exts/digest/Digest.html + # https://ruby-doc.org/3.3.0/exts/openssl/OpenSSL.html + # https://ruby-doc.org/3.3.0/stdlibs/securerandom/SecureRandom.html +} diff --git a/src/33_logger.rb b/src/33_logger.rb new file mode 100755 index 0000000..fd0c3d7 --- /dev/null +++ b/src/33_logger.rb @@ -0,0 +1,45 @@ +#!/usr/bin/ruby + +# 로깅 + +require 'logger' + + +## 로거 인스턴스 만들기 + +logger = Logger.new('my.log') +logger = Logger.new('my.log', 3, 10485760) # 각 10메가바이트 크기로 3개의 파일만 +logger = Logger.new('my.log', 'daily') # daily | weekly | monthly + +logger = Logger.new($stdout) + +## 로깅 + +logger.add(Logger::DEBUG, 'Maximal debugging info') # add 메서드로 로깅을 합니다. +logger.add(Logger::INFO, 'Non-error information') +logger.add(Logger::WARN, 'Non-error warning') +logger.add(Logger::ERROR, 'Non-fatal error') +logger.add(Logger::FATAL, 'Fatal error') +logger.add(Logger::UNKNOWN, 'Most severe') + + +logger.debug('Maximal debugging info') # 좀 더 짧게 사용할 수 있습니다. +logger.info('Non-error information') +logger.warn('Non-error warning') +logger.error('Non-fatal error') +logger.fatal('Fatal error') +logger.unknown('Most severe') + + +## 닫기 + +logger.close # 사용이 끝나면 닫아줍니다. + + + + + + +END{ + # https://ruby-doc.org/3.3.0/stdlibs/logger/Logger.html +} diff --git a/src/34_socket.rb b/src/34_socket.rb new file mode 100755 index 0000000..ae8fa55 --- /dev/null +++ b/src/34_socket.rb @@ -0,0 +1,13 @@ +#!/usr/bin/ruby + + + +END{ + # https://ruby-doc.org/3.3.0/exts/socket/Socket.html + # https://ruby-doc.org/3.3.0/exts/socket/TCPServer.html + # https://ruby-doc.org/3.3.0/exts/socket/TCPSocket.html + # https://ruby-doc.org/3.3.0/exts/socket/UDPSocket.html + + # https://ruby-doc.org/3.3.0/stdlibs/ipaddr/IPAddr.html + # https://ruby-doc.org/3.3.0/stdlibs/ipaddr/IPSocket.html +} diff --git a/src/35_compress.rb b/src/35_compress.rb new file mode 100755 index 0000000..3ecc6a3 --- /dev/null +++ b/src/35_compress.rb @@ -0,0 +1,7 @@ +#!/usr/bin/ruby + + + +END{ + # https://ruby-doc.org/3.3.0/exts/zlib/Zlib.html +} diff --git a/src/36_base64.rb b/src/36_base64.rb new file mode 100755 index 0000000..18e3f4c --- /dev/null +++ b/src/36_base64.rb @@ -0,0 +1,22 @@ +#!/usr/bin/ruby + +require 'base64' + + +encoded = Base64.encode64('Hello, World!') # "SGVsbG8sIFdvcmxkIQ==\n" 각 60문자마다, 그리고 맨 뒤에 줄바꿈 문자가 추가됩니다. +decodeed = Base64.decode64(encoded) + +encoded = Base64.urlsafe_encode64('Hello, World!') # "SGVsbG8sIFdvcmxkIQ==" +decodeed = Base64.urlsafe_decode64(encoded) + +encoded = Base64.strict_encode64('Hello, World!') # "SGVsbG8sIFdvcmxkIQ==" 줄바꿈이 추가되지 않습니다. +decodeed = Base64.strict_decode64(encoded) + +p encoded +p decodeed + + + +END{ + # https://ruby-doc.org/3.3.0/stdlibs/base64/Base64.html +} diff --git a/src/37_json.rb b/src/37_json.rb new file mode 100755 index 0000000..09edd12 --- /dev/null +++ b/src/37_json.rb @@ -0,0 +1,29 @@ +#!/usr/bin/ruby + +# JSON + +require 'json' + +## 읽기 + +json = '{"name":"Charlie", "age":14, "human":true}' +data = JSON.parse(json); +p data # {"name"=>"Charlie", "age"=>14, "human"=>true} + +data = JSON.parse(File.read('sample.json')) # 파일로부터 읽어 오기 +p data + + +## 내보내기 + +hash = {name:"Charlie", age: 13, alien:false} +json_str = JSON.generate(hash) +p json_str +array = [0, 's', true, hash] +json_str = JSON.generate(array) +p json_str + + +END{ + # https://ruby-doc.org/3.3.0/exts/json/JSON.html +} diff --git a/src/38_csv.rb b/src/38_csv.rb new file mode 100755 index 0000000..7513345 --- /dev/null +++ b/src/38_csv.rb @@ -0,0 +1,98 @@ +#!/usr/bin/ruby + +# Comma Separated Value + +require 'csv' + +## 읽기 + +csv = <"bar", "bar"=>["foo", "bar"]} + +data = YAML.load_file('sample.yaml') +p data + + +## 내보내기 + +hash = {foo: 'bar', bar:['foo', 'bar']} +str = YAML.dump(hash) # 해시 등의 객체를 dump()하면 yaml 문자열이 나옵니다. +p str # "---\n:foo: bar\n:bar:\n- foo\n- bar\n" + +str = hash.to_yaml # 또는, 해시나 배열 등에서 to_yaml 메서드를 호출 +p str # "---\n:foo: bar\n:bar:\n- foo\n- bar\n" + + +END{ + # https://ruby-doc.org/3.3.0/stdlibs/yaml/YAML.html +} diff --git a/src/41_dbi.rb b/src/41_dbi.rb new file mode 100755 index 0000000..ef2118e --- /dev/null +++ b/src/41_dbi.rb @@ -0,0 +1,12 @@ +#!/usr/bin/ruby + + +=begin + +=end + +END{ + # https://www.tutorialspoint.com/ruby/ruby_database_access.htm + + # https://www.devdungeon.com/content/ruby-activerecord-without-rails-tutorial +} diff --git a/src/data_type.rb b/src/data_type.rb new file mode 100755 index 0000000..29c50f3 --- /dev/null +++ b/src/data_type.rb @@ -0,0 +1,49 @@ +#!/usr/bin/ruby + +=begin + This is a comment. +=end + +# Number +i = 34 +f = 3.14 + +# Stirng +s = "Hello" +doc = < 1, + "B" => 2, + "C" => 3 +} +h.each do |key,value| + puts "#{key} => #{value}" +end + +# Range +(2..5).each do |i| #inclusive + puts "#{i}" +end +(2...5).each do |i| #exclusive + puts "#{i}" +end + +BEGIN{ + puts "Begining..." +} + +END { + puts "Finished." +} diff --git a/src/my.log b/src/my.log new file mode 100644 index 0000000..08e571c --- /dev/null +++ b/src/my.log @@ -0,0 +1 @@ +# Logfile created on 2024-04-01 05:53:45 +0900 by logger.rb/v1.4.3 diff --git a/src/sample.csv b/src/sample.csv new file mode 100644 index 0000000..fcc92e0 --- /dev/null +++ b/src/sample.csv @@ -0,0 +1,3 @@ +Charlie, 13 +Steve, 43 +Jane, 27 \ No newline at end of file diff --git a/src/sample.json b/src/sample.json new file mode 100644 index 0000000..97b6495 --- /dev/null +++ b/src/sample.json @@ -0,0 +1 @@ +{"name":"Charlie", "age":14, "human":true} \ No newline at end of file diff --git a/src/sample.txt b/src/sample.txt new file mode 100644 index 0000000..4a2c281 --- /dev/null +++ b/src/sample.txt @@ -0,0 +1 @@ +Hello, there! \ No newline at end of file diff --git a/src/sample.yaml b/src/sample.yaml new file mode 100644 index 0000000..a35d0e9 --- /dev/null +++ b/src/sample.yaml @@ -0,0 +1,4 @@ +foo: bar +bar: + - foo + - bar \ No newline at end of file diff --git a/src/sample2.txt b/src/sample2.txt new file mode 100644 index 0000000..30ab347 --- /dev/null +++ b/src/sample2.txt @@ -0,0 +1 @@ +Hahaha \ No newline at end of file