Printf ile başa çıkmak yerine neden takip eden yeni satırlar kullanıyorsunuz?


79

Kullanırken yeni hatlardan kaçınmanız gerektiğini duydum printf. Bu yüzden printf("\nHello World!")senin yerine kullanmalısın.printf("Hello World!\n")

Yukarıdaki bu özel örnekte, çıktı farklı olacağı için bir anlam ifade etmiyor, ancak şunu göz önünde bulundurun:

printf("Initializing");
init();
printf("\nProcessing");
process_data();
printf("\nExiting");

nazaran:

printf("Initializing\n");
init();
printf("Processing\n");
process_data();
printf("Exiting");

Daha iyi görünmesi dışında takip eden yeni hatlarla hiçbir fayda göremiyorum. Başka bir sebep var mı?

DÜZENLE:

Buradaki ve şimdiki yakın oyları ele alacağım. Bunun Stack overflow'a ait olduğunu sanmıyorum, çünkü bu soru çoğunlukla tasarımla ilgili. Bu konuda görüş olsa da, Kilian Foth'un cevabı ve yöneticisinin cevabının tek bir yaklaşımla gerçekten çok nesnel faydalar olduğunu kanıtladığını da söyleyebilirim .


5
Bu soru, "kodlu konular" (konu dışı) ve "kavramsal yazılım tasarımı" (konu dışı) arasındaki sınırdadır. Kapanabilir, ama fazla zorlama. Yine de somut kod örnekleri eklemek doğru seçimdi.
Kilian Foth

46
Son satır, linux'taki komut istemi ile takip eden bir yeni satır olmadan birleşirdi.
GrandmasterB

4
Eğer "daha iyi görünüyorsa" ve olumsuz bir tarafı yoksa, bunu yapmak için yeterince iyi bir neden, IMO. İyi bir kod yazmak iyi bir roman ya da iyi bir teknik makale yazmaktan farklı değildir - şeytan her zaman ayrıntıdadır.
alephzero

5
Kendini bir şey yap init()ve process_data()yazdır Sonuçta olsaydı nasıl görünmesini beklersiniz?
Bergi

9
\nbir satır sonlandırıcı , bir satır ayırıcı değil . Bu, UNIX'teki metin dosyalarının neredeyse her zaman sona erdiği gerçeğiyle kanıtlanmaktadır \n.
Jonathon Reinhart

Yanıtlar:


222

Adil miktarda terminal G / Ç satır arabelleğe alınır , bu nedenle \ n ile bir mesaj sonlandırıldığında, zamanında görüntüleneceğinden emin olabilirsiniz. Bir öncü \ n ile mesaj bir kerede gösterilebilir veya gösterilmeyebilir. Çoğu zaman, bu, her bir adımın , bir programın davranışını anlamaya çalıştığınızda karışıklığın sona ermesine ve zaman kaybına neden olmayan önceki adımın ilerleme mesajını göstermesi anlamına gelir .


20
Bu, çökmekte olan bir programda hata ayıklamak için printf kullanırken özellikle önemlidir. Yeni çizgiyi bir printf'in sonuna koymak, konsola yapılan stdout'un her printf'te yıkanması anlamına gelir. (Stdout bir dosyaya yönlendirildiğinde, std kütüphanelerinin genellikle satır tamponlama yerine blok tamponlama yapacağını unutmayın, böylece printf hata ayıklamasını
sonda newline'da

25
@ErikEidt fprintf(STDERR, …)Bunun yerine kullanmanız gerektiğine dikkat edin , bu genellikle teşhis diyagnostiği için tamponlanmaz.
Deduplicator

4
@Deduplicator Hata akışına tanılama mesajları yazmanın dezavantajları da vardır - birçok komut dosyası, hata akışına bir şey yazıldığında bir programın başarısız olduğunu varsayar.
Voo

54
@Voo: Stderr'e yazdığını varsayan herhangi bir programın bir hatanın yanlış olduğunu gösterdiğini iddia ediyorum. İşlemin çıkış kodu, başarısız olup olmadığını gösteren şeydir. Bir başarısızlık olsaydı, stderr çıkışı nedenini açıklayacaktır . İşlem başarılı bir şekilde çıkarsa (çıkış kodu sıfır) o zaman stderr çıktısı, bir makine kullanıcısı için özel olarak makineye özel bir anlamsal olmayan (örneğin insan tarafından okunabilen uyarılar içerebilir), örneğin insan tarafından okunabilen uyarıları içerecek şekilde bilgi çıktısı olarak kabul edilmelidir. Program, daha sonraki işlemler için uygun olabilir.
Daniel Pryden

