代码之家  ›  专栏  ›  技术社区  ›  CurtLH

定义分隔符的Python click参数导致CSV错误“delimiter”必须是1个字符的字符串

  •  1
  • CurtLH  · 技术社区  · 7 年前

    我想建立一个简单的 click 命令行应用程序,用一种分隔符读入一个文件,用另一种分隔符写出同一文件。我不想做像find和replace这样的事情,因为在列中可能有一些正确转义的分隔符,我不想触碰它们。

    为此,我编写了一个简单的基于单击的CLI,但是在传入 \t 创建制表符分隔的文件。

    如下面的错误所示,制表符分隔符没有正确地传递到pandas函数中以写出新文件。当我打印出CLI中间的分隔符时,一切看起来都很正常,所以我不确定这里发生了什么。

    import click
    import pandas as pd
    
    @click.command()
    @click.argument('filename')
    @click.argument('in_delimiter')
    @click.argument('out_delimiter')
    def cli(filename, in_delimiter, out_delimiter):
    
        """
        Command line interface to change file delimiters
        """
    
        # read in CSV file
        df = pd.read_csv(filename, sep=in_delimiter)
        print(len(df))
    
        # write out CSV file
        df.to_csv('output.csv', sep=out_delimiter, index=False)
        print("transformation complete")
    
    
    if __name__ == '__main__':
        cli()
    

    这就是我将输入和输出分隔符传递到CLI的方式:

    python cli.py data.csv "," "\t"
    

    这是生成的错误:

    Traceback (most recent call last):
      File "cli.py", line 24, in <module>
        cli()
      File "/home/curtis/Program_Files/miniconda3/envs/py36/lib/python3.6/site-packages/click/core.py", line 722, in __call__
        return self.main(*args, **kwargs)
      File "/home/curtis/Program_Files/miniconda3/envs/py36/lib/python3.6/site-packages/click/core.py", line 697, in main
        rv = self.invoke(ctx)
      File "/home/curtis/Program_Files/miniconda3/envs/py36/lib/python3.6/site-packages/click/core.py", line 895, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "/home/curtis/Program_Files/miniconda3/envs/py36/lib/python3.6/site-packages/click/core.py", line 535, in invoke
        return callback(*args, **kwargs)
      File "cli.py", line 19, in cli
        df.to_csv('output.csv', sep=out_delimiter, index=False)
      File "/home/curtis/Program_Files/miniconda3/envs/py36/lib/python3.6/site-packages/pandas/core/frame.py", line 1745, in to_csv
        formatter.save()
      File "/home/curtis/Program_Files/miniconda3/envs/py36/lib/python3.6/site-packages/pandas/io/formats/csvs.py", line 169, in save
        self.writer = UnicodeWriter(f, **writer_kwargs)
      File "/home/curtis/Program_Files/miniconda3/envs/py36/lib/python3.6/site-packages/pandas/io/common.py", line 521, in UnicodeWriter
        return csv.writer(f, dialect=dialect, **kwds)
    TypeError: "delimiter" must be a 1-character string
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Stephen Rauch Afsar Ali    7 年前

    要处理转义字符,可以使用如下回调:

    代码:

    import codecs
    
    def unescape(ctx, param, value):
        return codecs.getdecoder("unicode_escape")(value)[0]
    

    要使用回调,可以执行以下操作:

    @click.argument('escaped', callback=unescape)
    

    这个怎么用

    这将使用 unicode_escape 编解码器。

    ( Source )

    测试代码:

    import click
    
    @click.command()
    @click.argument('escaped', callback=unescape)
    def cli(escaped):
        click.echo('len: {}, ord: {}'.format(len(escaped), ord(escaped)))
    
    
    if __name__ == "__main__":
        commands = (
            r'\t',
            r'\n',
            '\t',
            ',',
            '--help',
        )
    
        import sys, time
    
        time.sleep(1)
        print('Click Version: {}'.format(click.__version__))
        print('Python Version: {}'.format(sys.version))
        for cmd in commands:
            try:
                time.sleep(0.1)
                print('-----------')
                print('> ' + cmd)
                time.sleep(0.1)
                cli(cmd.split())
    
            except BaseException as exc:
                if str(exc) != '0' and \
                        not isinstance(exc, (click.ClickException, SystemExit)):
                    raise
    

    结果:

    Click Version: 6.7
    Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
    -----------
    > \t
    len: 1, ord: 9
    -----------
    > \n
    len: 1, ord: 10
    -----------
    >   
    Usage: test.py [OPTIONS] ESCAPED
    
    Error: Missing argument "escaped".
    -----------
    > ,
    len: 1, ord: 44
    -----------
    > --help
    Usage: test.py [OPTIONS] ESCAPED
    
    Options:
      --help  Show this message and exit.