可以在不插入新行的情况下获得用户输入吗?

时间:2011-08-24 10:20:08

标签: python

我知道我可以通过添加逗号

来停止打印换行
print "Hello, world!",

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

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

结果:

  

您好,Tomas
  ,你好吗?

我想要的结果:

  

你好,托马斯,你好吗?

9 个答案:

答案 0 :(得分:8)

  

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

简而言之:你做不到。

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

如果要阻止这种情况,则必须使用终端控制库,例如curses模块。但这不是可移植的 - 例如,curses在Windows系统上不可用。

答案 1 :(得分:7)

这有点绕过它,但没有为变量name分配任何东西:

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

在打印整个邮件之前,它会提示用户输入名称。

答案 2 :(得分:7)

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

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

其中x是给定用户输入长度的整数,yraw_input()&#39}字符串长度的整数。虽然它可能不适用于所有终端(正如我在学习这种方法时所读到的那样),但它在我的工作正常。我正在使用Kubuntu 14.04。
字符串'\033[4C'用于向右跳转4个索引,因此它等同于' ' * 4。同样,字符串'\033[1A'用于向上跳1行。通过在字符串的最后一个索引上使用字母ABCD,您可以分别向上,向下,向右和向左移动。

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

答案 3 :(得分:4)

如果您不希望它换新行,您可以使用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

答案 4 :(得分:1)

就像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 :(得分:1)

回溯换行符的替代方法是定义您自己的函数,该函数模拟内置的input函数,回显并将每个击键附加到response变量,但 Enter (它将返回响应),同时还处理 Backspace Del Home End ,箭头键,行历史记录,KeyboardInterrupt,EOFError,SIGTSTP和从剪贴板粘贴。它非常简单。

请注意,在Windows上,如果您希望使用带有箭头键的行历史记录(例如通常的pyreadline函数),则需要安装input,尽管它不完整所以功能仍然不是完全正确。此外,如果您不在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 :(得分:0)

正如已经回答的那样,我们无法阻止 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?")

答案 7 :(得分:-1)

我认为您可以简单地使用它。

 name=input("Hello , ")

答案 8 :(得分:-1)

应该是这样的:-

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

print('new line')

输出是这样的:-

this eliminates the new line.