23
@Voo: Hangi programları anlatıyorsunuz? Açıkladığınız gibi davranan, yaygın olarak kullanılan herhangi bir yazılım paketinin farkında değilim. Bunu yapan programlar olduğunu biliyorum ama bu yukarıda tarif ettiğim konvansiyonu yeni yaptığım gibi değil: bir Unix veya Unix benzeri ortamdaki programların büyük çoğunluğunun çalışması ve benim bildiğim gibi Programların büyük çoğunluğu her zaman vardır. Herhangi bir programın stderr'e yazmaktan kaçınması için kesinlikle savunuculuk yapmam.
Daniel Pryden

73

POSIX sistemlerinde (temel olarak herhangi bir linux, BSD, bulabileceğiniz açık kaynak tabanlı sistem), bir satır yeni satır ile sonlandırılmış bir karakter dizisi olarak tanımlanır \n. Bu, tüm standart komut satırı araçları (ancak bunlarla sınırlı olmamak üzere) dahil olmak üzere, üzerine inşa temel varsayım wc, grep, sed, awk, ve vim. Bu aynı zamanda bazı editörlerin (gibi vim) her zaman \nbir dosyanın sonuna bir ekleme yapmasının ve daha önceki C standartlarının başlıkların bir \nkarakterle bitmesini gerektirmesinin nedenidir .

Btw: \nSonlandırılmış satırlara sahip olmak metnin işlenmesini çok kolaylaştırır: Sonlandırıcınız olduğunda tam bir satıra sahip olduğunuzdan emin olursunuz. Ayrıca, henüz bu terminatörle karşılaşmamışsanız daha fazla karaktere bakmanız gerektiğinden eminsiniz.

Tabii ki, bu programların giriş tarafındadır, ancak program çıktısı tekrar program girişi olarak kullanılır. Bu nedenle, çıktınız, diğer programlara kesintisiz girdi sağlama adına yapılan sözleşmeye bağlı kalmalıdır.


25
Bu, yazılım mühendisliğindeki en eski tartışmalardan biri: yeni satırları (ya da programlama dilinde, noktalı virgül gibi bir "deyimin sonu" işaretleyicisini) satır sonlandırıcıları veya satır ayırıcıları olarak kullanmak daha mı iyi? Her iki yaklaşımın da artıları ve eksileri var. Windows dünyası çoğunlukla newline dizisinin (tipik olarak orada CR LF olan) bir çizgi ayırıcı olduğu ve dolayısıyla bir dosyadaki son satırın bununla bitmesi gerekmediği fikrine odaklandı . Unix dünyasında, yeni satır dizisi (LF) satır sonlandırıcısıdır ve bu varsayım etrafında birçok program oluşturulmuştur.
Daniel Pryden

33
POSIX bile bir satırı "Sıfır ya da daha fazla sayıda yeni satır dışı karakter artı bir son satır yeni satır karakteri" olarak tanımlar .
boru

6
@Pipe'nin dediği gibi, POSIX şartnamesinde belirtildiği gibi, cevabın önerdiği gibi fiilen aksine , muhtemelen de jüri de olabilir mi?
Baldrickk

4
@Baldrickk Sağ. Şimdi daha olumlu olması için cevabımı güncelledim.
cmaster

C aynı zamanda kaynak dosyaları için de bu kuralı yapar: yeni bir satırla bitmeyen boş olmayan bir kaynak dosya tanımsız davranışa neden olur.
R ..

31

Başkalarının söylediklerine ek olarak, çok daha basit bir neden olduğunu hissediyorum: bu standart. Her ne zaman STDOUT şey baskılar, neredeyse her zaman olduğunu varsayar zaten yenisini başlamak gerekmez böylece yeni bir çizgi, vb. Ayrıca yazılacak bir sonraki satırın da aynı şekilde hareket edeceğini varsayar, bu nedenle yeni bir satır başlatarak yararlı bir şekilde sona erer.

Önde gelen newline satırlarını standart son satır newline satırlarıyla birleştirirseniz, "şöyle sonuçlanır:

Trailing-newline-line
Trailing-newline-line

Leading-newline-line
Leading-newline-line
Leading-newline-lineTrailing-newline-line
Trailing-newline-line

Leading-newline-lineTrailing-newline-line
Trailing-newline-line
Trailing-newline-line

... ki muhtemelen istediğin gibi değil.

Kodunuzda yalnızca önde gelen yeni satırları kullanıyor ve yalnızca bir IDE'de çalıştırıyorsanız, tamam olabilir. Bir terminalde çalıştırdığınızda veya kodunuzun yanında STDOUT'a yazacak başkalarının kodunu girdiğinizde, yukarıdaki gibi istenmeyen bir çıktı göreceksiniz.


