Repository: pandas-dev/pandas
Pull request: To string with encoding
- [x] close #28766
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Problem statement:
to_string() does not have an "encoding" parameter.
#### Code Sample, a copy-pastable example if possible

```python
df = pd.DataFrame(data = {'Japanese' : 'こんにちは', 'English' : 'Hello'}, index = [0])
df.to_string()

```

#### Output of ``pd.show_versions()``

<details>

[paste the output of ``pd.show_versions()`` here below this line]
INSTALLED VERSIONS
------------------
commit           : None
python           : 3.7.1.final.0
python-bits      : 64
OS               : Windows
OS-release       : 10
machine          : AMD64
processor        : Intel64 Family 6 Model 78 Stepping 3, GenuineIntel
byteorder        : little
LC_ALL           : None
LANG             : None
LOCALE           : None.None

pandas           : 0.25.0
numpy            : 1.16.2
pytz             : 2018.7
dateutil         : 2.7.5
pip              : 18.1
setuptools       : 40.6.3
Cython           : 0.29.2
pytest           : 4.0.2
hypothesis       : None
sphinx           : 1.8.2
blosc            : None
feather          : 0.4.0
xlsxwriter       : 1.1.2
lxml.etree       : 4.2.5
html5lib         : 1.0.1
pymysql          : None
psycopg2         : 2.8.3 (dt dec pq3 ext lo64)
jinja2           : 2.10
IPython          : 7.2.0
pandas_datareader: None
bs4              : 4.6.3
bottleneck       : 1.2.1
fastparquet      : None
gcsfs            : None
lxml.etree       : 4.2.5
matplotlib       : 3.0.2
numexpr          : 2.6.8
odfpy            : None
openpyxl         : 2.5.12
pandas_gbq       : None
pyarrow          : 0.13.0
pytables         : None
s3fs             : None
scipy            : 1.1.0
sqlalchemy       : 1.2.15
tables           : 3.4.4
xarray           : None
xlrd             : 1.2.0
xlwt             : 1.3.0
xlsxwriter       : 1.1.2

</details>

Hints from issue discussion:
Can you give an example of the expected behavior and API parameters?
Just like to_csv() and to_excel() have an "encoding" parameters, we can have an encoding parameter in string for consistency. 

```python
df.to_string(encoding = "iso-8859-1")
```
Might not be thinking straight today but wouldn't this only make sense if it were a `to_bytes` method? Not applicable for `to_string`, no?
Agreed with Will. An encoding is only necessary when going between strings
and bytes. But we're converting from a Python object to a string.

On Thu, Oct 3, 2019 at 4:43 PM William Ayd <notifications@github.com> wrote:

> Might not be thinking straight today but wouldn't this only make sense if
> it were a to_bytes method? Not applicable for to_string, no?
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/pandas-dev/pandas/issues/28766?email_source=notifications&email_token=AAKAOITGN7FSSWSDBQQ27R3QMZRRPA5CNFSM4I46HERKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAJWDTQ#issuecomment-538141134>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAKAOIVF6V2DFWHXZPJ7MYLQMZRRPANCNFSM4I46HERA>
> .
>

https://github.com/pandas-dev/pandas/issues/28663#issuecomment-536322605

>the to_html method has no encoding. I assume that it is fairly simple to work-around by passing an open buffer with the correct encoding or not passing a filepath, and writing the return unicode string to file using the appropriate encoding?

>the buffer handling code is shared with to_latex and to_string. to_latex already has an encoding parameter and the shared code can handle encoding. so it should be straightforward to add encoding to to_html ( and to_string) for consistency.
#15008 (comment) "Textual payloads should support the encoding argument"
Forgot about the `buf` argument accepting a string path.

In that case, accepting an `encoding` makes sense, for specifying the encoding for the newly created file. We should raise when encoding is specified and it's `buf` isn't a string path.

Commit message: Added whatsnew for to_string with encoding param.

Patch to review:
diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst
index 1112e42489342..4bab8582f26ee 100644
--- a/doc/source/whatsnew/v1.0.0.rst
+++ b/doc/source/whatsnew/v1.0.0.rst
@@ -317,6 +317,7 @@ I/O
 - Bug in :func:`read_hdf` closing stores that it didn't open when Exceptions are raised (:issue:`28699`)
 - Bug in :meth:`DataFrame.read_json` where using ``orient="index"`` would not maintain the order (:issue:`28557`)
 - Bug in :meth:`DataFrame.to_html` where the length of the ``formatters`` argument was not verified (:issue:`28469`)
