readChar from stdin - r

I would like to read in one character at a time and convert it to a string command to execute once a space is entered.
I tried
con <- file("stdin")
open(con, blocking=TRUE)
while(q!=" "){
#q=scan("",what="", nmax=1)
q=readChar(con,1)
cmd[i]=q;i=i+1
}
eval(cmd)
but I seem to not understand readChar() correctly.

Interesting question, but with a few flaws:
you use q, cmd and i and never assign them which leads to immediate error and program abort on undefined symbols being used in tests or assignments
you test for q at the top of your while loop but never give it a value (related to previous point)
you assign in the vector with i but never set to 1 first
you misunderstand how string concatenation works
you misunderstand how input from the console works, it is fundamentally by line and not by character so your approach has a deadly design issue (and blocking makes no difference)
you terminate the wrong way; your sentinel value of '' still gets assigned.
you open a console and never close it (though that may be ok with stdin)
you grow the result var the wrong way, but that doesn't matter here compared to all the other issues
If we repair the code a little, and use better style with whitespaces, no semicolons and an <- for assignment (all personal / common preferences) we get
con <- file("stdin")
open(con, blocking=TRUE)
cmd <- q <- ""
i <- 1
while (q != " ") {
q <- readChar(con,1)
cmd[i] <- q
i <- i+1
}
print(cmd) # for exposition only
close(con)
and note the final print rather than eval, we get this (and I typed the ls command letters followed by whitespace)
$ Rscript /tmp/marcusloecher.R
ls
[1] "l" "s" " "
$
I suggest you look into readLines() instead.
And now that I checked, I see that apparently you also never accept an answer to questions you asked. That is an ... interesting outcome for someone from our field which sometimes defines itself as being all about incentives...

Related

In R, Is there a way to break long string literals? [duplicate]

