The correct answer is:
p.communicate(b"insert into egg values ('egg')")
Note the leading b, telling you that it's a string of bytes, not a string of unicode characters. Also, if you are reading this from a file:
value = open('thefile', 'rt').read()
p.communicate(value)
The change that to:
value = open('thefile', 'rb').read()
p.communicate(value)
Again, note the 'b'.
Now if your value is a string you get from an API that only returns strings no matter what, then you need to encode it.
p.communicate(value.encode('latin-1'))
Latin-1, because unlike ASCII it supports all 256 bytes. But that said, having binary data in unicode is asking for trouble. It's better if you can make it binary from the start.
Answer from Lennart Regebro on Stack OverflowThe correct answer is:
p.communicate(b"insert into egg values ('egg')")
Note the leading b, telling you that it's a string of bytes, not a string of unicode characters. Also, if you are reading this from a file:
value = open('thefile', 'rt').read()
p.communicate(value)
The change that to:
value = open('thefile', 'rb').read()
p.communicate(value)
Again, note the 'b'.
Now if your value is a string you get from an API that only returns strings no matter what, then you need to encode it.
p.communicate(value.encode('latin-1'))
Latin-1, because unlike ASCII it supports all 256 bytes. But that said, having binary data in unicode is asking for trouble. It's better if you can make it binary from the start.
You can convert it to bytes with encode method:
>>> "insert into egg values ('egg');".encode('ascii') # ascii is just an example
b"insert into egg values ('egg');"
node.js - Nodejs InputStream Buffer convert to string using python - Stack Overflow
[Python] Is there an inherent difference with a ctypes string buffer that makes it a bad idea to use to store byte data?
String buffer to be filled by native side
vim : how to set current buffer to a string using `:python` - Vi and Vim Stack Exchange
(as opposed to doing something similar natively in C)
Running into an issue where a function in a DLL expects an unsigned char * as a buffer to byte data (specifically this function reads one byte from a device and stores that byte in the passed in data buffer). So I do ctypes.create_string_buffer(size) and pass that in as the data arg to this DLL function. This works sometimes, but I just spent some time debugging why it doesn't work at times, and it seems to be because when the data being read and set has a value of 0, this causes some weird behavior where this string buffer (which I know is actually a ctypes array of c_chars, but string buffer is more concise) then treats this value as a null character, and therefore goes wacky, specifically when I try to access that byte via `data.value[0]` (this causes an index out of range error). If the byte being read and set is any other value, it seems to work fine and 0 is a valid index into this string buffer.
I don't have a full 100% grasp on what's going on here, but it *seems* like there's just something under the hood with how these string buffers are used. I think in C these issues don't exist because if you're using a buffer of chars to store byte data rather than characters, then you won't ever really parse the bytes as a string and therefore the value of 0 anywhere in the buffer won't cause weird issues.
But I guess in ctypes/python it's different? Just wanted to get other opinions here to see if my current understanding is correct or at least headed in the right direction.
Let me know if anything isn't clear!
In vimscript, just as an alternative:
:1,$-1substitute/$/:
:global/^/join
In one line with abbreviated commands:
:1,$-1s/$/:/ | g/^/j
As @Mass's answer said, to set buffer content, you cannot assign to the buffer
object itself, you need to assign to the the sliced version, eg vim.current.buffer[:].
Currently what's missing from there is, you need to use a list of string instead of a string to assign to a sliced buffer object.
To summary
# set first line to a string
py3 vim.current.buffer[0] = "Hello world"
# replace whole buffer with a string
py3 vim.current.buffer[:] = ["a long long string"]
# replace whole buffer with two lines
py3 vim.current.buffer[:] = ["first line", "second line"]
# replace first 10 line with two lines
py3 vim.current.buffer[:10] = ["first line", "second line"]
In my day job, I wrote the software for an embedded system and a PC communicating with each other by a USB cable, using the UART protocol at 115,200 baud.
I see that you tagged your post with PySerial, so you already know about Python's most popular package for serial port communication. I will add that if you are using PyQt, there's a serial module included in that package as well.
115,200 baud is not fast for a modern desktop PC. I doubt that any parsing you do on the PC side will fail to keep up. I parse data streams and plot graphs of my data in real time using PyQt.
What I have noticed in my work with communication between an embedded system and a PC over a UART is that some data gets corrupted occasionally. A byte can be garbled, repeated, or dropped. Also, even if no bytes are added or dropped, you can occasionally perform a read while only part of a packet is in the buffer, and the read will terminate early. If you use a fixed read length of 40 bytes and trust that each read will always line up exactly with a data packet as you show above, you will frequently be wrong.
To solve these kinds of problems, I wrote a FIFO class in Python which consumes serial port data at the head of the FIFO, yields valid data packets at the tail, and discards invalid data. My FIFO holds 3 times as many bytes as my data packets, so if I am looking for packet boundaries using specific sequences, I have plenty of signposts.
A few more recommendations: work in Python 3 if you have the choice, it's cleaner. Use bytes and bytearray objects. Don't use str, because you will find yourself converting back and forth between Unicode and ASCII.
This format is almost parseable as a csv, but not quite, because the fourth field is JSON, and you may not be able to guarantee that the JSON doesn't contain any strings with embedded semicolons. So, I think you probably want to just use string (or, rather, bytes) manipulation functions:
def parsemsg(buf):
s, cmd, length, rest = buf.split(b';', 3)
j, _, e = rest.rpartition(b';')
if s != b'S' or e != b'E':
raise ValueError('must start with S and end with E')
return cmd.decode('utf-8'), int(length), json.loads(j)
Then:
>>> parsemsg(b'S,SEND,40,{"ID":"asg01","T":1,"P":{"T":180}},E')
('SEND', 40, {'ID': 'asg01', 'T': 1, 'P': {'T': 180}})
The actual semicolon-parsing part takes 602ns on my laptop, The decode and int raise that to 902ns. The json.loads, on the other hand, takes 10us. So, if you're worried about performance, the JSON part is really the only part that matters (trying third-party JSON libs I happen to have installed, the fastest one is still 8.1us, which isn't much better). You might as well keep everything else simple and robust.
Also, considering that you're reading this at 115000 baud, you can't get these messages any faster than about 6ms, so spending 11us parsing them is not even close to a problem in the first place.
Python 3
From the docs:
Concatenating immutable sequences always results in a new object. This means that building up a sequence by repeated concatenation will have a quadratic runtime cost in the total sequence length. To get a linear runtime cost, you must switch to one of the alternatives below: if concatenating str objects, you can build a list and use str.join() at the end or else write to an io.StringIO instance and retrieve its value when complete
Experiment to compare runtime of several options:
import sys
import timeit
from io import StringIO
from array import array
def test_concat():
out_str = ''
for _ in range(loop_count):
out_str += 'abc'
return out_str
def test_join_list_loop():
str_list = []
for _ in range(loop_count):
str_list.append('abc')
return ''.join(str_list)
def test_array():
char_array = array('b')
for _ in range(loop_count):
char_array.frombytes(b'abc')
return str(char_array.tostring())
def test_string_io():
file_str = StringIO()
for _ in range(loop_count):
file_str.write('abc')
return file_str.getvalue()
def test_join_list_compr():
return ''.join(['abc' for _ in range(loop_count)])
def test_join_gen_compr():
return ''.join('abc' for _ in range(loop_count))
loop_count = 80000
print(sys.version)
res = {}
for k, v in dict(globals()).items():
if k.startswith('test_'):
res[k] = timeit.timeit(v, number=10)
for k, v in sorted(res.items(), key=lambda x: x[1]):
print('{:.5f} {}'.format(v, k))
results
3.7.5 (default, Nov 1 2019, 02:16:32)
[Clang 11.0.0 (clang-1100.0.33.8)]
0.03738 test_join_list_compr
0.05681 test_join_gen_compr
0.09425 test_string_io
0.09636 test_join_list_loop
0.11976 test_concat
0.19267 test_array
Python 2
Efficient String Concatenation in Python is a rather old article and its main statement that the naive concatenation is far slower than joining is not valid anymore, because this part has been optimized in CPython since then. From the docs:
CPython implementation detail: If s and t are both strings, some Python implementations such as CPython can usually perform an in-place optimization for assignments of the form s = s + t or s += t. When applicable, this optimization makes quadratic run-time much less likely. This optimization is both version and implementation dependent. For performance sensitive code, it is preferable to use the str.join() method which assures consistent linear concatenation performance across versions and implementations.
I've adapted their code a bit and got the following results on my machine:
from cStringIO import StringIO
from UserString import MutableString
from array import array
import sys, timeit
def method1():
out_str = ''
for num in xrange(loop_count):
out_str += `num`
return out_str
def method2():
out_str = MutableString()
for num in xrange(loop_count):
out_str += `num`
return out_str
def method3():
char_array = array('c')
for num in xrange(loop_count):
char_array.fromstring(`num`)
return char_array.tostring()
def method4():
str_list = []
for num in xrange(loop_count):
str_list.append(`num`)
out_str = ''.join(str_list)
return out_str
def method5():
file_str = StringIO()
for num in xrange(loop_count):
file_str.write(`num`)
out_str = file_str.getvalue()
return out_str
def method6():
out_str = ''.join([`num` for num in xrange(loop_count)])
return out_str
def method7():
out_str = ''.join(`num` for num in xrange(loop_count))
return out_str
loop_count = 80000
print sys.version
print 'method1=', timeit.timeit(method1, number=10)
print 'method2=', timeit.timeit(method2, number=10)
print 'method3=', timeit.timeit(method3, number=10)
print 'method4=', timeit.timeit(method4, number=10)
print 'method5=', timeit.timeit(method5, number=10)
print 'method6=', timeit.timeit(method6, number=10)
print 'method7=', timeit.timeit(method7, number=10)
Results:
2.7.1 (r271:86832, Jul 31 2011, 19:30:53)
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)]
method1= 0.171155929565
method2= 16.7158739567
method3= 0.420584917068
method4= 0.231794118881
method5= 0.323612928391
method6= 0.120429992676
method7= 0.145267963409
Conclusions:
joinstill wins over concat, but marginally- list comprehensions are faster than loops (when building a list)
- joining generators is slower than joining lists
- other methods are of no use (unless you're doing something special)
Depends on what you want to do. If you want a mutable sequence, the builtin list type is your friend, and going from str to list and back is as simple as:
mystring = "abcdef"
mylist = list(mystring)
mystring = "".join(mylist)
If you want to build a large string using a for loop, the pythonic way is usually to build a list of strings then join them together with the proper separator (linebreak or whatever).
Else you can also use some text template system, or a parser or whatever specialized tool is the most appropriate for the job.
Decode the bytes object to produce a string:
>>> b"abcde".decode("utf-8")
'abcde'
The above example assumes that the bytes object is in UTF-8, because it is a common encoding. However, you should use the encoding your data is actually in!
Decode the byte string and turn it in to a character (Unicode) string.
Python 3:
encoding = 'utf-8'
b'hello'.decode(encoding)
or
str(b'hello', encoding)
Python 2:
encoding = 'utf-8'
'hello'.decode(encoding)
or
unicode('hello', encoding)