How to Manipulate each character in of a string using a for loop in Julia ?

1,340 views
Skip to first unread message

Rishabh Raghunath

unread,
Aug 17, 2016, 2:30:25 AM8/17/16
to julia-users

Hello fellow Julia Users!!

How do you manipulate the individual characters comprising a string in Julia using a for loop ?
For example:
###################

a = "abcd"

  for i in length(a)
   a[i]+=1
 end

print(a)

####################
 I am expecting to get my EXPECTED OUTPUT as        "     bcde      "

 BUT I get the following error:  
##########################################

 ERROR: MethodError: `setindex!` has no method matching setindex!(::ASCIIString, ::Char, ::Int64)
 [inlined code] from ./none:2
 in anonymous at ./no file:4294967295

##########################################
I also tried using:

for i in eachindex(a) instead of the for loop in the above program .. And I get the same error..

Please tell me what i should do to get my desired output ..
Please respond ASAP..
Thanks..

Jacob Quinn

unread,
Aug 17, 2016, 2:40:36 AM8/17/16
to julia...@googlegroups.com
Strings are immutable (similar to other languages). There are several different ways to get what you want, but I tend to utilize IOBuffer a lot:

a = "abcd"
io = IOBuffer()

for char in a
    write(io, a + 1)
end

println(takebuf_string(io))

-Jacob

Rishabh Raghunath

unread,
Aug 17, 2016, 2:54:17 AM8/17/16
to julia-users
Hello Jacob !!..
I get this error while trying to run your code:

Error evaluating cyclic.jl
LoadError: MethodError: `+` has no method matching +(::ASCIIString, ::Int64)
Closest candidates are:
  +(::Any, ::Any, !Matched::Any, !Matched::Any...)
  +(!Matched::Int64, ::Int64)
  +(!Matched::Complex{Bool}, ::Real)
  ...
 [inlined code] from /home/rishabh/CodeVita/ProjectJulia/cyclic.jl:6
 in anonymous at ./no file:4294967295
while loading /home/rishabh/CodeVita/ProjectJulia/cyclic.jl, in expression starting on line 5

Greg Plowman

unread,
Aug 17, 2016, 3:59:23 AM8/17/16
to julia-users
I think Jacob meant:

for char in a
   write(io, char + 1)
end

Erik Schnetter

unread,
Aug 17, 2016, 12:49:37 PM8/17/16
to julia...@googlegroups.com
Alternatively, convert the string to an array of characters, which is mutable:

```Julia
a = Vector{Char}("abcd")
for i in 1:length(a)
   a[i] += 1
end
```

-erik

ggggg

unread,
Aug 17, 2016, 1:18:58 PM8/17/16
to julia-users, quinn....@gmail.com
Isn't it a red herring to say that strings lack setindex because they are immutable? I think strings don't have setindex! because it is considered to be a bad API choice, at least partially because you can't do O(1) indexing for many string encodings.

I can easily define a setindex! method* that does what the OP is expecting, so how can this be related to strings being immutable? 

julia> Base.setindex!(s::ASCIIString, v,i) = s.data[i]=v

setindex! (generic function with 58 methods)

julia> a="abc"

"abc"

julia> a[1]='7';a

"7bc"

julia> a[1]+=1;a

"8bc"


*This works in 0.4, and I'm lead to believe this is considered a bad idea.

Stefan Karpinski

unread,
Aug 17, 2016, 1:46:33 PM8/17/16
to Julia Users, Jacob Quinn
Indeed. Don't do this – your code will definitely break in the future if you do this.

Rishabh Raghunath

unread,
Aug 17, 2016, 1:52:47 PM8/17/16
to julia...@googlegroups.com
Thank you for the immense support from this amazing group !!.. I am a beginner in Julia and I am more familiar with the C language.. Now.. I understand strings are immutable..
I am actually learning Julia for use in a programming contest and love the experience so far..
There are often questions that require you to manipulate the characters comprising the string. Its easy to do in C as strings are nothing but an array of Characters and you can play around with it.. C is the online language I know well.. So I do not know about other languages..
But How do I such manipulation with the individual characters of a string while still maintaining the type as a string.. You said its considered a bad Idea to take this route...
Please tell me how I should go about manipulating characters in Julia the right way while the type is string..?
For Example: Writing a Encrypting program requires me to modify the characters in the string..
I tried using Character Array but .. It prints out as separate characters instead of a string

Bill Hart

unread,
Aug 17, 2016, 2:26:34 PM8/17/16
to julia-users
Since strings are immutable in Julia, and immutable means you can't change them, you can't change the characters in a string whilst keeping them of type string.

If you want to be able to change the characters directly, you need to use something other than a string, e.g. an array of characters as explained above.

Here is an example using byte arrays:

