首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >设置UTF-8输入,用C/C++通过管道从Powershell获得UTF-8输出。

设置UTF-8输入,用C/C++通过管道从Powershell获得UTF-8输出。
EN

Stack Overflow用户
提问于 2021-08-30 18:22:44
回答 1查看 1.1K关注 0票数 3

我无法将正确的utf-8字符串写入powershell子进程。ASCII字符工作,但utf-8字符,例如'ü',将被不同的解释。从同一个powershell子进程读取相同的问题。

总结:我想通过我的程序使用powershell进行utf-8编码.

更新:AllocConsole();分配一个控制台,然后调用SetConsoleCP(CP_UTF8);SetConsoleOutputCP(CP_UTF8);,就像他在回答中提到的那样,如果您有一个没有任何控制台的GUI应用程序,那么它对我是有效的。如果您有一个控制台应用程序,则不必手动分配控制台。

更新2: --如果您有一个GUI,名为AllocConsole(),您可以稍后调用ShowWindow(GetConsoleWindow(), SW_HIDE);来隐藏控制台,正如前面提到的这里

到目前为止,我已经尝试过:

  • 将输入和输出编码设置为进程内的utf-8 $OutputEncoding = [System.Console]::OutputEncoding = [System.Console]::InputEncoding = [System.Text.Encoding]::UTF8
  • 对UTF-16进行同样的操作,以防出现错误,例如...ext.Encoding]::Unicode
  • 对ISO-拉丁文1 (cp1252)也是如此
  • 使用wchar_t作为所有测试编码的缓冲区和输入
  • 测试给定字符串的字节顺序
  • 测试Unicode (每个字符4个字节,而不是2个)
  • 自己一点一点地构建字符串
  • 将编译器标志设置为\D UNICODE

编写代码示例:

代码语言:javascript
运行
复制
std::string test("ls ä\n");
DWORD ret = WriteFile(std_in_write, test.c_str(), test.size(), &number_of_bytes_written, nullptr);
if (ret == 0) {
    throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_WRITE_TO_FILE, GetLastError());
}

产出: ls├

示例代码:

代码语言:javascript
运行
复制
HANDLE std_in_read = nullptr;
HANDLE std_in_write = nullptr;
HANDLE std_out_read = nullptr;
HANDLE std_out_write = nullptr;
SECURITY_ATTRIBUTES security_attr;
STARTUPINFO startup_info;
PROCESS_INFORMATION process_information;
DWORD buffer_size = 1000000;

security_attr = {sizeof(SECURITY_ATTRIBUTES), nullptr, true};

if (!CreatePipe(&std_in_read, &std_in_write, &security_attr, buffer_size)) {
    throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_CREATE_IN_PIPE, GetLastError());
}

if (!CreatePipe(&std_out_read, &std_out_write, &security_attr, buffer_size)) {
    throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_CREATE_OUT_PIPE, GetLastError());
}

GetStartupInfo(&startup_info);
startup_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
startup_info.wShowWindow = SW_HIDE;
startup_info.hStdOutput = std_out_write;
startup_info.hStdError = std_out_write;
startup_info.hStdInput = std_in_read;

if (!CreateProcess(TEXT(default_powershell_path), nullptr, nullptr, nullptr, TRUE, 0, nullptr, TEXT(default_windows_path), &startup_info, &process_information)) {
    throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_CREATE_PROCESS, GetLastError());
}

std::string test("ls ä\n");
DWORD ret = WriteFile(std_in_write, test.c_str(), test.size(), &number_of_bytes_written, nullptr);
if (ret == 0) {
    throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_WRITE_TO_FILE, GetLastError());
}

DWORD dword_read;
while (true) {
    DWORD total_bytes_available;
    if (PeekNamedPipe(std_out_read, nullptr, 0, nullptr, &total_bytes_available, nullptr) == 0) {
        throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_COPY_FROM_PIPE, GetLastError());
    }

    if (total_bytes_available != 0) {
        DWORD minimum = min(buffer_size, total_bytes_available);
        char buf[buffer_size];
        if (ReadFile(std_out_read, buf, minimum, &dword_read, nullptr) == 0) {
            throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_READ_FILE, GetLastError());
        }

        std::string tmp(buf);
        std::cout << tmp << std::endl;
    }

    if (total_bytes_available == 0) {
        break;
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}

注意:没有重复的redirect-input-and-output-of-powershell-exe-to-pipes-in-c,因为代码只适用于ASCII字符,根本不处理utf-8字符。

也没有重复的C-获取-utf-8-从创造过程输出,因为建议的解决方案不能像上面提到的那样工作,我想输入utf-8以及读取utf-8。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-08-30 18:41:56

在通过和< code >C12 WinAPI函数创建PowerShell process之前,需要将控制台的输入和输出代码页设置为 (UTF-8) ,因为PowerShell CLI使用它们来解码其stdin输入并对其stdout输出进行编码。

(相比之下,$OutputEncoding = [System.Console]::OutputEncoding = [System.Console]::InputEncoding = [System.Text.Encoding]::UTF8只在从PowerShell发出外部程序调用时应用内部PowerShell会话。)

注意:如果调用过程本身不是控制台应用程序,那么在调用SetConsoleCPSetConsoleOutputCP之前,您可能必须使用AllocConsole WinAPI函数分配控制台,但坦率地说,我不清楚(a)这是否使该控制台立即可见(这可能是不想要的)和(b) CreateProcess调用是否会自动使用此控制台。

它不能工作,您可以通过cmd.exe调用chcp,然后按照cmd /c "chcp 65001 >NUL & powershell -c ..."的方式调用powershell.exechcp 65001将控制台代码页设置为65001,即UTF-8。

(这会带来额外的开销,但是与cmd.exe进程相比,powershell.exe进程相对较轻,chcp.com也是如此)。

下面是可以从PowerShell运行的示例命令来演示:

代码语言:javascript
运行
复制
& {

  # Save the current code pages.
  $prevInCp, $prevOutCp = [Console]::InputEncoding, [Console]::OutputEncoding

  # Write the UTF-8 encoded form of string 'kö' to a temp. file.
  # Note: In PowerShell (Core) 7+, use -AsByteStream instead of -Encoding Byte
  Set-Content temp1.txt -Encoding Byte ([Text.UTF8Encoding]::new().GetBytes('kö'))

  # Switch to UTF-8, pipe the UTF-8 file's content to PowerShell's stdin,
  # verify that it was decoded correctly, and output it, again encoded as UTF-8.
  cmd /c 'chcp 65001 >NUL & type temp1.txt | powershell -nop -c "$stdinLine = @($input)[0]; $stdinLine -eq ''kö''; Write-Output $stdinLine" > temp2.txt'

  # Read the temporary file as UTF-8 and echo its content.
  Get-Content -Encoding Utf8 temp2.txt

  # Clean up.
  Remove-Item temp[12].txt
  # Restore the original code pages.
  [Console]::InputEncoding = $prevInCp; [Console]::OutputEncoding = $prevOutCp

}

这将输出以下内容,指示powershell调用既正确读取UTF-8编码的输入,又将其输出为UTF-8:

代码语言:javascript
运行
复制
True
ö

注意:

您可以通过使用进程内的 PowerShell SDK来替代创建powershell.exe子进程,从而绕过字符编码问题,尽管我不知道C++有多痛苦。有关C#示例,请参见这个答案

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68988696

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档