Ruby: Birleştirme olmadan çok satırlı dize yazabilir miyim?


397

Bunu biraz daha iyi göstermenin bir yolu var mı?

conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' +
          'from table1, table2, table3, etc, etc, etc, etc, etc, ' +
          'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

Mesela, birleştirme anlamına gelmenin bir yolu var mı?


28
SQL enjeksiyon saldırılarına karşı dikkatli olun. :)
Roy Tinker

Yanıtlar:


595

Bu cevaba ihtiyacım olanı elde etmeme yardımcı olan parçalar var (ekstra boşluk OLMADAN kolay çok satırlı birleştirme), ancak gerçek cevapların hiçbirine sahip olmadığından bunları derliyorum:

str = 'this is a multi-line string'\
  ' using implicit concatenation'\
  ' to prevent spare \n\'s'

=> "this is a multi-line string using implicit concatenation to eliminate spare
\\n's"

Bonus olarak, komik HEREDOC sözdizimini kullanan bir sürüm ( bu bağlantı üzerinden ):

p <<END_SQL.gsub(/\s+/, " ").strip
SELECT * FROM     users
         ORDER BY users.id DESC
END_SQL
# >> "SELECT * FROM users ORDER BY users.id DESC"

İkincisi, çoğunlukla işlemede daha fazla esneklik gerektiren durumlar içindir. Ben şahsen hoşlanmıyorum, bu dize wrt garip bir yere koyar (yani, önünde, ama genellikle sonradan gelen örnek yöntemleri kullanarak), ama orada. Son END_SQLtanımlayıcıyı girintiliyorsanız (bu yaygın bir işlevdir, çünkü bu muhtemelen bir işlev veya modülün içindedir), tireli sözdizimini (yani, p <<-END_SQLyerine) kullanmanız gerekeceğini unutmayın .p <<END_SQL ) kullanmanız gerekir. Aksi takdirde, girinti boşluğu, tanımlayıcının dizenin devamı olarak yorumlanmasına neden olur.

Bu yazı yazmaktan çok tasarruf etmez, ancak + işaretlerini kullanmaktan daha güzel görünüyor.

Ayrıca (birkaç yıl sonra bir düzenlemede söylüyorum), Ruby 2.3+ kullanıyorsanız , son dizeden fazladan girintiyi kaldıran << ~ operatörü de kullanılabilir . Bu .gsubdurumda çağrıyı kaldırabilmelisiniz (ancak hem başlangıç ​​girintisine hem de son ihtiyaçlarınıza bağlı olabilir).

DÜZENLEME: Bir tane daha ekleme:

p %{
SELECT * FROM     users
         ORDER BY users.id DESC
}.gsub(/\s+/, " ").strip
# >> "SELECT * FROM users ORDER BY users.id DESC"

2
Bu eski bir soru AMA cevapta bir hata var ya da o zamandan beri sözdiziminde bir değişiklik oldu. p <<END_SQLolmalıdır p <<-END_SQLAksi takdirde bu cevap olduğunu. isteğe bağlı olarak, kıvrımlı HEREDOC operatörü ile önde gelen beyaz alanı çıkarabilirsiniz,<<~END_SQL
jaydel

Yalnızca bitiş tanımlayıcısı girintili olduğunda bir hatadır (kısa çizgi, yakut yorumlayıcıya bitiş tanımlayıcısını belirlemeden önce boşlukları kesmesini söyler). Yine de bundan bahseden bir not yazabilirim. Ayrıca, ~ gereksiz, gsub \ s + ve şerit zaten önde gelen boşluk kaldırıyor.
A. Wilson

Cevaba eklemek <<~güzel olurdu, oradan araştırdı. Şahsen, ben <<~MSG.strip ... MSGde son şerit olan kullanın \n.
Qortex

1
Bu cevabı yazdığımda (dokuz yıl önce, şeyh!), Ruby 1.9'daydı ve << ~ (açıkça) 2.3'e kadar tanıtılmadı. Her neyse, antik tarih bir kenara, onu koyacağım, getirdiğin için teşekkürler.
A. Wilson