2
Benzer bir şey etkileşimli bir kabukta kesintiye uğramış programlarda da olur - kısmi bir satır yazdırılırsa (yeni hattını eksikse), imlecin hangi sütunun üzerinde olduğu hakkında kabuk karışır ve sonraki komut satırını düzenlemeyi zorlaştırır. Size öncü bir yeni hat eklemezseniz $PS1, bu geleneksel programları izleyen tahriş edici olacaktır.
Toby Speight

17

Yüksek oy alan cevaplar, takip eden yeni hatların tercih edilmesinin neden mükemmel teknik sebepler verdiğinden, ben buna başka bir açıdan yaklaşacağım.

Benim düşünceme göre, aşağıdakiler bir programı daha okunaklı kılmaktadır:

  1. yüksek bir sinyal-gürültü oranı (aka basit ama basit değil)
  2. önemli fikirler önce gelir

Yukarıdaki noktalardan, takip eden yeni satırların daha iyi olduğunu iddia edebiliriz. Yeni satırlar, mesajla karşılaştırıldığında "paraziti" biçimlendirmektedir, mesaj öne çıkmalı ve böylece önce gelmelidir (sözdizimi vurgulaması da yardımcı olabilir).


19
Evet, "ok\n"çok daha iyidir "\nok"...
cmaster

@cmaster: MacOS'ta, Pascal'da string dizgeli API'leri kullanarak okumaya başladığımı hatırlatıyor, bu da tüm dizgelerin değişmezlerini gibi sihirli bir kaçış kodu ile ön eklemelerini gerektiriyordu "\pFoobar".
Kasım’da 23:18

16

İzleyen yeni satırları kullanmak daha sonra yapılan değişiklikleri kolaylaştırır.

OP koduna dayanan (çok önemsiz) bir örnek olarak , "Başlatma" iletisinden önce bir çıktı üretmeniz gerektiğini ve bu çıkışın kodun farklı bir kaynak dosyadaki farklı bir mantıksal bölümünden geldiğini varsayalım .

İlk sınamayı çalıştırdığınızda ve "Başlatılıyor" u bulduğunuzda, şimdi başka bir çıktının satırının sonuna eklenir, yazdırıldığı yeri bulmak için kodu aramanız gerekir, sonra "Başlatılıyor" olarak "\ nBaşlatmayı" değiştirmeyi umarsınız "farklı durumlarda başka bir şeyin biçimini mahvetmiyor.

Şimdi, yeni çıktınızın gerçekte isteğe bağlı olduğu gerçeğiyle nasıl başa çıkacağınızı düşünün, bu nedenle "\ nBaşlatılıyor" olarak yaptığınız değişiklik bazen çıktının başlangıcında istenmeyen bir boş satır oluşturur ...

Önceden herhangi bir çıktı olup olmadığını belirten bir genel ( şok korku ?? !!! ) bayrağı ayarlayıp, "Başlatma" seçeneğini isteğe bağlı bir "\ n" harfi ile yazdırmak için test ediyor musunuz ya da "\ n" ile birlikte çıkarıyor musunuz? önceki çıktınızı alın ve gelecekteki kod okuyucularından neden bu "Başlatma" nın neden diğer tüm çıkış iletilerindeki gibi öncü "\ n" olmadığına merak edin.

İzleyen yeni satırları tutarlı bir şekilde çıkarırsanız, sonlandırılması gereken satırın sonuna geldiğinizi bildiğiniz noktada, tüm bu sorunlardan kaçının. Unutmayın, bunun bir satır parçasını parçalayan bazı mantıkların sonunda ayrı bir "p" ("\ n") ifadesi gerektirebileceğini unutmayın; bunu yapmak değil başka bir yerde.


1
Her bağımsız çıktı öğesinin kendi satırında görünmesi gerekiyorsa, sondaki yeni satırlar iyi çalışabilir. Bununla birlikte, pratikte birden fazla öğe konsolide edilmek zorundaysa, işler daha karmaşık hale gelir. Tüm çıktıyı ortak bir rutinde beslemek pratikse, son karakter bir CR ise, satır sonuna kadar net bir satır ekleme işlemi, son karakter yeni bir satırsa ve son karakter ise bir satırdı başka bir şeydi, programların bir dizi bağımsız satır çıktısı dışında bir şey yapması gerekiyorsa faydalı olabilir.
supercat

7

Printf ile başa çıkmak yerine neden takip eden yeni satırlar kullanıyorsunuz?

C spec ile yakından eşleşin.

C kütüphanesi tanımlar çizgi olarak sona eren bir ile yeni satır karakteri '\n' .

Bir metin akışı , her bir satır sıfır veya daha fazla karakter artı bir sonlandırıcı yeni satır karakteri içeren satırlardan oluşan sıralı bir karakter dizisidir . Son satırın sonlandırıcı yeni satır karakteri gerektirip gerektirmediği, uygulama tarafından tanımlanır. C11 §7.21.2 2