+- Added ``encoding`` argument to :func:`DataFrame.to_string` for non-ascii text (:issue:`28766`)
 
 Plotting
 ^^^^^^^^
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
index 5200ad0ba0d23..a02f0aa7d4437 100644
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -744,6 +744,7 @@ def to_string(
         decimal: str = ".",
         line_width: Optional[int] = None,
         max_colwidth: Optional[int] = None,
+        encoding=None,
     ) -> Optional[str]:
         """
         Render a DataFrame to a console-friendly tabular output.
@@ -754,6 +755,10 @@ def to_string(
             Max width to truncate each column in characters. By default, no limit.
 
             .. versionadded:: 1.0.0
+        encoding : str, default "utf-8"
+            Set character encoding
+
+            .. versionadded:: 1.0
         %(returns)s
         See Also
         --------
@@ -792,7 +797,7 @@ def to_string(
                 decimal=decimal,
                 line_width=line_width,
             )
-            return formatter.to_string(buf=buf)
+            return formatter.to_string(buf=buf, encoding=encoding)
 
     # ----------------------------------------------------------------------
 
diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py
index ad62c56a337b6..06a4c1f1425c8 100644
--- a/pandas/io/formats/format.py
+++ b/pandas/io/formats/format.py
@@ -895,8 +895,12 @@ def _join_multiline(self, *args) -> str:
             st = ed
         return "\n\n".join(str_lst)
 
-    def to_string(self, buf: Optional[FilePathOrBuffer[str]] = None) -> Optional[str]:
-        return self.get_result(buf=buf)
+    def to_string(
+        self,
+        buf: Optional[FilePathOrBuffer[str]] = None,
+        encoding: Optional[str] = None,
+    ) -> Optional[str]:
+        return self.get_result(buf=buf, encoding=encoding)
 
     def to_latex(
         self,
diff --git a/pandas/tests/io/formats/test_to_string.py b/pandas/tests/io/formats/test_to_string.py
new file mode 100644
index 0000000000000..19bf53e3c8cf8
--- /dev/null
+++ b/pandas/tests/io/formats/test_to_string.py
@@ -0,0 +1,6 @@
+def test_to_string_encoding(float_frame,):
+    # GH 28766
+    path = "test_to_string_file"
+    float_frame.to_string(path, encoding="gbk")
+    with open(str(path), "r", encoding="gbk") as f:
+        assert float_frame.to_string() == f.read()


Code review comments:
diff_hunk: @@ -317,6 +317,7 @@ I/O
 - Bug in :func:`read_hdf` closing stores that it didn't open when Exceptions are raised (:issue:`28699`)
 - Bug in :meth:`DataFrame.read_json` where using ``orient="index"`` would not maintain the order (:issue:`28557`)
 - Bug in :meth:`DataFrame.to_html` where the length of the ``formatters`` argument was not verified (:issue:`28469`)
+- Added ``encoding`` argument to :func:`DataFrame.to_string` for non-ascii text (:issue:`28766`)
path: doc/source/whatsnew/v1.0.0.rst
text: @user1:
can you move to Other enhancements section

@author:
Sure @user1 . I'll make the necessary changes.

@user1:
can you move to Other enhancements section (starts on L99)

diff_hunk: @@ -317,6 +317,7 @@ I/O
 - Bug in :func:`read_hdf` closing stores that it didn't open when Exceptions are raised (:issue:`28699`)
 - Bug in :meth:`DataFrame.read_json` where using ``orient="index"`` would not maintain the order (:issue:`28557`)
 - Bug in :meth:`DataFrame.to_html` where the length of the ``formatters`` argument was not verified (:issue:`28469`)
+- Added ``encoding`` argument to :func:`DataFrame.to_string` for non-ascii text (:issue:`28766`)
path: doc/source/whatsnew/v1.0.0.rst
text: @user1:
```suggestion
- Added ``encoding`` argument to :meth:`DataFrame.to_string` for non-ascii text (:issue:`28766`)
```