Fazladan yeni satır eklemeyen birkaç cevaptan biri olduğunuz için teşekkür ederim , bu soruyu bulduğumda kaçınmaya çalışıyorum.
Josh

174

Ruby 2.0'da artık sadece %

Örneğin:

SQL = %{
SELECT user, name
FROM users
WHERE users.id = #{var}
LIMIT #{var2}
}

14
Ruby 1.9.3'te de çalışır.
Andy Stewart

26
Bu sözdizimiyle oluşturulan bir dize, hem yeni satırları hem de sonraki satırlara eklenen herhangi bir girintiyi içerir.
James

Bu bile << EOT ...... EOT (burada belge) daha iyidir! ayrıca gerekirse enterpolasyon yapar.
Nasser

1
@ Nasır Heredoc enterpolasyon da yapar.
Monica'nın Davası

3
Raylar kullanılıyorsa squish, çıkış çağrısı yararlı olacaktır.
Jignesh Gohel

167

Evet, eklenen yeni satır satırlarının sakıncası yoksa:

 conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc,
            where etc etc etc etc etc etc etc etc etc etc etc etc etc'

Alternatif olarak bir yorumlu metin kullanabilirsiniz :

conn.exec <<-eos
   select attr1, attr2, attr3, attr4, attr5, attr6, attr7
   from table1, table2, table3, etc, etc, etc, etc, etc,
   where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

87
Ayrıca kullanabilirsiniz%Q(...)
BaroqueBobcat

3
@Zombiler: Yeni satırlara genellikle SQL deyimlerinde izin verilir ve yalnızca normal boşluk olarak kabul edilir.
Mark Byers

2
bir örnek için cevabımı aşağıda görebilirsiniz, şimdi% 'yi kullanabilirsiniz.
Robbie Guilfoyle

4
Ayrıca kullanabilirsiniz%(...)
sıfır bölen

1
Kasıtlı olarak sondaki boşluk ekleyip bu çözümlerden birini kullanırsanız, aklınızda bulundurmanız gereken önemli bir şey , düzenleyicinizin dosyayı kaydederken arka boşluğu otomatik olarak kaldırabilmesidir . Normalde bu davranışı tercih ederken, benim için birkaç kez beklenmedik sorunlara neden oldu. Bir çözüm, OP'nin soruda yaptığı gibi çok satırlı dizenizi yazmaktır.
Dennis

50

Daha önce okuduğunuz gibi çok satırlı dizelerin birden fazla sözdizimi vardır. Benim favorim Perl tarzı:

conn.exec %q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
      from table1, table2, table3, etc, etc, etc, etc, etc,
      where etc etc etc etc etc etc etc etc etc etc etc etc etc}

