【问题标题】:Possible to get user input without inserting a new line?可以在不插入新行的情况下获得用户输入吗?
【发布时间】:2011-11-02 16:24:03
【问题描述】:

我知道我可以通过添加逗号来阻止 print 写入换行符

print "Hello, world!",

但是如何阻止raw_input 写换行符?

print "Hello, ",
name = raw_input()
print ", how do you do?"

结果:

你好,托马斯
,你好吗?

我想要的结果:

你好,托马斯,你好吗?

【问题讨论】:

  • 作为记录,如果raw_input 中的换行是由于用户单击了返回键,那么这就是我的预期。我仍然需要一种方法来避免它:-)

标签: python


【解决方案1】:

但是如何阻止 raw_input 写入换行符?

简而言之:你不能。

raw_input() 将始终回显用户输入的文本,包括尾随的换行符。这意味着用户输入的任何内容都将打印到标准输出。

如果您想防止这种情况发生,您将不得不使用终端控制库,例如curses 模块。但是,这不是可移植的——例如,curses 在 Windows 系统上不可用。

【讨论】:

  • raw_input() 根据自己的文档字符串去除尾随的换行符。
  • @Wieland H.:但它仍会回显它,例如将其写入标准输出。这就是我所说的。返回值将删除换行符,但这与这个问题无关。投票前阅读。
【解决方案2】:

这在某种程度上规避了它,但没有将任何东西分配给变量name

print("Hello, {0}, how do you do?".format(raw_input("Enter name here: ")))

它会在打印整个消息之前提示用户输入名称。