a = b"abcd"

for i in 1:length(a) # <--- note I corrected an error in your original code
    a[i] += 1
end

println(String(a))

Technically, the right way to deal with immutable objects is to just create a new one every time you want to change something. The compiler can then recognise that all you are doing is changing a string and it can decide to actually do the mutation behind the scenes automatically. On the other hand, I wouldn't necessarily expect this to be the case with Julia currently. 

As an example:

a = "abcd"

for i in 1:length(a)
   a ="$(a[1:i-1])$(a[i] + 1)$(a[i + 1:end])"
end

println(a)

Here I've used "string interpolation" and "ranges" to generate a new string from parts of the existing one.

I'm not suggesting this is a good way of doing it. I'm just demonstrating that this is a "functional" way of doing things, using immutables instead of the mutable strings we are used to in C.

Bill.

Steven G. Johnson

unread,
Aug 17, 2016, 2:43:02 PM8/17/16
to julia-users


On Wednesday, August 17, 2016 at 1:52:47 PM UTC-4, Rishabh Raghunath wrote:
For Example: Writing a Encrypting program requires me to modify the characters in the string..

This is a pretty bad example, because encryption almost always (a) doesn't work in-place and (b) only looks at raw bytes (ignoring whether they are "characters").

Similarly lot of the very low-level C-like operations that you might want to do actually involve raw bytes, and so you are better off working with a byte array, which you can obtain by Vector{UInt8}(some string) and can subsequently convert to a string via String(some byte array).

If you are creating a string incrementally, you can do that (roughly) in-place with an IOBuffer() as Jacob mentioned.

A lot of things that you might think are do-able in-place, like converting a string to upper-case, are actually not possible to do in-place if you have non-ASCII data, because strings are typically stored in a variable-length encoding (UTF-8 is the default in Julia).

 

jonatha...@alumni.epfl.ch

unread,
Aug 17, 2016, 3:45:44 PM8/17/16
to julia-users
Note also that using a range to loop through a string is not a good idea if it might contain unicode characters.

s = "a°sd2β2"
for i=1:length(s)
    print(s[i])  
end

LoadError: UnicodeError: invalid character index

Last time I checked there was still some functions in Base like findlast that had such issue.

The correct way to do it is to use start/done/next. That said it can become a bit tedious, when you have to reverse the string order first or so.
I found that using a vector of characters is sometimes an easy solution.


Steven G. Johnson

unread,
Aug 18, 2016, 8:14:11 AM8/18/16
to julia-users


On Wednesday, August 17, 2016 at 3:45:44 PM UTC-4, jonatha...@alumni.epfl.ch wrote:
Note also that using a range to loop through a string is not a good idea if it might contain unicode characters.

s = "a°sd2β2"
for i=1:length(s)

"for i in eachindex(s)" works, and in general eachindex is the recommended way to do iteration over the indices of a collection.

Steven G. Johnson

unread,
Aug 18, 2016, 8:15:13 AM8/18/16
to julia-users
(You can also do "for c in s" to iterate over the characters; you rarely need to call start/next/done manually.)

Steven G. Johnson

unread,
Aug 18, 2016, 8:19:33 AM8/18/16
to julia-users


On Wednesday, August 17, 2016 at 3:45:44 PM UTC-4, jonatha...@alumni.epfl.ch wrote:
Last time I checked there was still some functions in Base like findlast that had such issue.


(In general, please feel free to file issues when you encounter this sort of thing.)

Steven G. Johnson

unread,
Aug 18, 2016, 8:25:28 AM8/18/16
to julia-users
There are also the nextind and prevind functions for explicit index arithmetic on strings.

Páll Haraldsson

unread,
Aug 18, 2016, 8:35:22 AM8/18/16
to julia-users
On Wednesday, August 17, 2016 at 5:52:47 PM UTC, Rishabh Raghunath wrote:
I am a beginner in Julia and I am more familiar with the C language..
 
I am actually learning Julia for use in a programming contest and love the experience so far..
There are often questions that require you to manipulate the characters comprising the string. Its easy to do in C as strings are nothing but an array of Characters

A lot is not as easy as you might think :) I haven't looked up C recently, but at least C++14 has now 4 char literals (and 5 string), and C++17 added the 5th "utf8" literal that is bizzare as it only supports ASCII.. Things used to be simple with fixed-sized length ASCII and EBCDIC, with the latter still holding back C++ (or you might be optimisitc and say it makes C++ more portable..). Unicode, and e.g. grapheme clusters, made strings of letters ("Char"s, not just meaning bytes) a can-of-worm..

Having said that.. I was thinking up a new mutable string type.. (seems Julia developers are too with at least similar ideas).

--
Palli.
Reply all
Reply to author
Forward
0 new messages