Çok satırlı dize% q ile başlar, ardından {, [veya (ve sonra karşılık gelen tersine çevrilmiş karakterle sonlandırılır.% Q enterpolasyona izin vermez;% Q böyle şeyler yazabilir:

conn.exec %Q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
      from #{table_names},
      where etc etc etc etc etc etc etc etc etc etc etc etc etc}

Aslında bu tür çok satırlı dizelerin nasıl adlandırıldığı hakkında hiçbir fikrim yok, bu yüzden onlara Perl çok satırlı diyelim.

Bununla birlikte, Mark ve Peter'ın önerdiği gibi Perl çok satırlı veya heredoc kullanıyor olun, potansiyel olarak gereksiz beyaz alanlarla sonuçlanacağınızı unutmayın. Hem benim örneklerimde hem de örneklerimde, "from" ve "where" satırları, koddaki girintileri nedeniyle önde gelen boşluklar içeriyor. Bu boşluk istenmiyorsa, şu anda yaptığınız gibi birleştirilmiş dizeleri kullanmanız gerekir.


4
# {table_names} 'dan bu örnekte çalışmaz,% q {} kullandığınız için% q [] veya ()
MatthewFord

2
Bu damarda en sevdiğim sadece% {enterpolasyon desteği ile süper çok satırlı dize}
Duke

%qaileden üretilen dizeler orijinal koda eşit olmayan yeni satırları içerecektir.
Josh

29

Bazen aşağıdaki \ngibi yeni satır karakterlerini kaldırmaya değer :

conn.exec <<-eos.squish
 select attr1, attr2, attr3, attr4, attr5, attr6, attr7
 from table1, table2, table3, etc, etc, etc, etc, etc,
 where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

5
Bu raylar değil yakut değil
a14m

23

Çift tırnak işaretleri de kullanabilirsiniz

x = """
this is 
a multiline
string
"""

2.3.3 :012 > x
 => "\nthis is\na multiline\nstring\n"

Gerekirse satır sonlarını kaldırmak için "\ n" her satırın sonunda ters eğik çizgi "\" kullanın


5
Aynı sonucu tekil çift tırnaklarla elde edebilirsiniz. Ruby'de üçlü çift tırnak gibi bir şey yoktur. Onları sadece olarak yorumlar "" + "double quotes with some content" + "".
rakvium

Evet, ama "" + "\ n merhaba \ n" + "" Tuhaf görünüyor
juliangonzalez

1
Evet, garip görünüyor ve bu yüzden tekil çift tırnakları aynı sonuçla kullanabileceğinizde ekstra çift tırnak eklemek için bir neden yok.
rakvium

Evet, artı işaretini kastetmiştim. Onsuz çift tırnak sadece iyi görünüyor, okunabilir ve tek satır dizeleri üzerinde kullanılması gereken tek bir teklif yerine tespit etmek daha kolay.
juliangonzalez

1
Yani "x"daha iyi görünüyor ve daha hızlı çalışıyor """x"""(temelde aynı ""+"x"+"") veya """""x"""""(aynı "" + "" + "x" + "" + ""). Bu kullandığınız Yakut, değil Python, var """yerine "bir çok satırlı dize gerektiğinde.
rakvium

15
conn.exec = <<eos
  select attr1, attr2, attr3, attr4, attr5, attr6, attr7
  from table1, table2, table3, etc, etc, etc, etc, etc,
  where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

1
'-' olmadan heredoc kullanılması, '<< - eos' da olduğu gibi, ek lider alanlarını içerecektir. bkz. Mark Byers'ın yanıtı.
ives

yorumlu metin orijinal koda eşdeğer olmayan yeni satırları içerecektir.
Josh

15

Diğer seçenekler:

#multi line string
multiline_string = <<EOM
This is a very long string
that contains interpolation
like #{4 + 5} \n\n
EOM

puts multiline_string

#another option for multiline string
message = <<-EOF
asdfasdfsador #{2+2} this month.
asdfadsfasdfadsfad.
EOF

puts message

1
Değişmelidir <<EOMiçin <<-EOMhayır?
kingPuppy

Belki benim örneğim için işe yarıyor gibiydi <<-EOF. Tahminimce her iki şekilde de çalışır.
Alex Cohen

yorumlu metin orijinal koda eşdeğer olmayan yeni satırları içerecektir.
Josh

11

Son zamanlarda Ruby 2.3'teki yeni özelliklerle yeni squiggly HEREDOC, çok satırlı dizelerimizi minimal bir değişiklikle güzel bir şekilde yazmanıza izin verecek, bu yüzden bunu .squish(raylar kullanıyorsanız) ile birlikte kullanmak çok satırlı güzel bir şekilde yazmanıza izin verecektir! sadece yakut kullanmanız durumunda <<~SQL.split.join(" "), hemen hemen aynı olan

[1] pry(main)> <<~SQL.squish
[1] pry(main)*   select attr1, attr2, attr3, attr4, attr5, attr6, attr7
[1] pry(main)*   from table1, table2, table3, etc, etc, etc, etc, etc,
[1] pry(main)*   where etc etc etc etc etc etc etc etc etc etc etc etc etc
[1] pry(main)* SQL
=> "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 from table1, table2, table3, etc, etc, etc, etc, etc, where etc etc etc etc etc etc etc etc etc etc etc etc etc"

ref: https://infinum.co/the-capsized-eight/multiline-strings-ruby-2-3-0-the-squiggly-heredoc


squish raylar, yakut değil
Josh

1
@Josh, evet haklısın, cevabı güncelledin, şerefe.
Mark Jad

6
conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' <<
        'from table1, table2, table3, etc, etc, etc, etc, etc, ' <<
        'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

<< dizeler için birleştirme operatörüdür


2
+Düzenli birleştirme operatörüdür, <<olduğu yerinde ekleme operatörü. Bir kelimenin tam anlamıyla yan etkileri kullanmak burada çalışır (ilk dize iki kez değiştirilir ve geri döner), ancak IMHO garip ve beni +tamamen netleştirecek bir çift çekim yapmamı sağlıyor . Ama belki sadece Ruby'de yeniyim ...
Beni Cherniavsky-Paskin

frozen_string_literalEtkinleştirilirse bu çalışmaz
Raido

6

Eğer varsa yapmak aklın fazladan boşluk ve yeni satır, kullanabilirsiniz

conn.exec %w{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
  from table1, table2, table3, etc, etc, etc, etc, etc,
  where etc etc etc etc etc etc etc etc etc etc etc etc etc} * ' '

(enterpolasyonlu dizeler için% W kullanın)


Bunu çok seviyorum çünkü çok daha fazla kullanım kombinasyonuna izin veriyor.
schmijos

1
Bu, bitişik birden fazla boşluğu tek bir alana sıkıştıracaktır. (Newline + girintiyi takiben fışkırması burada bir galibiyettir ancak çizginin ortasında şaşırtıcı olabilir.)
Beni Cherniavsky-Paskin

5

Her satır için parantezlerin kapanmasını önlemek için, yeni satırdan çıkmak için ters eğik çizgiyle çift tırnak kullanabilirsiniz:

"select attr1, attr2, attr3, attr4, attr5, attr6, attr7 \
from table1, table2, table3, etc, etc, etc, etc, etc, \
where etc etc etc etc etc etc etc etc etc etc etc etc etc"

Bu, bu soruya gerçekten cevap veren birkaç cevaptan biridir!
Josh

4
conn.exec [
  "select attr1, attr2, attr3, ...",
  "from table1, table2, table3, ...",
  "where ..."
].join(' ')

Bu öneri, burada otomatik girintilerin dizenin her bir parçasını uygun bir şekilde girintileyebileceği belgeler ve uzun dizeler üzerinde avantaj sağlar. Ama bu bir verimlilik maliyetiyle geliyor.


@Aidan, Virgülleri ters eğik çizgilerle (a la C) değiştirebilirsiniz ve birleştirme (veya dizi) gerekmez: Tercüman, (sanırım) ayrıştırma zamanında dizeleri birleştirecek ve alternatiflerin çoğuna kıyasla oldukça hızlı hale getirecektir. . Bununla birlikte, bir dizi diziye katılmanın bir avantajı, bazı otomatik girintilerin örneğin burada doc dizeleri veya \ ile olduğundan daha iyi çalışmasıdır.
Wayne Conrad

1
Bir not, yorumlu metin sözdizimi << - uygun girinti sağlar.
A. Wilson

2

Ruby 2.3'ten beri Ruby-way (TM): Satırsonu ve doğru iddaa içeren çok satırlı bir dize tanımlamak için kıvrımlı HEREDOC'u kullanın <<~:

conn.exec <<~EOS
            select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc
            where etc etc etc etc etc etc etc etc etc etc etc etc etc
          EOS

# -> "select...\nfrom...\nwhere..."

Doğru idrar endişe verici değilse, tek ve çift tırnaklar Ruby'de birden fazla satıra yayılabilir:

conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 
           from table1, table2, table3, etc, etc, etc, etc, etc, 
           where etc etc etc etc etc etc etc etc etc etc etc etc etc"    

# -> "select...\n           from...\n           where..."

Tek veya çift tırnaklar hantalsa, bu çok fazla kaçış gerektirdiğinden, yüzde dize değişmez gösterim %en esnek çözümdür:

conn.exec %(select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc
            where (ProductLine = 'R' OR ProductLine = "S") AND Country = "...")
# -> "select...\n            from...\n            where..."

Amaç satır satırlarından kaçınmaksa (hem kıvrımlı HEREDOC, tırnak işaretleri ve yüzde değişmez değeri neden olur), son boşluk olmayan karakter olarak bir ters eğik çizgi devam eder ve Ruby'nin Dizeleri arka arkaya birleştirmesine neden olur (alıntılanan dizenin içindeki boşluklara dikkat edin):

conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' \
          'from table1, table2, table3, etc, etc, etc, etc, etc, ' \
          'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

# -> "select...from...where..."

Rails kullanırsanız String.squish, ön ve arka boşluk dizesini sıyırır ve ardışık tüm boşluk alanlarını (yeni satırlar, sekmeler ve tümü) tek bir alana daraltır:

conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 
           from table1, table2, table3, etc, etc, etc, etc, etc, 
           where etc etc etc etc etc etc etc etc etc etc etc etc etc".squish

# -> "select...from...where..."

Daha fazla detay:

Ruby HEREDOC Sözdizimi

Burada Dizeler için Belge Gösterimi çalışır kodda metin satır içi uzun bloklar atamak için bir yoldur. Ardından <<kullanıcı tanımlı bir Dize (Dize Sonu sonlandırıcısı) başlar. Aşağıdaki tüm satırlar, bir satırın en başında Dize Sonu sonlandırıcı bulunana kadar birleştirilir :

puts <<HEREDOC 
Text Text Text Text
Bla Bla
HEREDOC
# -> "Text Text Text Text\nBlaBla"

Dize Sonu sonlandırıcı serbestçe seçilebilir, ancak "EOS" (Dize Sonu) gibi veya Dize'nin "SQL" gibi alanıyla eşleşen bir şey kullanmak yaygındır.

HEREDOC, enterpolasyonu varsayılan olarak veya EOS sonlandırıcının çift tırnak işaretli olduğu durumlarda destekler :

price = 10
print <<"EOS"  # comments can be put here
1.) The price is #{price}.
EOS
# -> "1.) The price is 10."

EOS sonlandırıcı tek tırnaklıysa enterpolasyon devre dışı bırakılabilir:

print <<'EOS' # Disabled interpolation
3.) The price is #{price}.
EOS
# -> "3.) The price is #{price}."

Bunun önemli bir kısıtlaması <<HEREDOCDize Sonu sonlandırıcısının satırın başında olması gerektiğidir:

  puts <<EOS 
    def foo
      print "foo"
    end
  EOS
EOS
#-> "....def foo\n......print "foo"\n....end\n..EOS

Bu <<-sorunu çözmek için sözdizimi oluşturuldu. EOS sonlandırıcının kodun daha güzel görünmesi için girintili olmasına izin verir. <<-Ve EOS sonlandırıcı arasındaki çizgiler, tüm girintiler dahil olmak üzere hala tam olarak kullanılmaktadır:

puts <<-EOS # Use <<- to indent End of String terminator
  def foo
    print "foo"
  end
EOS
# -> "..def foo\n....print "foo"\n..end"

Ruby 2.3'ten bu yana, kıvrımlı HEREDOC <<~önde gelen boşluğu kaldırıyor:

puts <<~EOS # Use the squiggly HEREDOC <<~ to remove leading whitespace (since Ruby 2.3!)
  def foo
    print "foo"
  end
EOS
# -> "def foo\n..print "foo"\nend"

Yalnızca sekme ve boşluk içeren boş satırlar ve satırlar << ~ tarafından yoksayılır

puts <<~EOS.inspect 
  Hello

    World!
EOS
#-> "Hello\n..World!"

Hem sekmeler hem de boşluklar kullanılıyorsa, sekmeler 8 boşluğa eşit kabul edilir. En az girintili çizgi bir sekmenin ortasındaysa, bu sekme kaldırılmaz.

puts <<~EOS.inspect
<tab>One Tab
<space><space>Two Spaces
EOS
# -> "\tOne Tab\nTwoSpaces"

HEREDOC, backticks kullanarak komutları yürütmek gibi çılgın şeyler yapabilir:

puts <<`EOC`            
echo #{price}
echo #{price * 2}
EOC

HEREDOC Dize tanımları "yığınlanabilir", yani ilk EOS sonlandırıcının (aşağıda EOSFOO) ilk dizeyi bitirip ikinciyi başlatacağı (aşağıdaki EOSBAR):

print <<EOSFOO, <<EOSBAR    # you can stack them
I said foo.
EOSFOO
I said bar.
EOSBAR

Ben hiç kimse böyle kullanmak olacağını sanmıyorum, ama <<EOSgerçekten sadece bir dize değişmez ve bir dize normalde konabilir her yere konabilir:

def func(a,b,c)
  puts a
  puts b
  puts c
end

func(<<THIS, 23, <<THAT) 
Here's a line
or two.
THIS
and here's another.
THAT

Ruby 2.3, ancak Rails >=3.0'a sahip değilseniz String.strip_heredoc, aynı olanı kullanabilirsiniz .<<~

# File activesupport/lib/active_support/core_ext/string/strip.rb, line 22
class String
  def strip_heredoc
    gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
  end
end

puts <<-USAGE.strip_heredoc # If no Ruby 2.3, but Rails >= 3.0
  This command does such and such.

  Supported options are:
    -h         This message
    ...
USAGE

Yüzde Dizgi Değişmezleri

Bkz RubyDoc böyle bir şekilde bir parantez çifti bir dize izledi yüzde işaretini nasıl kullanılacağı için %(...), %[...],%{...} vs ya da bunların herhangi bir alfanümerik olmayan karakterin bir çift gibi%+...+

Son sözler

Son olarak, asıl sorunun cevabını almak için "Birleştirme anlamına gelmenin bir yolu var mı?" Cevap: İki dizginin (tek ve çift tırnaklı) arka arkaya bulunması durumunda Ruby her zaman birleştirme anlamına gelir:

puts "select..." 'from table...' "where..."
# -> "select...from table...where..."

Uyarı, bunun satır kesmeleri arasında çalışmadığıdır, çünkü Ruby bir ifade sonu yorumlamaktadır ve sadece bir satırdaki sadece dizelerin sonuç satırı hiçbir şey yapmaz.


1

Bugün Zarif Cevap:

<<~TEXT
Hi #{user.name}, 

Thanks for raising the flag, we're always happy to help you.
Your issue will be resolved within 2 hours.
Please be patient!

Thanks again,
Team #{user.organization.name}
TEXT

Bir fark var <<-TEXTve <<~TEXTeski blok içindeki boşluğu koruyor ve ikincisi yok.

Başka seçenekler de var. Birleştirme gibi ama bu genel olarak daha mantıklı.

Burada yanılıyorsam, nasıl yapılacağını bana bildirin ...


yorumlu metin orijinal koda eşdeğer olmayan yeni satırları içerecektir.
Josh

1

Senin gibi ben de satırsonu içermeyen bir çözüm arıyordum . (SQL'de güvenli olsalar da, benim durumumda güvenli değiller ve başa çıkmak için büyük bir metin bloğum var)

Bu tartışmalı bir şekilde çirkin, ancak sonuçlanan dizeden atlamak için bir yorumlu metindeki satır sonlarından kaçabilir:

conn.exec <<~END_OF_INPUT
    select attr1, attr2, attr3, attr4, attr5, attr6, attr7 \
    from table1, table2, table3, etc, etc, etc, etc, etc, \
    where etc etc etc etc etc etc etc etc etc etc etc etc etc
  END_OF_INPUT

İnterpolasyon (IE <<~'END_OF_INPUT') olmadan bunu yapamayacağınızı unutmayın, bu yüzden dikkatli olun. #{expressions}burada değerlendirilecek, oysa orijinal kodunuzda olmayacak. A. Wilson'ın cevabı bu nedenle daha iyi olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.