Scala: записати рядок, який потрібно подати в одне твердження


144

Для читання файлів у Scala є

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

Чи існує еквівалентний і стислий спосіб написати рядок у файл?

Більшість мов підтримують щось подібне. Мій улюблений Groovy:

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

Я хотів би використовувати код для програм, починаючи від одного рядка до короткої сторінки коду. Тут не має сенсу користуватися власною бібліотекою. Я очікую, що сучасна мова дозволить мені щось зручно написати у файл.

Є повідомлення, подібні до цього, але вони не відповідають на моє точне запитання або зосереджені на старих версіях Scala.

Наприклад:


Дивіться це питання. Я згоден з найвищою оцінкою відповіді - краще мати свою особисту бібліотеку.
подруга

2
в цьому випадку я не згоден, що треба написати власну особисту бібліотеку. Зазвичай, коли ви пишете невеликі шматки програм, щоб робити спеціальні речі (можливо, я просто хочу записати це як сценарій масштабування на одній сторінці або просто в REPL). Тоді доступ до особистої бібліотеки стає болем.
smartnut007

В основному, схоже, що в цьому масштабі немає нічого в масштабі 2.9. Не знаю, як, якщо я повинен тримати це питання відкритим.
smartnut007

1
Якщо ви шукаєте java.io у вихідному коді Scala, ви не знайдете багато подій, а тим більше для операцій з виводу даних, зокрема PrintWriter. Отже, поки бібліотека Scala-IO не стане офіційною частиною Scala, ми повинні використовувати чисту Java, як показано парадигматикою.
PhiLho

Так, проблемі також потрібна програма Scala-io, сумісна з вдосконаленнями IO jdk7.
smartnut007

Відповіді:


77

Короткий один рядок:

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

14
Хоча цей підхід виглядає приємно, він не є безпечним ні для винятку, ні для кодування. Якщо виняток трапиться в write(), closeвін ніколи не буде викликаний, і файл не закриється. PrintWriterтакож використовується кодування системи за замовчуванням, що дуже погано для портативності. І нарешті, такий підхід генерує окремий клас спеціально для цього рядка (однак, враховуючи, що Scala вже генерує тонни класів навіть для простого коду, це навряд чи є недоліком саме по собі).
Володимир Матвєєв

Відповідно до вищевказаного коментаря, хоча це один вкладиш, він небезпечний. Якщо ви хочете більше безпеки, маючи більше можливостей навколо місця розташування і / або буферні вхід, дивіться відповідь я тільки що відправлений на аналогічну тему: stackoverflow.com/a/34277491/501113
chaotic3quilibrium

2
Для тимчасової реєстрації налагоджень я використовувавnew PrintWriter(tmpFile) { try {write(debugmsg)} finally {close} }
Рандалл Вітман

Стилістично, мабуть, краще використовувати close(), я думаю
Кейсі

Це два рядки, і їх можна легко перетворити на один такий рядок:new java.io.PrintWriter("/tmp/hello") { write("file contents"); close }
Philluminati

163

Дивно, що ніхто не пропонував операції NIO.2 (доступні з Java 7):

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

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

Я думаю, що це, безумовно, найпростіший і найпростіший і ідіоматичний спосіб, і він не потребує ніяких залежностей від самої Java.


Це чомусь ігнорує символ нового рядка.
Какаджі

@Kakaji, ви можете, будь ласка, докладно? Я щойно тестував це за допомогою рядків з новими рядками, і він відмінно працює. Він просто не може нічого фільтрувати - Files.write () записує байтовий масив як крапку, не аналізуючи його вміст. Зрештою, у деяких бінарних даних 0x0d байт може мати важливе значення, крім нового рядка.
Володимир Матвєєв

4
Додаткова примітка: якщо у вас є файл, просто '.toPath', щоб отримати перший параметр.
акауппі

1
Цей клас більш промисловий (завдяки явному вибору CharSets), але йому не вистачає простоти (і одного вкладиша ..) reflect.io.File.writeAll(contents). Чому? Три рядки, коли ви включаєте два заяви про імпорт. Можливо, IDE робить це за вас автоматично, але якщо в REPL це не так просто.
javadba