Verileri satır olarak yazan kod , kütüphane kavramına uygun olacaktır.

printf("Initializing"); // Write part of a line
printf("\nProcessing"); // Finish prior line & write part of a line
printf("\nExiting");    // Finish prior line & write an implementation-defined last line

printf("Initializing\n");//Write a line 
printf("Processing\n");  //Write a line
printf("Exiting");       //Write an implementation-defined last line

Re: son satır sonlandırıcı bir yeni satır karakteri gerektirir . Her zaman '\n'çıktıya bir son yazmasını ve girdideki eksikliğini tolere etmeyi öneririm .


Yazım denetimi

Yazım denetleyicim şikayet ediyor. Belki senin de öyle.

  v---------v Not a valid word
"\nProcessing"

 v--------v OK
"Processing\n");

Bununla ispell.eldaha iyi başa çıkmak için bir kez daha geliştirdim \tSorunun daha sık olduğunu ve dize birden çok belirteçe bölünerek önlenebileceğini kabul ediyorum , ancak HTML’nin metin olmayan kısımlarını seçici olarak atlamak, daha genel "yoksay" çalışmasının yalnızca bir yan etkisi oldu. veya MIME çok parçalı gövdeleri ve kodun yorum yapmayan kısımları. Ben her zaman onu uygun meta verinin olduğu yerlere (örneğin <p lang="de_AT">veya Content-Language: gd) geçiş yapmak için genişletmek istemiştim ama asla bir Yuvarlak Tutama almadım. Ve kaleci, yamamı tamamen reddetti. :-(
Toby Speight

@TobySpeight İşte yuvarlak bir özellik . Yeniden geliştirmeyi denemek için sabırsızlanıyoruz ispell.el.
chux

4

Öncü yeni hatlar, şartlı durumlar söz konusu olduğunda kodu yazmayı genellikle kolaylaştırabilir, örneğin

printf("Initializing");
if (jobName != null)
    printf(": %s", jobName);
init();
printf("\nProcessing");

(Ancak başka yerlerde de belirtildiği gibi, CPU zaman alan herhangi bir işlem yapmadan önce çıkış tamponunu temizlemeniz gerekebilir.)

Bu nedenle, bunu yapmanın her iki yolu için de iyi bir durum olabilir, ancak şahsen printf () 'i sevmiyorum ve çıktıyı oluşturmak için özel bir sınıf kullanırdım.


1
Bu sürümün neden yeni satırlarda yazmaktan daha kolay yazıldığını açıklayabilir misiniz? Bu örnekte bana görünmüyor. Bunun yerine, bir sonraki çıktının aynı satıra eklenmesiyle ortaya çıkan sorunları görebiliyordum "\nProcessing".
Raimund Krämer,

Raimund gibi ben de bu şekilde çalışmaktan kaynaklanan sorunları görebiliyorum. Ararken çevreleyen baskıları göz önüne almanız gerekir printf. Tüm "Başlatma" satırını şartlandırmak istiyorsanız ne yapmalıyım? "Procesing" satırını bu durumda yeni bir satırın önüne koyup koymayacağınızı bilmek için eklemeniz gerekir. İleride başka bir baskı varsa ve "İşleniyor" satırını şartlandırmanız gerekiyorsa, her durumda başka bir yeni satır ekleyip eklemeyeceğinizi bilmek için bir sonraki baskıyı da eklemeniz gerekir.
JoL

2
Prensip ile aynı fikirdeyim, ancak örnek iyi değil. Daha uygun bir örnek, her satırda belirli sayıda öğe çıkarması beklenen kodla olacaktır. Çıktının yeni bir satırla biten bir başlık ile başlaması ve her satırın bir başlık ile başlaması gerekiyorsa, kullanmaktan daha kolay, örneğin if ((addr & 0x0F)==0) printf("\n%08X:", addr);ve koşulsuz olarak çıktıya yeni bir satır eklenmesi daha kolay olabilir. Her satırın başlığı ve sondaki yeni satır için ayrı kod.
supercat

1

Önde gelen yeni satırlar, özellikle puts()ve perrorStandart Kitaplıktaki diğer kitaplık işlevleriyle değil, kullanmanız muhtemel diğer kitaplıklarla da iyi çalışmaz .

Önceden yazılmış bir satır yazdırmak istiyorsanız (sabit veya önceden biçimlendirilmiş - örneğin ile sprintf()), o zaman puts()doğal (ve verimli) seçimdir. Ancak, puts()önceki satırı sonlandırmanın ve sonlandırılmamış bir satır yazmanın bir yolu yoktur - her zaman satır sonlandırıcısını yazar.

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.