There may be various way to read plain text file in kotlin.
I want know what are the possible ways and how I can use them.
1. Using BufferedReader
import java.io.File
import java.io.BufferedReader
fun main(args: Array<String>) {
val bufferedReader: BufferedReader = File("example.txt").bufferedReader()
val inputString = bufferedReader.use { it.readText() }
println(inputString)
}
2. Using InputStream
Read By Line
import java.io.File
import java.io.InputStream
fun main(args: Array<String>) {
val inputStream: InputStream = File("example.txt").inputStream()
val lineList = mutableListOf<String>()
inputStream.bufferedReader().forEachLine { lineList.add(it) }
lineList.forEach{println("> " + it)}
}
Read All Lines
import java.io.File
import java.io.InputStream
fun main(args: Array<String>) {
val inputStream: InputStream = File("example.txt").inputStream()
val inputString = inputStream.bufferedReader().use { it.readText() }
println(inputString)
}
3. Use File directly
import java.io.File
import java.io.BufferedReader
fun main(args: Array<String>) {
val lineList = mutableListOf<String>()
File("example.txt").useLines { lines -> lines.forEach { lineList.add(it) }}
lineList.forEach { println("> " + it) }
}
I think the simplest way to code is using kotlin.text and java.io.File
import java.io.File
fun main(args: Array<String>) {
val text = File("sample.txt").readText()
println(text)
}
The answers above here are all based on Kotlin Java. Here is a Kotlin Native way to read text files:
val bufferLength = 64 * 1024
val buffer = allocArray<ByteVar>(bufferLength)
for (i in 1..count) {
val nextLine = fgets(buffer, bufferLength, file)?.toKString()
if (nextLine == null || nextLine.isEmpty()) break
val records = parseLine(nextLine, ',')
val key = records[column]
val current = keyValue[key] ?: 0
keyValue[key] = current + 1
}
fun parseLine(line: String, separator: Char) : List<String> {
val result = mutableListOf<String>()
val builder = StringBuilder()
var quotes = 0
for (ch in line) {
when {
ch == '\"' -> {
quotes++
builder.append(ch)
}
(ch == '\n') || (ch == '\r') -> {}
(ch == separator) && (quotes % 2 == 0) -> {
result.add(builder.toString())
builder.setLength(0)
}
else -> builder.append(ch)
}
}
return result
}
See: https://github.com/JetBrains/kotlin-native/blob/master/samples/csvparser/src/csvParserMain/kotlin/CsvParser.kt
Anisuzzaman's answer lists several possibilities.
The main differences between them are in whether the file is read into memory as a single String, read into memory and split into lines, or read line-by-line.
Obviously, reading the entire file into memory in one go can take a lot more memory, so that's something to avoid unless it's really necessary. (Text files can get arbitrarily big!) So processing line-by-line with BufferedReader.useLines() is often a good approach.
The remaining differences are mostly historical. Very early versions of Java used InputStream &c which didn't properly distinguish between characters and bytes; Reader &c were added to correct that. Java 8 added ways to read line-by-line more efficiently using streams (e.g. Files.lines()). And more recently, Kotlin has added its own extension functions (e.g. BufferedReader.useLines()) which make it even simpler.
To read a text file, it must first be created. In Android Studio, you would create the text file like this:
1) Select "Project" from the top of the vertical toolbar to open the project "tool window"
2) From the drop-down menu at the top of the "tool window", select "Android"
3) Right-click on "App" and select "New"
then -> "Folder" (the one with the green Android icon beside it)
then -> "Assets Folder"
4) Right-click on the "assets" folder after it appears in the "tool window"
5) Select "New" -> "File"
6) Name the file, and included the extension ".txt" if it is text file, or ".html" if it is for WebView
7) Edit the file or cut and paste text into it. The file will now display under the "Project" files in the "tool window" and you will be able to double-click it to edit it at any time.
TO ACCESS THIS FILE, use a prefix of "application.assets." followed by someFunction(fileName). For example (in Kotlin):
val fileName = "townNames.txt"
val inputString = application.assets.open(fileName).bufferedReader().use { it.readText() }
val townList: List<String> = inputString.split("\n")
how to apply Documents path on that:
fun main(args: Array<String>) {
val inputStream: InputStream = File("example.txt").inputStream()
val inputString = inputStream.bufferedReader().use { it.readText() }
println(inputString)
}
Related
It is implementing a file download API
The problem is that the Korean file name is broken
text: "AWS자산.svg"
my code ↓
import (
"github.com/gin-gonic/gin"
"net/url"
)
func FileDownload(c *gin.Context) {
result, infos := fileDownload(c)
if result.Code == 200 {
c.Writer.Header().Set("Content-Type", infos.FileType)
/* infos.FileOgName = AWS자산.svg */
c.FileAttachment(infos.FilePath, infos.FileOgName) // 1 image
c.FileAttachment(infos.FilePath, url.QueryEscape(infos.FileOgName)) // 2 image
} else {
c.JSON(result.Code, result)
}
}
// 3 image
func (c \*Context) FileAttachment(filepath, filename string) {
c.Writer.Header().Set("content-disposition", fmt.Sprintf(attachment; filename\*="UTF-8''%s", filename))
http.ServeFile(c.Writer, c.Request, filepath)
}
I conducted three tests
I sent you a pure file name.
test 1 image
url encoding
test 2 image
Apply by referring to the Internet (filename*="UTF-8''%s")
test 3 image
The problem has not been solved.
I need help from good developers
----------2022-12-09 update-----------
[go gin 1.8.1]
The module update has been completed, but the problem remains.
// FileAttachment writes the specified file into the body stream in an efficient way
// On the client side, the file will typically be downloaded with the given filename
func (c *Context) FileAttachment(filepath, filename string) {
if isASCII(filename) {
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`)
} else {
c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename))
}
http.ServeFile(c.Writer, c.Request, filepath)
}
test 4 image
Singleton object LongLines which I created can't be recognized in object with main function (FindingLines). I put their files (LongLines.scala, FindingLines.scala ) in /src/main/scala/com/files/lines directory. Program code should have to find in particular file the lines which length is greater than parameter width.
Codes:
1) LongLines.scala (version from book I learn):
package com.files.lines
import scala.io.Source
object LongLines {
def processFile(filename: String, width: Int) {
val source = Source.fromFile(filename)
for (line <- source.getLines())
processLine(filename, width, line)
}
private def processLine(filename: String,
width: Int, line: String) {
if (line.length > width)
println(filename +": "+ line.trim)
}
}
2) LongLines.scala (my own version):
package com.files.lines
import scala.io.Source._
import java.nio.files.Paths._
object LongLines {
def processFile(filePath: String, width:Int): Unit = {
val path = get(filePath)
val fileName = path.getFileName.toString
val lines = fromFile(filePath).getLines().toList
for (line<-lines) processLines(fileName,line,width)
}
private def processLines(fileName: String, line: String, width: Int): Unit = {
if (line.length() > width) println(s"$fileName: $line");
}
}
3) FindingLines.scala (book version I used - only version):
package com.files.lines
object FindLongLines {
def main(args: Array[String]) = {
val width = args(0).toInt
for (arg <- args.drop(1))
LongLines.processFile(arg, width)
}
}
Error (compiled from linux terminal):
After I compiled program with my version and book version of LongLines.scala (separately, of course):
scalac FindingLines.scala
I've got this error:
FindLongLines.scala:15: error: not found: value LongLines
LongLines.processFile(arg, width)
^
one error found
Solution:
1) I removed "package com.file.lines" from both files (FindingLines.scala, LongLines.scala) - that means I need not to remove those files in this package (directory). Those two files could stay in src/main/scala directory.
2) scalac FindingLines.scala LongLines.scala
3) scala FindingLines.scala 45 ~/workspace/Rational/src/Rational.scala
Here example of associative array that I would like to get:
string [string] rlist = ["dima":"first", "masha":"second", "roma":"third"];
Text file that I read have very simple structure:
peter = fourth
ivan = fifth
david = sixth
string [string] strarr;
string txt = readText("test.txt");
foreach (t;txt.splitLines())
{
// ??
}
Could anybody suggest way?
It may be me but I find it hard to reason about with a for loop and temp variables, I would rather do something like:
import std.conv;
import std.stdio;
import std.array;
import std.algorithm;
void main() {
string[string] dic = File("test")
.byLine
.map!(l => l.to!string.findSplit(" = "))
.map!(l => tuple( l[0], l[2] ))
.assocArray;
}
byLine: read line by line, better than reading the whole thing and then splitting.
first map: split each line into three parts as explained by rcorre
second map: build pairs from the splitted lines
assocArray: build an associative array from those pairs.
Try the following:
import std.string : splitLines, strip;
import std.file : readText;
import std.algorithm : findSplit;
string[string] strarr;
string txt = readText("test.txt");
foreach(t ; txt.splitLines()) {
auto res = t.findSplit("=");
string key = res[0].strip;
string val = res[2].strip;
strarr[key] = val;
}
findSplit will return three ranges: the part before '=', '=', and the part after '='. strip can be used to remove whitespace around the = that would otherwise be included in the key and value.
If you want a more robust solution, you could consider a D library for reading config/ini files like onyx-config, ctini, or dini.
I have a file with content like this:
"Some","Words","separated","by","comma","and","quoted","with","double","quotes"
The File is to large to read it into just on String.
What is the simplest way to split it into a Traversable of Strings, with each element being a word?
If it matters: While the content of the file won't fit in a single String the resulting Traversable might be a List without a problem.
Here is an adaptation of your own solution, using JavaConversions to manipulate the Java iterator as a Scala one.
import java.util.Scanner
import java.io.File
import scala.collection.JavaConversions._
val scanner = new Scanner(new File("...")).useDelimiter(",")
scanner.map(_.trim).map(quoted => quoted.substring(1, quoted.length - 1))
This gives you an iterator. You can always convert it to a list using e.g. .toList.
Here is a version using stringLit and repsep from Scala parser combinators. I won't vouch for its efficiency, though.
import scala.util.parsing.combinator.syntactical.StdTokenParsers
import scala.util.parsing.combinator.lexical.StdLexical
import scala.util.parsing.input.StreamReader
import java.io.FileReader
object P extends StdTokenParsers {
type Tokens = StdLexical
val lexical = new StdLexical
lexical.delimiters += ","
def words : Parser[List[String]] = repsep(stringLit, ",")
def getWords(fileName : String) : List[String] = {
val scanner = new lexical.Scanner(StreamReader(new FileReader(fileName)))
// better error handling wouldn't hurt.
words(scanner).get
}
}
I did it using the java.util.Scanner while it does work, I'd appreciate a more scalaesc version.
val scanner = new Scanner(new File("""bigFile.txt""")).useDelimiter(",")
var wordList: Vector[String] = Vector()
while (scanner.hasNext()) {
val quoted = scanner.next()
val word = quoted.replace("\"", "")
wordList = wordList :+ word
}
I have the following code, which is supposed to write to a file one line at a time, until it reaches ^EOF.
import java.io.PrintWriter
import java.io.File
object WF {
def writeline(file: String)(delim: String): Unit = {
val writer = new PrintWriter(new File(file))
val line = Console.readLine(delim)
if (line != "^EOF") {
writer.write(line + "\n")
writer.flush()
}
else {
sys.exit()
}
}
}
var counter = 0
val filename = Console.readLine("Enter a file: ")
while (true) {
counter += 1
WF.writeline(filename)(counter.toString + ": ")
}
For some reason, at the console, everything looks like it works fine, but then, when I actually read the file, nothing has been written to it! What is wrong with my program?
Every time you create a new PrintWriter you're wiping out the existing file. Use something like a FileWriter, which allows you to specify that you want to open the file for appending:
val writer = new PrintWriter(new FileWriter(new File(file), true))
That should work, although the logic here is pretty confusing.
Use val writer = new FileWriter(new File(file), true) instead. The second parameter tells the FileWriter to append to the file. See http://docs.oracle.com/javase/6/docs/api/java/io/FileWriter.html
I'm guessing the problem is that you forgot to close the writer.