Scala: bir ifadede dosyaya string yaz


144

Scala'daki dosyaları okumak için

Source.fromFile("file.txt").mkString

Dosyaya bir dize yazmanın eşdeğer ve özlü bir yolu var mı?

Çoğu dil böyle bir şeyi destekler. Benim favorim Groovy:

def f = new File("file.txt")
// Read
def s = f.text
// Write
f.text = "file contents"

Kodu tek bir satırdan kısa bir kod sayfasına kadar değişen programlar için kullanmak istiyorum. Kendi kütüphanenizi kullanmanız burada bir anlam ifade etmiyor. Modern bir dilin bir dosyaya rahatça bir şeyler yazmama izin vermesini bekliyorum.

Buna benzer yayınlar var, ancak tam soruma cevap vermiyorlar veya eski Scala sürümlerine odaklanıyorlar.

Örneğin:


Bu soruya bakın . En yüksek dereceli cevaba katılıyorum - kendi kişisel kütüphanenize sahip olmak daha iyidir.
ffriend

2
bu durumda kişinin kendi kişisel kütüphanesini yazması gerektiğine katılmıyorum. Genellikle, geçici şeyler yapmak için küçük program parçaları yazarken (belki de sadece tek bir sayfa scala komut dosyası olarak veya sadece REPL'de yazmak istiyorum). Sonra kişisel bir kütüphaneye erişmek acı verici olur.
smartnut007

Temel olarak, bu noktada scala 2.9'da hiçbir şey yok gibi görünüyor. Bu soruyu açık tutmalı mıyım emin değilim.
smartnut007

1
Scala kaynak kodunda java.io için arama yaparsanız, pek çok olay bulamazsınız ve özellikle PrintWriter olmak üzere çıktı işlemleri için daha da azdır. Bu nedenle, Scala-IO kütüphanesi Scala'nın resmi parçası oluncaya kadar, paradigmatik tarafından gösterildiği gibi saf Java kullanmalıyız.
PhiLho

evet, prob ayrıca jdk7 IO geliştirmeleriyle uyumlu bir skalaya ihtiyaç duyar.
smartnut007

Yanıtlar:


77

Kısa bir satır:

import java.io.PrintWriter
new PrintWriter("filename") { write("file contents"); close }

14
Bu yaklaşım hoş görünse de, ne istisna güvenli ne de kodlama güvenli değildir. Bir istisna de olursa write(), closedenilen asla ve dosya kapatıldı edilmeyecektir. PrintWriterayrıca taşınabilirlik için çok kötü olan varsayılan sistem kodlamasını da kullanır . Ve son olarak, bu yaklaşım özellikle bu çizgi için ayrı bir sınıf oluşturur (ancak, Scala'nın basit bir kod için bile tonlarca sınıf oluşturduğu göz önüne alındığında, bu kendi başına bir dezavantajdır).
Vladimir Matveev

Yukarıdaki yoruma göre, bu tek bir astar olsa da, güvensizdir. Konum hakkında daha fazla seçeneğe sahip olmak ve / veya girişi arabelleğe alırken daha fazla güvenlik istiyorsanız, benzer bir konuya az önce gönderdiğim yanıta bakın: stackoverflow.com/a/34277491/501113
chaotic3quilibrium 14:15

2
Geçici hata ayıklama kaydı için kullandımnew PrintWriter(tmpFile) { try {write(debugmsg)} finally {close} }
Randall Whitman

Stilistik olarak kullanmak muhtemelen daha iyi close(), sanırım
Casey

Bu iki satır ve kolayca böyle bir satıra yapılabilir:new java.io.PrintWriter("/tmp/hello") { write("file contents"); close }
Philluminati

163

Hiç kimsenin NIO.2 işlemlerini önermemesi gariptir (Java 7'den beri mevcuttur):

import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets

Files.write(Paths.get("file.txt"), "file contents".getBytes(StandardCharsets.UTF_8))

Bence bu en basit ve en kolay ve en deyimsel yol ve Java'nın kendisi için herhangi bir bağımlılığa ihtiyaç duymuyor.


Bu, herhangi bir nedenle yeni satır karakterini yok sayar.
Kakaji

@Kakaji, lütfen biraz ayrıntı verebilir misiniz? Yeni satırlı dizelerle test ettim ve mükemmel çalışıyor. Hiçbir şeyi filtreleyemez - Files.write (), bayt dizisini içeriğini analiz etmeden bir blob olarak yazar. Sonuçta, bazı ikili verilerde 0x0d baytı yeni satır dışında önemli bir anlama sahip olabilir.
Vladimir Matveev

4
Ek not: Eğer bir Dosyanız varsa, ilk parametreyi almak için '.toPath' yeterlidir.
akauppi

1
Bu daha endüstriyel sınıf (açık CharSets seçim nedeniyle) ama basitliği (ve bir astar ..) yoktur reflect.io.File.writeAll(contents). Neden? İki içe aktarma ifadesini eklediğinizde üç satır. Belki IDE bunu sizin için otomatik olarak yapar, ancak REPL'de bu kadar kolay değildir.
javadba

3
@javadba JVM'deyiz, ithalat hemen hemen 'satır' olarak sayılmıyor çünkü özellikle alternatif neredeyse her zaman yeni bir kütüphane bağımlılığı ekliyor. Her neyse, ikinci argüman olarak Files.writeda kabul eder java.lang.Iterableve bunu bir Scala'dan Iterable, yani hemen hemen her türlü toplama türünde, bir dönüştürücü kullanarak elde edebiliriz :import java.nio.file.{Files, Paths}; import scala.collection.JavaConverters.asJavaIterableConverter; val output = List("1", "2", "3"); Files.write(Paths.get("output.txt"), output.asJava)
Yawar

83

Refl.io.file kullanan kısa bir tek astar, bu Scala 2.12 ile çalışır:

reflect.io.File("filename").writeAll("hello world")

Alternatif olarak, Java kitaplıklarını kullanmak istiyorsanız bu hack'i yapabilirsiniz:

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}

1
+1 Harika çalışıyor. Some/ foreachAçılan biraz korkak, ama nihayet hiçbir try / catch yararına / ile geliyor.
Brent Faust

3
Yazma bir istisna atarsa, istisnadan kurtarmayı ve dosyayı tekrar okuma / yazmayı planlıyorsanız dosyayı kapatmak isteyebilirsiniz. Neyse ki scala bunu yapmak için bir gömlek sağlar.
Garrett Hall

25
Scala.tools.nsc.io paketi genel API'nin bir parçası olmadığı, ancak derleyici tarafından kullanıldığı için bu önerilmez.
Giovanni Botta

3
Some/ foreachBirçok kişi bunu üretmek için korsanları neden okunmaz kodu için Scala neden nefret kesmek tam olarak budur.
Erik Kaplun

3
scala.tootls.nsc.io.Fileiçin bir takma addır reflect.io.File. Hala dahili API, ancak en azından biraz daha kısa.
kostja

41

Groovy sözdizimini beğendiyseniz, Scala'ya getirmek için Pimp-My-Library tasarım desenini kullanabilirsiniz:

import java.io._
import scala.io._

class RichFile( file: File ) {

  def text = Source.fromFile( file )(Codec.UTF8).mkString

  def text_=( s: String ) {
    val out = new PrintWriter( file , "UTF-8")
    try{ out.print( s ) }
    finally{ out.close }
  }
}

object RichFile {

  implicit def enrichFile( file: File ) = new RichFile( file )

}

Beklendiği gibi çalışacaktır:

scala> import RichFile.enrichFile
import RichFile.enrichFile

scala> val f = new File("/tmp/example.txt")
f: java.io.File = /tmp/example.txt

scala> f.text = "hello world"

scala> f.text
res1: String = 
"hello world

2
Source.fromFile döndürülen örneğinde hiçbir zaman kapatmayı çağırmazsınız, bu da kaynağın GCed (Çöp Toplanan) olana kadar kapalı olmadığı anlamına gelir. PrintWriter'ınız arabelleğe alınmadığından, 8 baytlık küçük JVM varsayılan arabelleğini kullanıyor ve IO'nuzu önemli ölçüde yavaşlatıyor. Ben sadece bu sorunları ele benzer bir iş parçacığı üzerinde bir cevap oluşturduk: stackoverflow.com/a/34277491/501113
chaotic3quilibrium 14:15

1
Haklısın. Ancak burada verdiğim çözüm, küçük IO'lu kısa ömürlü programlar için iyi çalışıyor. Sunucu işlemi veya büyük veriler için tavsiye etmiyorum (genel bir kural olarak, 500 MB'den fazla).
paradigmatik

22
import sys.process._
"echo hello world" #> new java.io.File("/tmp/example.txt") !

REPL'de benim için çalışmıyor. Hata iletisi yok, ancak /tmp/example.txt dosyasına bakarsam hayır yoktur.
kullanıcı bilinmiyor

@user bilinmiyor, '!' komutun sonunda, şimdi düzeltildi.
xiefei

Olağanüstü! Şimdi işe yaradığına göre, nedenini bilmek istiyorum. Nedir #>, ne yapar !?
kullanıcı bilinmiyor

10
Bu çözüm taşınabilir değil ! sadece * nix sistemlerinde çalışır.
Giovanni Botta

2
Bu, rastgele dizeler yazmak için çalışmaz. Yalnızca komut satırı echoaracına bağımsız değişken olarak iletebileceğiniz kısa dizeler için çalışır .
Zengin


12

Apache Dosya Yardımcı Programlarını kolayca kullanabilirsiniz . İşleve bakın writeStringToFile. Bu kütüphaneyi projelerimizde kullanıyoruz.


3
Ben de her zaman kullanıyorum. Soruyu dikkatlice okuduysanız neden bir kütüphane kullanmak istemediğimi zaten belirtmiştim.
smartnut007

Bir kütüphane kullanmadan, okuma / yazma sırasında istisnaları işleyen ve Java kütüphaneleri tarafından sağlanan küçük arabellek varsayılanlarının ötesinde arabelleğe alınabilen bir çözüm oluşturdum: stackoverflow.com/a/34277491/501113
chaotic3quilibrium

7

Birisi de hem özlü hem de alttaki kütüphane güzelce yazılan bu biçime sahiptir (kaynak koduna bakın):

import scalax.io.Codec
import scalax.io.JavaConverters._

implicit val codec = Codec.UTF8

new java.io.File("hi-file.txt").asOutput.write("I'm a hi file! ... Really!")

7

Bu yeterince özlü, sanırım:

scala> import java.io._
import java.io._

scala> val w = new BufferedWriter(new FileWriter("output.txt"))
w: java.io.BufferedWriter = java.io.BufferedWriter@44ba4f

scala> w.write("Alice\r\nBob\r\nCharlie\r\n")

scala> w.close()

4
Yeterince adil, ama bu "yeterince özlü", "tek bir ifade" olarak sınıflandırılmaz: P
Erik Kaplun

Bu kod, Java'nın algılanan sorunlarının çoğunu ortadan kaldırır. Ne yazık ki Scala, IO'yu yeterince önemli bulmuyor, bu nedenle standart kütüphane bir tane içermiyor.
Chris

Yanıt, yeni FileWriter ile yetim kalmış bir kaynak sorununu gizliyor. Yeni FileWriter başarılı olursa, ancak yeni BufferedWriter başarısız olursa, FileWriter örneği bir daha asla görülmez ve GCed (Garbage Collected) olana kadar açık asılı kalır ve o zaman bile kapatılamaz (kesinleştirme garantilerinin JVM'de çalışması nedeniyle). Bu sorunları ele alan benzer bir soruya cevap yazdım: stackoverflow.com/a/34277491/501113
chaotic3quilibrium 14:15

2

Bunu Java ve Scala kütüphanelerinin bir karışımıyla yapabilirsiniz. Karakter kodlaması üzerinde tam kontrole sahip olacaksınız. Ancak ne yazık ki, dosya tanıtıcıları düzgün kapatılmaz.

scala> import java.io.ByteArrayInputStream
import java.io.ByteArrayInputStream

scala> import java.io.FileOutputStream
import java.io.FileOutputStream

scala> BasicIO.transferFully(new ByteArrayInputStream("test".getBytes("UTF-8")), new FileOutputStream("test.txt"))

Kodunuzda artık bir kaynak örneği sorunu var. Çağrınızdan önce örnekleri yakalamadığınız için, çağırdığınız yöntem parametreleri geçmeden önce bir istisna atarsa, başarılı bir şekilde başlatılan kaynakların GCed (Çöp Toplanan) ve hatta bu durumda GC'nin garanti çalışma şekline bağlı olmayabilir. Bu sorunları ele alan benzer bir soruya cevap yazdım: stackoverflow.com/a/34277491/501113
chaotic3quilibrium 14:15

1
Haklısınız ve çözümünüz oldukça güzel. Ama burada gereksinim bir satırda yapılıyordu. Dikkatlice okuduğunuzda, cevabımdaki kaynak sızıntısından bu gereklilik ve yaklaşımımla gelen bir sınırlama olarak bahsetmiştim. Çözümünüz güzel, ancak bu tek hat gereksinimiyle eşleşmiyor.
stefan.schwetschke

2

Bunun tek satır olmadığını biliyorum, ancak güvenlik sorunlarını anlayabildiğim kadar çözüyor;

// This possibly creates a FileWriter, but maybe not
val writer = Try(new FileWriter(new File("filename")))
// If it did, we use it to write the data and return it again
// If it didn't we skip the map and print the exception and return the original, just in-case it was actually .write() that failed
// Then we close the file
writer.map(w => {w.write("data"); w}).recoverWith{case e => {e.printStackTrace(); writer}}.map(_.close)

İstisna işlemeyi önemsemediyseniz yazabilirsiniz

writer.map(w => {w.writer("data"); w}).recoverWith{case _ => writer}.map(_.close)

1

GÜNCELLEME: O zamandan beri burada üzerinde durduğum daha etkili bir çözüm oluşturdum: https://stackoverflow.com/a/34277491/501113

Kendimi Eclipse için Scala IDE içindeki Scala Çalışma Sayfasında giderek daha fazla çalışıyorum (ve IntelliJ IDEA'da eşdeğer bir şey olduğuna inanıyorum). Neyse, "Çıktı kesme sınırını aşıyor" alırken bazı içerik çıktı için bir astar yapmak gerekir. Özellikle Scala koleksiyonları için önemli bir şey yapıyorsam mesaj.

Bunu basitleştirmek için her yeni Scala Çalışma Sayfasının üstüne yerleştirdiğim bir astar ile geldim (ve böylece çok basit bir ihtiyaç için tüm harici kütüphane ithalat alıştırmasını yapmak zorunda değilim). Eğer bir çöpçü iseniz ve teknik olarak iki satır olduğunu fark ederseniz, bu forumda sadece daha okunabilir hale getirmek içindir. Scala Çalışma Sayfamdaki tek bir satır.

def printToFile(content: String, location: String = "C:/Users/jtdoe/Desktop/WorkSheet.txt") =
  Some(new java.io.PrintWriter(location)).foreach{f => try{f.write(content)}finally{f.close}}

Ve kullanımı basitçe:

printToFile("A fancy test string\ncontaining newlines\nOMG!\n")

Bu, varsayılanın ötesinde (yöntem her çağrıldığında dosyanın üzerine tamamen yazılır) ek dosyalar olmasını istiyorsanız, isteğe bağlı olarak dosya adını sağlamamı sağlar.

Yani, ikinci kullanım basitçe:

printToFile("A fancy test string\ncontaining newlines\nOMG!\n", "C:/Users/jtdoe/Desktop/WorkSheet.txt")

Zevk almak!


1

Amonit ops kütüphanesini kullanın . Sözdizimi çok azdır, ancak kütüphanenin genişliği neredeyse bash gibi bir kabuk komut dosyası dilinde böyle bir görevi denemekten beklendiği kadar geniştir.

Bağlantı verdiğim sayfada, kitaplık ile yapılabilecek çok sayıda işlem gösteriliyor, ancak bu soruyu cevaplamak için, bu bir dosyaya yazma örneğidir

import ammonite.ops._
write(pwd/'"file.txt", "file contents")

-1

Noktalı virgülün büyüsü sayesinde, bir astar gibi istediğiniz her şeyi yapabilirsiniz.

import java.io.PrintWriter
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.charset.StandardCharsets
import java.nio.file.StandardOpenOption
val outfile = java.io.File.createTempFile("", "").getPath
val outstream = new PrintWriter(Files.newBufferedWriter(Paths.get(outfile)
  , StandardCharsets.UTF_8
  , StandardOpenOption.WRITE)); outstream.println("content"); outstream.flush(); outstream.close()

Burada tartışma yok. Hangi API'lerin hayatımın bir kısmını temizlemem gerektiğini hatırlamamaya karar verdim, bu yüzden her zaman yaparım.
Ion Freeman
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.