3
@javadba ми в JVM, імпорт майже не вважається "рядками", тим більше, що альтернатива майже завжди додає нову залежність від бібліотеки. Так чи інакше, Files.writeтакож приймається java.lang.Iterableяк другий аргумент, і ми можемо отримати це від Scala Iterable, тобто майже будь-якого типу колекції, використовуючи конвертер: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

Ось стислий однокласник, що використовує refle.io.file, це працює з Scala 2.12:

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

Крім того, якщо ви хочете використовувати бібліотеки Java, ви можете зробити цей злом:

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

1
+1 Чудово працює. Some/ foreachКомбо трохи наляканий, але це приходить з вигоди не TRY / улову / нарешті.
Brent Faust

3
Добре, якщо запис кидає виняток, ви можете закрити файл, якщо ви плануєте відновити виняток і прочитати / записати файл знову. На щастя, Scala надає одноразові лайнери і для цього.
Гарретт Холл

25
Це не рекомендується, оскільки пакет scala.tools.nsc.io не є частиною загальнодоступного API, але використовується компілятором.
Джованні Ботта

3
Саме так Some/ foreachhack багато хто ненавидить Scala за нечитабельний код, який викликає зловмисники.
Ерік Каплун

3
scala.tootls.nsc.io.FileПсевдонім для reflect.io.File. Ще внутрішній API, але принаймні трохи коротший.
костя

41

Якщо вам подобається синтаксис Groovy, ви можете використовувати шаблон дизайну Pimp-My-Library для приведення його до Scala:

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 )

}

Він буде працювати, як очікувалося:

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, що означає, що ресурс не закритий, поки він не буде GCed (Сміття зібрано). І ваш PrintWriter не буферизований, тому він використовує крихітний буфер JVM за замовчуванням у 8 байт, що може значно уповільнити ваш IO. Я тільки що створив відповідь на аналогічну нитка , яка займається цими питаннями: stackoverflow.com/a/34277491/501113
chaotic3quilibrium

1
Ти правий. Але рішення, яке я дав тут, добре працює для короткочасних програм з невеликим IO. Я не рекомендую це для серверного процесу або великих даних (як правило, більше 500 МБ).
парадигматичний

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

Це не працює для мене у відповіді. Немає повідомлень про помилки, але якщо я дивлюсь на /tmp/example.txt, немає.
користувач невідомий

@user невідомо, вибачте за те, що пропущено "!" в кінці команди, виправленої зараз.
xiefei

Чудово! Тепер, коли це працює, я хотів би знати чому. Що таке #>, що робить !?
користувач невідомий

10
Це рішення не є портативним! працює лише в * nix системах.
Джованні Ботта

2
Це не допоможе для написання довільних рядків. Він буде працювати лише для коротких рядків, які ви можете передати як аргументи echoінструменту командного рядка .
Багатий

15

Мікробібліотека, яку я написав: https://github.com/pathikrit/better-files

file.write("Hi!")

або

file << "Hi!"

3
Любіть свою бібліотеку! Це питання є одним із найпопулярніших результатів, коли гуглити, як написати файл зі скалою - тепер, коли ваш проект посилився, ви можете трохи розширити свою відповідь?
asac

12

Ви можете легко використовувати утиліти Apache File . Подивіться на функцію writeStringToFile. Ми використовуємо цю бібліотеку в наших проектах.


3
Я також постійно його використовую. Якщо ви уважно читаєте питання, я вже знав, чому я не хочу користуватися бібліотекою.
smartnut007

Без використання бібліотеки я створив рішення , яке обробляє виключення під час читання / запису і може бути буферном за крихітними по замовчуванням буфера , що надаються бібліотеками Java: stackoverflow.com/a/34277491/501113
chaotic3quilibrium

7

Один також має такий формат, який є і стислим, і основна бібліотека прекрасно написана (див. Вихідний код):

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