I want to split a line in an R script over multiple lines (because it is too long). How do I do that?
Specifically, I have a line such as
setwd('~/a/very/long/path/here/that/goes/beyond/80/characters/and/then/some/more')
Is it possible to split the long path over multiple lines? I tried
setwd('~/a/very/long/path/here/that/goes/beyond/80/characters/and/
then/some/more')
with return key at the end of the first line; but that does not work.
Thanks.
Bah, comments are too small. Anyway, #Dirk is very right.
R doesn't need to be told the code starts at the next line. It is smarter than Python ;-) and will just continue to read the next line whenever it considers the statement as "not finished". Actually, in your case it also went to the next line, but R takes the return as a character when it is placed between "".
Mind you, you'll have to make sure your code isn't finished. Compare
a <- 1 + 2
+ 3
with
a <- 1 + 2 +
3
So, when spreading code over multiple lines, you have to make sure that R knows something is coming, either by :
leaving a bracket open, or
ending the line with an operator
When we're talking strings, this still works but you need to be a bit careful. You can open the quotation marks and R will read on until you close it. But every character, including the newline, will be seen as part of the string :
x <- "This is a very
long string over two lines."
x
## [1] "This is a very\nlong string over two lines."
cat(x)
## This is a very
## long string over two lines.
That's the reason why in this case, your code didn't work: a path can't contain a newline character (\n). So that's also why you better use the solution with paste() or paste0() Dirk proposed.
You are not breaking code over multiple lines, but rather a single identifier. There is a difference.
For your issue, try
R> setwd(paste("~/a/very/long/path/here",
"/and/then/some/more",
"/and/then/some/more",
"/and/then/some/more", sep=""))
which also illustrates that it is perfectly fine to break code across multiple lines.
Dirk's method above will absolutely work, but if you're looking for a way to bring in a long string where whitespace/structure is important to preserve (example: a SQL query using RODBC) there is a two step solution.
1) Bring the text string in across multiple lines
long_string <- "this
is
a
long
string
with
whitespace"
2) R will introduce a bunch of \n characters. Strip those out with strwrap(), which destroys whitespace, per the documentation:
strwrap(long_string, width=10000, simplify=TRUE)
By telling strwrap to wrap your text to a very, very long row, you get a single character vector with no whitespace/newline characters.
For that particular case there is file.path :
File <- file.path("~",
"a",
"very",
"long",
"path",
"here",
"that",
"goes",
"beyond",
"80",
"characters",
"and",
"then",
"some",
"more")
setwd(File)
The glue::glue function can help. You can write a string on multiple lines in a script but remove the line breaks from the string object by ending each line with \\:
glue("some\\
thing")
something
I know this post is old, but I had a Situation like this and just want to share my solution. All the answers above work fine. But if you have a Code such as those in data.table chaining Syntax it becomes abit challenging. e.g. I had a Problem like this.
mass <- files[, Veg:=tstrsplit(files$file, "/")[1:4][[1]]][, Rain:=tstrsplit(files$file, "/")[1:4][[2]]][, Roughness:=tstrsplit(files$file, "/")[1:4][[3]]][, Geom:=tstrsplit(files$file, "/")[1:4][[4]]][time_[s]<=12000]
I tried most of the suggestions above and they didn´t work. but I figured out that they can be split after the comma within []. Splitting at ][ doesn´t work.
mass <- files[, Veg:=tstrsplit(files$file, "/")[1:4][[1]]][,
Rain:=tstrsplit(files$file, "/")[1:4][[2]]][,
Roughness:=tstrsplit(files$file, "/")[1:4][[3]]][,
Geom:=tstrsplit(files$file, "/")[1:4][[4]]][`time_[s]`<=12000]
There is no coinvent way to do this because there is no operator in R to do string concatenation.
However, you can define a R Infix operator to do string concatenation:
`%+%` = function(x,y) return(paste0(x,y))
Then you can use it to concat strings, even break the code to multiple lines:
s = "a" %+%
"b" %+%
"c"
This will give you "abc".
This will keep the \n character, but you can also just wrap the quote in parentheses. Especially useful in RMarkdown.
t <- ("
this is a long
string
")
I do this all of the time. Use the paste0() function.
Rootdir = "/myhome/thisproject/part1/"
Subdir = "subdirectory1/subsubdir2/"
fullpath = paste0( Rootdir, Subdir )
fullpath
> fullpath
[1] "/myhome/thisproject/part1/subdirectory1/subsubdir2/"

Comparing the MD5 sum of a string to the contents of a file

I am trying to compare a string (in memory) to the contents of a file to see if they are the same. Boring details on motivation are below the question if anyone cares.
My confusion is that when I hash file contents, I get a different result than when I hash the string.
library(readr)
library(digest)
# write the string to the file
the_string <- "here is some stuff"
the_file <- "fake.txt"
readr::write_lines(the_string, the_file)
# both of these functions (predictably) give the same hash
tools::md5sum(the_file)
# "44b0350ee9f822d10f2f9ca7dbe54398"
digest(file = the_file)
# "44b0350ee9f822d10f2f9ca7dbe54398"
# now read it back to a string and get something different
back_to_a_string <- readr::read_file(the_file)
# "here is some stuff\n"
digest(back_to_a_string)
# "03ed1c8a2b997277100399bef6f88939"
# add a newline because that's what write_lines did
orig_with_newline <- paste0(the_string, "\n")
# "here is some stuff\n"
digest(orig_with_newline)
# "03ed1c8a2b997277100399bef6f88939"
What I want to do is just digest(orig_with_newline) == digest(file = the_file) to see if they're the same (they are) but that returns FALSE because, as shown, the hashes are different.
Obviously I could either read the file back to a string with read_file or write the string to a temp file, but both of those seem a bit silly and hacky. I guess both of those are actually fine solutions, I really just want to understand why this is happening so that I can better understand how the hashing works.
Boring details on motivation
The situation is that I have a function that will write a string to a file, but if the file already exists then it will error unless the user has explicitly passed .overwrite = TRUE. However, if the file exists, I would like to check whether the string about to be written to the file is in fact the same thing that's already in the file. If this is the case, then I will skip the error (and the write). This code could be called in a loop and it will be obnoxious for the user to continually see this error that they are about to overwrite a file with the same thing that's already in it.
Short answer: I think you need to set serialize=FALSE. Supposing that the file doesn't contain the extra newline (see below),
digest(the_string,serialize=FALSE) == digest(file=the_file) ## TRUE
(serialize has no effect on the file= version of the command)
dealing with newlines
If you read ?write_lines, it only says
sep: The line separator ... [information about defaults for different OSes]
To me, this seems ambiguous as to whether the separator will be added after the last line or not. (You don't expect a "comma-separated list" to end with a comma ...)
On the other hand, ?base::writeLines is a little more explicit,
sep: character string. A string to be written to the connection
after each line of text.
If you dig down into the source code of readr you can see that it uses
output << na << sep;
for each line of code, i.e. it's behaving the same way as writeLines.
If you really just want to write the string to the file with no added nonsense, I suggest cat():
identical(the_string, { cat(the_string,file=the_file); readr::read_file(the_file) }) ## TRUE

How to store a special character in a string in R

I am trying to store the special escape character \ in R as part of a string.
x = "abcd\efg"
# Error: '\e' is an unrecognized escape in character string starting ""abcd\e"
x = "abcd\\efg"
# Works
The problem is that x is actually a password that I am passing as part of an API web call so I need to find a way for the string to store a literal single slash in order for this to work.
Example using the ignoring literal command:
> x = r"(abcd\efg)"
> y = "https://url?password="
> paste(x, y, sep = "")
[1] "abcd\\efg123"
> # What I need is for it to return
[1] "abcd\efg123"
> z = paste(y, x, sep = "")
> z
[1] "https://url?password=abcd\\efg"
> cat(z)
https://url?password=abcd\efg
When I pass z as part of the API command I get "bad credentials" message because it's sending \\ as part of the string. cat returns it to the console correctly, but it appears to not be sending it the same way it's printing it back.
Like everyone else in the comments said, the string appears to be handled correctly by R. I would assume it becomes a trouble somewhere else down the line. Either the API or the pass-matching software itself might be doing something with it.
As one example, the backslash will not work in most browsers if written verbatim, you would need to use %5C instead. Here is one discussion about it.
So in your case - try replacing the backslash with something else, either %5C or, as #Roland mentioned in the comments, some extra back-slash symbols, like \\\\ or \\\ or \\. And then see if any of them work.
The answers and comments contain the solution, but for completeness ill post an answer that matches my specific situation.
My package expects the user to supply a password. If there is a \ in the password the command will fail as stated above. The solution seems to be to take care of it on the input rather than trying to alter the value once the user submits the password.
library(RobinHood)
# If the true password is: abc\def
# The following inputs will all work
rh <- RobinHood("username", pwd = r"(abc\def)")
rh <- RobinHood("username", pwd = "abc\\def")
rh <- RobinHood("username", pwd = "abc%5Cdef")

R split call to funciton over several lines

I wonder if there is a way to split a call to a function in R over several lines, other then using commas or '+' which is not always applicable.
I am basically looking like Python's '\' escape.
For example, I want to display this line:
PromoterIslands$illumina_probes[bins_with_probes]-tapply(CGIP_to_Probe$subjectHits,CGIP_to_Probe$subjectHits,function(x) length(x))
as:
PromoterIslands$illumina_probes[bins_with_probes]
<-tapply(CGIP_to_Probe$subjectHits,CGIP_to_Probe$subjectHits,
function(x) length(x))]
Is there any way to do this?
Thanks in advance
You can just split commands on various lines, no special sign needed.
The command you wrote is almost OK, you just need to put the <- operator on the first line.
So for instance, this is valid R code, and will assign 13 to a
a <-
5 +
8
But this is not
a
<- 5 +
8
Note, however, that this is valid code
a <-
5
+ 8
But would assign 5 to a and print 8.
Assuming you're talking about source (as opposed to desiring to get something to print a particular way), you can break R code in any place that doesn't produce a syntactically complete statement. There is no special character that tells R that the statement isn't complete. In your case, one option is:
PromoterIslands$illumina_probes[bins_with_probes] <-
tapply(
CGIP_to_Probe$subjectHits,
CGIP_to_Probe$subjectHits,
function(x) length(x)
)
You have to leave the <- at the end of the first line, otherwise PromoterIslands$illumina_probes[bins_with_probes] is a syntactically complete statement that would get evaluated. Similarly, on the next line, you have to leave the ( on the same line as tapply otherwise tapply is a syntactically complete statement (returns the contents of the tapply function).
While this doesn't quite answer your question, hopefully you will find there are enough places you can break a line in R that the lack of the special command you are looking for isn't a problem.

Split code over multiple lines in an R script

I want to split a line in an R script over multiple lines (because it is too long). How do I do that?
Specifically, I have a line such as
setwd('~/a/very/long/path/here/that/goes/beyond/80/characters/and/then/some/more')
Is it possible to split the long path over multiple lines? I tried
setwd('~/a/very/long/path/here/that/goes/beyond/80/characters/and/
then/some/more')
with return key at the end of the first line; but that does not work.
Thanks.
Bah, comments are too small. Anyway, #Dirk is very right.
R doesn't need to be told the code starts at the next line. It is smarter than Python ;-) and will just continue to read the next line whenever it considers the statement as "not finished". Actually, in your case it also went to the next line, but R takes the return as a character when it is placed between "".
Mind you, you'll have to make sure your code isn't finished. Compare
a <- 1 + 2
+ 3
with
a <- 1 + 2 +
3
So, when spreading code over multiple lines, you have to make sure that R knows something is coming, either by :
leaving a bracket open, or
ending the line with an operator
When we're talking strings, this still works but you need to be a bit careful. You can open the quotation marks and R will read on until you close it. But every character, including the newline, will be seen as part of the string :
x <- "This is a very
long string over two lines."
x
## [1] "This is a very\nlong string over two lines."
cat(x)
## This is a very
## long string over two lines.
That's the reason why in this case, your code didn't work: a path can't contain a newline character (\n). So that's also why you better use the solution with paste() or paste0() Dirk proposed.
You are not breaking code over multiple lines, but rather a single identifier. There is a difference.
For your issue, try
R> setwd(paste("~/a/very/long/path/here",
"/and/then/some/more",
"/and/then/some/more",
"/and/then/some/more", sep=""))
which also illustrates that it is perfectly fine to break code across multiple lines.
Dirk's method above will absolutely work, but if you're looking for a way to bring in a long string where whitespace/structure is important to preserve (example: a SQL query using RODBC) there is a two step solution.
1) Bring the text string in across multiple lines
long_string <- "this
is
a
long
string
with
whitespace"
2) R will introduce a bunch of \n characters. Strip those out with strwrap(), which destroys whitespace, per the documentation:
strwrap(long_string, width=10000, simplify=TRUE)
By telling strwrap to wrap your text to a very, very long row, you get a single character vector with no whitespace/newline characters.
For that particular case there is file.path :
File <- file.path("~",
"a",
"very",
"long",
"path",
"here",
"that",
"goes",
"beyond",
"80",
"characters",
"and",
"then",
"some",
"more")
setwd(File)
The glue::glue function can help. You can write a string on multiple lines in a script but remove the line breaks from the string object by ending each line with \\:
glue("some\\
thing")
something
I know this post is old, but I had a Situation like this and just want to share my solution. All the answers above work fine. But if you have a Code such as those in data.table chaining Syntax it becomes abit challenging. e.g. I had a Problem like this.
mass <- files[, Veg:=tstrsplit(files$file, "/")[1:4][[1]]][, Rain:=tstrsplit(files$file, "/")[1:4][[2]]][, Roughness:=tstrsplit(files$file, "/")[1:4][[3]]][, Geom:=tstrsplit(files$file, "/")[1:4][[4]]][time_[s]<=12000]
I tried most of the suggestions above and they didn´t work. but I figured out that they can be split after the comma within []. Splitting at ][ doesn´t work.
mass <- files[, Veg:=tstrsplit(files$file, "/")[1:4][[1]]][,
Rain:=tstrsplit(files$file, "/")[1:4][[2]]][,
Roughness:=tstrsplit(files$file, "/")[1:4][[3]]][,
Geom:=tstrsplit(files$file, "/")[1:4][[4]]][`time_[s]`<=12000]
There is no coinvent way to do this because there is no operator in R to do string concatenation.
However, you can define a R Infix operator to do string concatenation:
`%+%` = function(x,y) return(paste0(x,y))
Then you can use it to concat strings, even break the code to multiple lines:
s = "a" %+%
"b" %+%
"c"
This will give you "abc".
This will keep the \n character, but you can also just wrap the quote in parentheses. Especially useful in RMarkdown.
t <- ("
this is a long
string
")
I do this all of the time. Use the paste0() function.
Rootdir = "/myhome/thisproject/part1/"
Subdir = "subdirectory1/subsubdir2/"
fullpath = paste0( Rootdir, Subdir )
fullpath
> fullpath
[1] "/myhome/thisproject/part1/subdirectory1/subsubdir2/"

Resources