【讨论】:

    【解决方案3】:

    我看到没有人给出可行的解决方案,所以我决定试一试。 正如Ferdinand Beyer 所说,不可能让raw_input() 在用户输入后不打印新行。但是,有可能回到您之前的线路。 我把它做成了单线。您可以使用:

    print '\033[{}C\033[1A'.format(len(x) + y),
    

    其中x 是给定用户输入长度的整数,yraw_input() 字符串长度的整数。尽管它可能不适用于所有终端(正如我在了解此方法时所读到的),但它在我的终端上运行良好。我正在使用 Kubuntu 14.04。
    字符串'\033[4C' 用于向右跳转4 个索引,因此它相当于' ' * 4。同理,字符串'\033[1A'用于向上跳转1行。通过在字符串的最后一个索引上使用字母ABCD,您可以分别向上、向下、向右和向左。

    请注意,如果有的话,排队将删除该位置的现有打印字符。

    【讨论】:

      【解决方案4】:

      就像 Nick K. 所说,您需要将文本光标移回回显换行符之前。问题是你不能轻易得到前一行的长度以便向右移动,以免将打印、提示和输入的每个字符串存储在自己的变量中。

      下面是一个类(用于 Python 3),它通过自动存储终端的最后一行来解决这个问题(前提是您使用它的方法)。与使用终端控制库相比,这样做的好处是它可以在最新版本的 Windows 和 *NIX 操作系统的标准终端中工作。它还会在输入之前打印“Hello,”提示。

      如果您使用的是 Windows 但不是 Windows 10 的 v1511,那么您需要安装 colorama 模块,否则这将不起作用,因为他们在该版本中提供了 ANSI 光标移动支持。

      # For the sys.stdout file-like object
      import sys
      import platform
      
      if platform.system() == 'Windows':
          try:
              import colorama
          except ImportError:
              import ctypes
              kernel32 = ctypes.windll.kernel32
              # Enable ANSI support on Windows 10 v1511
              kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
          else:
              colorama.init()
      else:
          # Fix Linux arrow key support in Python scripts
          import readline
      
      
      class TempHistory:
          """Record one line from the terminal.
      
          It is necessary to keep track of the last line on the terminal so we
          can move the text cursor rightward and upward back into the position
          before the newline from the `input` function was echoed.
      
          Note: I use the term 'echo' to refer to when text is
          shown on the terminal but might not be written to `sys.stdout`.
      
          """
      
          def __init__(self):
              """Initialise `line` and save the `print` and `input` functions.
      
              `line` is initially set to '\n' so that the `record` method
              doesn't raise an error about the string index being out of range.
      
              """
              self.line = '\n'
              self.builtin_print = print
              self.builtin_input = input
      
          def _record(self, text):
              """Append to `line` or overwrite it if it has ended."""
              if text == '':
                  # You can't record nothing
                  return
              # Take into account `text` being multiple lines
              lines = text.split('\n')
              if text[-1] == '\n':
                  last_line = lines[-2] + '\n'
                  # If `text` ended with a newline, then `text.split('\n')[-1]`
                  # would have merely returned the newline, and not the text
                  # preceding it
              else:
                  last_line = lines[-1]
              # Take into account return characters which overwrite the line
              last_line = last_line.split('\r')[-1]
              # `line` is considered ended if it ends with a newline character
              if self.line[-1] == '\n':
                  self.line = last_line
              else:
                  self.line += last_line
      
          def _undo_newline(self):
              """Move text cursor back to its position before echoing newline.
      
              ANSI escape sequence: `\x1b[{count}{command}`
              `\x1b` is the escape code, and commands `A`, `B`, `C` and `D` are
              for moving the text cursor up, down, forward and backward {count}
              times respectively.
      
              Thus, after having echoed a newline, the final statement tells
              the terminal to move the text cursor forward to be inline with
              the end of the previous line, and then move up into said line
              (making it the current line again).
      
              """
              line_length = len(self.line)
              # Take into account (multiple) backspaces which would
              # otherwise artificially increase `line_length`
              for i, char in enumerate(self.line[1:]):
                  if char == '\b' and self.line[i-1] != '\b':
                      line_length -= 2
              self.print('\x1b[{}C\x1b[1A'.format(line_length),
                         end='', flush=True, record=False)
      
          def print(self, *args, sep=' ', end='\n', file=sys.stdout, flush=False,
                    record=True):
              """Print to `file` and record the printed text.
      
              Other than recording the printed text, it behaves exactly like
              the built-in `print` function.
      
              """
              self.builtin_print(*args, sep=sep, end=end, file=file, flush=flush)
              if record:
                  text = sep.join([str(arg) for arg in args]) + end
                  self._record(text)
      
          def input(self, prompt='', newline=True, record=True):
              """Return one line of user input and record the echoed text.
      
              Other than storing the echoed text and optionally stripping the
              echoed newline, it behaves exactly like the built-in `input`
              function.
      
              """
              if prompt == '':
                  # Prevent arrow key overwriting previously printed text by
                  # ensuring the built-in `input` function's `prompt` argument
                  # isn't empty
                  prompt = ' \b'
              response = self.builtin_input(prompt)
              if record:
                  self._record(prompt)
                  self._record(response)
              if not newline:
                  self._undo_newline()
              return response
      
      
      record = TempHistory()
      # For convenience
      print = record.print
      input = record.input
      
      print('Hello, ', end='', flush=True)
      name = input(newline=False)
      print(', how do you do?)
      

      【讨论】:

        【解决方案5】:

        回溯换行符的另一种方法是定义您自己的函数,该函数模拟内置的 input 函数,回显并附加每个击键到 response 变量,除了 Enter (这将返回response),同时还处理 BackspaceDelHomeEnd、箭头键、行历史记录、KeyboardInterrupt、 EOFError、SIGTSTP 和从剪贴板粘贴。这非常简单。

        请注意,在 Windows 上,如果您想像通常的 input 功能一样使用箭头键使用行历史记录,则需要安装 pyreadline,尽管它不完整,因此功能仍然不太正确。此外,如果您未使用 v1511 或更高版本的 Windows 10,则需要安装 colorama 模块(如果您使用的是 Linux 或 macOS,则无需执行任何操作)。

        另外,由于msvcrt.getwch 使用“\xe0”表示特殊字符,您将无法输入“à”。您应该可以粘贴它。

        以下是使这项工作在更新 Windows 10 系统(至少 v1511)、基于 Debian 的 Linux 发行版以及可能的 macOS 和其他 *NIX 操作系统上运行的代码。无论您是否在 Windows 上安装了pyreadline,它也应该可以工作,尽管它会缺少一些功能。

        windows_specific.py:

        """Windows-specific functions and variables for input_no_newline."""
        import ctypes
        from msvcrt import getwch  # pylint: disable=import-error, unused-import
        from shared_stuff import ANSI
        
        try:
            import colorama  # pylint: disable=import-error
        except ImportError:
            kernel32 = ctypes.windll.kernel32
            # Enable ANSI support to move the text cursor
            kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
        else:
            colorama.init()
        
        
        def get_clipboard_data():
            """Return string previously copied from Windows clipboard.
        
            Adapted from <http://stackoverflow.com/a/23285159/6379747>.
        
            """
            CF_TEXT = 1
            user32 = ctypes.windll.user32
            user32.OpenClipboard(0)
            try:
                if user32.IsClipboardFormatAvailable(CF_TEXT):
                    data = user32.GetClipboardData(CF_TEXT)
                    data_locked = kernel32.GlobalLock(data)
                    text = ctypes.c_char_p(data_locked)
                    kernel32.GlobalUnlock(data_locked)
            finally:
                user32.CloseClipboard()
            return text.value
        
        
        def sigtstp():
            """Raise EOFError from Ctrl-Z since SIGTSTP doesn't exist on Windows."""
            raise EOFError
        
        
        input_code = {
            **ANSI,
            'CSI': [['\xe0', '\x00'], ''],
            'up': 'H',
            'down': 'P',
            'right': 'M',
            'left': 'K',
            'end': 'O',
            'home': 'G',
            'backspace': '\b',
            'del': 'S',
        }
        

        unix_specific.py:

        """Functions and variables for Debian-based Linux distros and macOS."""
        import sys
        import os
        import tty
        import signal
        import termios
        from shared_stuff import ANSI
        
        def getwch():
            """Return a single character from user input without echoing.
        
            ActiveState code, adapted from
            <http://code.activestate.com/recipes/134892> by Danny Yoo under
            the Python Software Foundation license.
        
            """
            file_descriptor = sys.stdin.fileno()
            old_settings = termios.tcgetattr(file_descriptor)
            try:
                tty.setraw(file_descriptor)
                char = sys.stdin.read(1)
            finally:
                termios.tcsetattr(file_descriptor, termios.TCSADRAIN, old_settings)
            return char
        
        
        def get_clipboard_data():
            """Return nothing; *NIX systems automagically change sys.stdin."""
            return ''
        
        
        def sigtstp():
            """Suspend the script."""
            os.kill(os.getpid(), signal.SIGTSTP)
        
        
        input_code = {
            **ANSI,
            'CSI': ['\x1b', '['],
            'backspace': '\x7f',
            'del': ['3', '~'],
        }
        

        readline_available.py:

        """Provide functions for up and down arrows if readline is installed.
        
        Basically to prevent duplicate code and make it work on systems without
        readline.
        
        """
        try:
            import readline
        except ImportError:
            import pyreadline as readline
        from shared_stuff import move_cursor
        
        
        def init_history_index():
            """Return index for last element of readline.get_history_item."""
            # readline.get_history_item is one-based
            return readline.get_current_history_length() + 1
        
        
        def restore_history(history_index, replaced, cursor_position):
            """Replace 'replaced' with history and return the replacement."""
            try:
                replacement = readline.get_history_item(history_index)
            except IndexError:
                replacement = None
            if replacement is not None:
                move_cursor('right', len(replaced) - cursor_position)
                print('\b \b' * len(replaced), end='', flush=True)
                print(replacement, end='', flush=True)
                return replacement
            return replaced
        
        
        def store_and_replace_history(history_index, replacement, old_history):
            """Store history and then replace it."""
            old_history[history_index] = readline.get_history_item(history_index)
            try:
                readline.replace_history_item(history_index - 1, replacement)
            except AttributeError:
            # pyreadline is incomplete
                pass
        
        
        def handle_prev_history(history_index, replaced, old_history,
                                input_replaced, history_modified):
            """Handle some up-arrow logic."""
            try:
                history = readline.get_history_item(history_index - 1)
            except IndexError:
                history = None
            if history is not None:
                if history_index > readline.get_current_history_length():
                    readline.add_history(replaced)
                    input_replaced = True
                else:
                    store_and_replace_history(
                        history_index, replaced, old_history)
                    history_modified = True
                history_index -= 1
            return (history_index, input_replaced, history_modified)
        
        
        def handle_next_history(history_index, replaced, old_history,
                                input_replaced, history_modified):
            """Handle some down-arrow logic."""
            try:
                history = readline.get_history_item(history_index + 1)
            except IndexError:
                history = None
            if history is not None:
                store_and_replace_history(history_index, replaced, old_history)
                history_modified = True
                history_index += 1
                input_replaced = (not history_index
                                    == readline.get_current_history_length())
            return (history_index, input_replaced, history_modified)
        
        
        def finalise_history(history_index, response, old_history,
                             input_replaced, history_modified):
            """Change history before the response will be returned elsewhere."""
            try:
                if input_replaced:
                    readline.remove_history_item(history_index - 1)
                elif history_modified:
                    readline.remove_history_item(history_index - 1)
                    readline.add_history(old_history[history_index - 1])
            except AttributeError:
            # pyreadline is also missing remove_history_item
                pass
            readline.add_history(response)
        

        readline_unavailable.py:

        """Provide dummy functions for if readline isn't available."""
        # pylint: disable-msg=unused-argument
        
        
        def init_history_index():
            """Return an index of 1 which probably won't ever change."""
            return 1
        
        
        def restore_history(history_index, replaced, cursor_position):
            """Return the replaced thing without replacing it."""
            return replaced
        
        
        def store_and_replace_history(history_index, replacement, old_history):
            """Don't store history."""
            pass
        
        
        def handle_prev_history(history_index, replaced, old_history,
                                input_replaced, history_modified):
            """Return 'input_replaced' and 'history_modified' without change."""
            return (history_index, input_replaced, history_modified)
        
        
        def handle_next_history(history_index, replaced, old_history,
                                input_replaced, history_modified):
            """Also return 'input_replaced' and 'history_modified'."""
            return (history_index, input_replaced, history_modified)
        
        
        def finalise_history(history_index, response, old_history,
                             input_replaced, history_modified):
            """Don't change nonexistent history."""
            pass
        

        shared_stuff.py:

        """Provide platform-independent functions and variables."""
        ANSI = {
            'CSI': '\x1b[',
            'up': 'A',
            'down': 'B',
            'right': 'C',
            'left': 'D',
            'end': 'F',
            'home': 'H',
            'enter': '\r',
            '^C': '\x03',
            '^D': '\x04',
            '^V': '\x16',
            '^Z': '\x1a',
        }
        
        
        def move_cursor(direction, count=1):
            """Move the text cursor 'count' times in the specified direction."""
            if direction not in ['up', 'down', 'right', 'left']:
                raise ValueError("direction should be either 'up', 'down', 'right' "
                                 "or 'left'")
            # A 'count' of zero still moves the cursor, so this needs to be
            # tested for.
            if count != 0:
                print(ANSI['CSI'] + str(count) + ANSI[direction], end='', flush=True)
        
        
        def line_insert(text, extra=''):
            """Insert text between terminal line and reposition cursor."""
            if not extra:
            # It's not guaranteed that the new line will completely overshadow
            # the old one if there is no extra. Maybe something was 'deleted'?
                move_cursor('right', len(text) + 1)
                print('\b \b' * (len(text)+1), end='', flush=True)
            print(extra + text, end='', flush=True)
            move_cursor('left', len(text))
        

        最后,在input_no_newline.py

        #!/usr/bin/python3
        """Provide an input function that doesn't echo a newline."""
        try:
        from windows_specific import getwch, get_clipboard_data, sigtstp, input_code
        except ImportError:
            from unix_specific import getwch, get_clipboard_data, sigtstp, input_code
        try:
            from readline_available import (init_history_index, restore_history,
                                            store_and_replace_history,
                                            handle_prev_history, handle_next_history,
                                            finalise_history)
        except ImportError:
            from readline_unavailable import (init_history_index, restore_history,
                                              store_and_replace_history,
                                              handle_prev_history, handle_next_history,
                                              finalise_history)
        from shared_stuff import ANSI, move_cursor, line_insert
        
        
        def input_no_newline(prompt=''):  # pylint: disable=too-many-branches, too-many-statements
            """Echo and return user input, except for the newline."""
            print(prompt, end='', flush=True)
            response = ''
            position = 0
            history_index = init_history_index()
            input_replaced = False
            history_modified = False
            replacements = {}
        
            while True:
                char = getwch()
                if char in input_code['CSI'][0]:
                    char = getwch()
                    # Relevant input codes are made of two to four characters
                    if char == input_code['CSI'][1]:
                        # *NIX uses at least three characters, only the third is
                        # important
                        char = getwch()
                    if char == input_code['up']:
                        (history_index, input_replaced, history_modified) = (
                            handle_prev_history(
                                history_index, response, replacements, input_replaced,
                                history_modified))
                        response = restore_history(history_index, response, position)
                        position = len(response)
                    elif char == input_code['down']:
                        (history_index, input_replaced, history_modified) = (
                            handle_next_history(
                                history_index, response, replacements, input_replaced,
                                history_modified))
                        response = restore_history(history_index, response, position)
                        position = len(response)
                    elif char == input_code['right'] and position < len(response):
                        move_cursor('right')
                        position += 1
                    elif char == input_code['left'] and position > 0:
                        move_cursor('left')
                        position -= 1
                    elif char == input_code['end']:
                        move_cursor('right', len(response) - position)
                        position = len(response)
                    elif char == input_code['home']:
                        move_cursor('left', position)
                        position = 0
                    elif char == input_code['del'][0]:
                        if ''.join(input_code['del']) == '3~':
                            # *NIX uses '\x1b[3~' as its del key code, but only
                            # '\x1b[3' has currently been read from sys.stdin
                            getwch()
                        backlog = response[position+1 :]
                        response = response[:position] + backlog
                        line_insert(backlog)
                elif char == input_code['backspace']:
                    if position > 0:
                        backlog = response[position:]
                        response = response[: position-1] + backlog
                        print('\b', end='', flush=True)
                        position -= 1
                        line_insert(backlog)
                elif char == input_code['^C']:
                    raise KeyboardInterrupt
                elif char == input_code['^D']:
                    raise EOFError
                elif char == input_code['^V']:
                    paste = get_clipboard_data()
                    backlog = response[position:]
                    response = response[:position] + paste + backlog
                    position += len(paste)
                    line_insert(backlog, extra=paste)
                elif char == input_code['^Z']:
                    sigtstp()
                elif char == input_code['enter']:
                    finalise_history(history_index, response, replacements,
                                     input_replaced, history_modified)
                    move_cursor('right', len(response) - position)
                    return response
                else:
                    backlog = response[position:]
                    response = response[:position] + char + backlog
                    position += 1
                    line_insert(backlog, extra=char)
        
        
        def main():
            """Called if script isn't imported."""
            # "print(text, end='')" is equivalent to "print text,", and 'flush'
            # forces the text to appear, even if the line isn't terminated with
            # a '\n'
            print('Hello, ', end='', flush=True)
            name = input_no_newline()  # pylint: disable=unused-variable
            print(', how do you do?')
        
        
        if __name__ == '__main__':
            main()
        

        正如您所看到的,因为您需要处理不同的操作系统并且基本上用 Python 而不是 C 重新实现内置函数,所以工作量并不大。我建议您只使用我在另一个答案中制作的更简单的TempHistory 类,将所有复杂的逻辑处理留给了内置函数。

        【讨论】:

          【解决方案6】:

          如果不想换行,可以用getpass代替raw_input

          import sys, getpass
          
          def raw_input2(value="",end=""):
              sys.stdout.write(value)
              data = getpass.getpass("")
              sys.stdout.write(data)
              sys.stdout.write(end)
              return data
          

          【讨论】:

            【解决方案7】:

            我认为你可以使用这个:

             name = input("Hello , ")
            

            【讨论】:

            • 这只是从用户那里获取输入。您没有解决最重要的部分,即在同一行的输入 之后打印一些内容...
            【解决方案8】:

            应该是这样的:-

            print('this eliminates the ', end=' ')
            
            print('new line')
            

            输出是这样的:-

            this eliminates the new line.
            

            【讨论】:

            • 这不接受用户的任何输入,因此并没有真正回答问题......
            【解决方案9】:

            正如已经回答的那样,我们不能阻止input() 写一个换行符。尽管它可能无法满足您的期望,但以下代码以某种方式满足条件 if -

            您没有任何问题清屏

            import os
            name = input("Hello, ")
            os.system("cls") # on linux or max, use "cls"
            print(f"Hello, {name}, how do you do?")
            

            或者使用gui对话框没有问题,因为在接受用户输入后对话框会消失,你会看到你所期望的

            import easygui
            name = easygui.enterbox("Hello, ", "User Name")
            print("Hello, "+name+", how do you do?")
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-01-13
              • 2017-06-12
              • 1970-01-01
              • 2011-02-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多