Думаю, це досить лаконічно:

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
Досить справедливо, але це "досить лаконічне" не класифікується як "одне твердження": P
Ерік Каплун

Цей код підтверджує багато сприйнятих проблем Java. На жаль, Scala не вважає IO досить важливим, тому стандартна бібліотека не включає її.
Кріс,

У відповіді приховано проблематику осиротілих ресурсів із новим FileWriter. Якщо новий FileWriter досяг успіху, але новий BufferedWriter - файли, екземпляр FileWriter більше ніколи не побачиться і залишається відкритим до GCed (зібраний сміття), і він не може бути закритий навіть тоді (через спосіб завершення гарантує роботу в JVM). Я написав відповідь на подібне питання , який адресує ці питання: stackoverflow.com/a/34277491/501113
chaotic3quilibrium

2

Це можна зробити за допомогою суміші бібліотек Java та Scala. Ви будете мати повний контроль над кодуванням символів. Але, на жаль, ручки файлів не будуть закриті належним чином.

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"))

У вас в коді виникла проблема осиротілого ресурсу. Оскільки ви не захоплюєте екземпляри до вашого дзвінка, якщо будь-який викидає виняток до того, як метод, який ви викликаєте, був переданий параметрами, ресурси, які успішно створені, не будуть закриті до GCed (зібраний сміття) і навіть то це може не бути пов'язано з тим, як GC гарантує роботу. Я написав відповідь на подібне питання , який адресує ці питання: stackoverflow.com/a/34277491/501113
chaotic3quilibrium

1
Ви маєте рацію, і ваше рішення дуже приємне. Але тут вимога робила це в один рядок. І коли ви уважно читаєте, я згадував про витік ресурсів у своїй відповіді як обмеження, яке пов'язане з цією вимогою та з моїм підходом. Ваше рішення є приємним, але не відповідало б цій вимозі.
stefan.schwetschke

2

Я знаю, що це не одна лінія, але вона вирішує питання безпеки, наскільки я можу сказати;

// 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)

Якщо вас не хвилювало обробка винятків, тоді ви можете написати

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

1

ОНОВЛЕННЯ: З тих пір я створив більш ефективне рішення, над яким я детально розробив тут: https://stackoverflow.com/a/34277491/501113

Я все більше працюю на робочому аркуші Scala в рамках програми Scala IDE for Eclipse (і я вважаю, що в IntelliJ IDEA є щось еквівалентне). У будь-якому разі, мені потрібно мати змогу зробити однолінійний вміст, щоб вивести частину вмісту, оскільки я отримав "Вихід перевищує межу обмеження". повідомлення, якщо я роблю щось важливе, особливо з колекціями Scala.

Я придумав однолінійний вкладиш, який я вставляв у верхню частину кожного нового робочого листа Scala, щоб спростити це (і тому мені не потрібно робити цілі вправи із імпорту зовнішньої бібліотеки для дуже простої потреби). Якщо ви є стикером і помітили, що це технічно два рядки, це лише зробити його більш читабельним на цьому форумі. Це один рядок у моєму листі Scala.

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}}

А використання просто:

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

Це дозволяє мені необов'язково вказати ім'я файлу, якщо я хочу мати додаткові файли за замовчуванням (який повністю перезаписує файл щоразу, коли метод викликається).

Отже, друге використання просто:

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

Насолоджуйтесь!


1

Використовуйте бібліотеку аммоніту ops . Синтаксис дуже мінімальний, але ширина бібліотеки майже така ж широка, як того, що можна було б очікувати від спроби такого завдання на мові сценарію оболонки, як bash.

На сторінці, до якої я пов’язаний, показано численні операції, які можна виконати з бібліотекою, але щоб відповісти на це запитання, це приклад написання у файл

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

-1

Завдяки магії крапки з комою ви зможете зробити все, що вам подобається, з одного вкладиша.

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()

Тут немає аргументів. Я вирішив не згадувати, які API потрібні мені для того, щоб залишити частину свого життя, тому я завжди завжди це роблю.
Іон Фріман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.