在.NET字符串中将制表符转换为空格

11
我正在使用正则表达式构建一个文本解析器。我需要将字符串中的所有制表符转换为空格字符。我不能假设一个制表符应该包含多少个空格,否则我可以将制表符替换为4个空格字符。对于这种类型的问题,有没有好的解决方案?我需要在代码中完成这个任务,所以不能使用外部工具。
很遗憾,这些答案都没有解决我遇到的问题。我正在从外部文本文件中提取文本,无法确定它们是如何创建的,或者使用了哪个操作系统来创建它们。我相信制表符的长度可能会有所不同,所以如果在读取文本文件时遇到制表符,我想知道应该用多少个空格字符来替换它。
请参阅制表位定义 | PCMag以了解制表位的解释。请参阅制表位 - 维基百科以了解历史解释。

2
这里的一些答案并不了解制表位的概念(请参见http://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Stops.html和http://www.jwz.org/doc/tabs-vs-spaces.html)。@ckal,Nick-McCowin和user275640是正确的答案。 - Jonke
@Jonke 发布了一个新的解决方案,可以正确地确定具有4或8个空格的制表位。 - HappyTown
12个回答

15

很遗憾,你需要假设一个制表符代表多少个空格。你应该将其设置为固定值(比如提到的四个空格)或者让用户可以选择。

在.NET中最快的方法是(我使用的是C#):

var NewString = "This is a string with a    Tab";
var TabLength = 4;
var TabSpace = new String(' ', TabLength);

NewString = NewString.Replace("\t", TabSpace);

你可以更改TabLength变量为任何你想要的值,通常就像之前提到的那样,四个空格字符。
在所有操作系统中,制表符的长度都是相同的,一个制表符!不同的是软件显示它们的方式,通常这相当于四个空格字符的等宽,这也假定显示使用固定宽度字体,如Courier New
例如,我选择的IDE允许我更改制表符字符的宽度,以适合我的需要。

2
制表符占据高达TabSpace个字符,不一定是准确的那么多个字符。 - Joel Coehoorn
@JoelCoehoorn发布了一个新的解决方案,可以正确地确定具有4或8个空格的制表位。 - HappyTown
1
这不是一个解决方案,因为它没有考虑到那些没有对齐制表位的制表符(请参见@HappyTown的答案)。假设制表符宽度为4,“aa\tb”变成“aa____b”,而不是“aa__b”。 - Sprotty

7
我不确定Unix文本文件中的选项卡将如何读取,或者您使用的各种格式是什么,但对于内联文本,这个方法是可行的。也许这会有所帮助。
var textWithTabs = "some\tvalues\tseperated\twith\ttabs";
var textWithSpaces = string.Empty;

var textValues = textWithTabs.Split('\t');

foreach (var val in textValues)
{
    textWithSpaces += val + new string(' ', 8 - val.Length % 8);
}

Console.WriteLine(textWithTabs);
Console.WriteLine(textWithSpaces);
Console.Read();

这个概念看起来还不错,但是在大字符串上使用 += 会非常慢。 - Sprotty

4

我认为你想表达的意思是,你想用它们扩展到的有效空格数量替换标签。我首先想到的方法不涉及正则表达式(而且我不知道这个问题是否可以用它们解决)。

  • 逐个字符地遍历字符串,并跟踪您在字符串中的当前位置。
  • 当您找到一个制表符时,请将其替换为N个空格,其中N = tab_length - (current_position%tab_length)
  • 将N添加到您的当前位置并继续遍历字符串。

3

(如果您想知道如何在编辑器中将制表符转换为空格,请查看我的回答末尾。)

最近有要求我将制表符替换为空格。

这个解决方案会用空格替换制表符,每个制表符会被替换成最多 4 或 8 个空格。

该逻辑逐个字符地迭代输入字符串,并跟踪输出字符串中的当前位置(列#)。

  • 如果它遇到\t(制表符),则找到下一个制表位,计算它需要多少空格才能到达下一个制表位,并用这些空格替换\t
  • 如果是\n(新行)- 将其附加到输出字符串并在新行上将位置指针重置为1。 Windows上的新行为\r\n,而Unix(或其他版本)使用\n,因此我认为这对两个平台都适用。 我已在Windows上测试过,但没有Unix系统。
  • 任何其他字符- 将其附加到输出字符串并增加位置。

.

using System.Text;

namespace CSharpScratchPad
{
    class TabToSpaceConvertor
    {
        static int GetNearestTabStop(int currentPosition, int tabLength)
        {
            // If already at the tab stop, jump to the next tab stop.
            if ((currentPosition % tabLength) == 1)
                currentPosition += tabLength;
            else
            {
                // If in the middle of two tab stops, move forward to the nearest.
                for (int i = 0; i < tabLength; i++, currentPosition++)
                    if ((currentPosition % tabLength) == 1)
                        break;
            }

            return currentPosition;
        }

        public static string Process(string input, int tabLength)
        {
            if (string.IsNullOrEmpty(input))
                return input;

            StringBuilder output = new StringBuilder();

            int positionInOutput = 1;
            foreach (var c in input)
            {
                switch (c)
                {
                    case '\t':
                        int spacesToAdd = GetNearestTabStop(positionInOutput, tabLength) - positionInOutput;
                        output.Append(new string(' ', spacesToAdd));
                        positionInOutput += spacesToAdd;
                        break;

                    case '\n':
                        output.Append(c);
                        positionInOutput = 1;
                        break;

                    default:
                        output.Append(c);
                        positionInOutput++;
                        break;
                }
            }
            return output.ToString();
        }
    }
}

调用代码应该是这样的:
string input = "I\tlove\tYosemite\tNational\tPark\t\t,\t\t\tGrand Canyon,\n\t\tand\tZion";
string output = CSharpScratchPad.TabToSpaceConvertor.Process(input, 4);

输出字符串将获得以下值:
    I   love    Yosemite    National    Park        ,           Grand Canyon,
            and Zion

如何在编辑器中将制表符转换为空格?

如果您因为找不到编辑器中将制表符转换为空格的选项而遇到了这个问题(就像我一样,想要编写自己的工具来完成它),这里是不同编辑器中该选项的位置 -

Notepad++:              Edit → Blank Operations → TAB to Space
Visual Studio:          Edit → Advanced → Untabify Selected Lines
SQL Management Studio:  Edit → Advanced → Untabify Selected Lines

由于 Stackoverflow 的格式混乱,我无法将代码片段的第一行 using System.Text; 添加到其中。 - HappyTown
1
代码示例前的最后一段文字是一个列表。列表使用与代码示例相同的缩进来标记同一列表项元素中的连续段落。我通常使用单个句点(.)来“重置”列表,然后代码将正确显示。 - Joel Coehoorn
@JoelCoehoorn 感谢您修复它并展示如何修复它。 - HappyTown
终于有人真正把制表符转换为空格的想法理解了!!\o/ - Konamiman

2

我不确定我的解决方案是否在执行效率上更高,但它的代码更加紧凑。这与用户ckal的解决方案相似,但使用了Join函数重新组合分割的字符串,而不是使用'+='。

public static string ExpandTabs(string input, int tabLength)
{
    string[] parts = input.Split('\t');
    int count = 0;
    int maxpart = parts.Count() - 1;
    foreach (string part in parts)
    {
        if (count < maxpart)
            parts[count] = part + new string(' ', tabLength - (part.Length % tabLength));
        count++;
    }
    return(string.Join("", parts));
}

1

这正是他们需要的。我在Visual Basic 6.0中编写了这个程序。我进行了一些快速的VB.NET 2010更新,但它还需要更好的修复。只需确保设置所需的制表符宽度;它已经设置为8。只需将字符串发送到它,甚至可以像这样直接在文本框内进行修复:

RichTextBox1.Text = strFixTab(RichTextBox1.Text)

Function strFixTab(ByVal TheStr As String) As String
    Dim c As Integer
    Dim i As Integer
    Dim T As Integer
    Dim RetStr As String
    Dim ch As String
    Dim TabWidth as Integer = 8    ' Set the desired tab width

    c = 1
    For i = 1 To TheStr.Length
        ch = Mid(TheStr, i, 1)
        If ch = vbTab Then
            T = (TabWidth + 1) - (c Mod TabWidth)
            If T = TabWidth + 1 Then T = 1
            RetStr &= Space(T)
            c += T - 1
        Else
            RetStr &= ch
        End If
        If ch = vbCr Or ch = vbLf Then
            c = 1
        Else
            c += 1
        End If
    Next
    Return RetStr
End Function

0

这里有很多答案都忽略了一个制表符意味着“到下一个制表位的空格数”,而不是“四个(或八个)空格”。很多答案也忽略了回车和换行符,因此无法处理多行内容。所以,话不多说:

    public static string TabsToSpaces(string inTxt, int tabLen=4 )
    {
        var outTxt = new List<string>();

        var textValues = inTxt.Split('\t');

        foreach (var val in textValues)
        {
            var lines = val.Split("\r");
            var preTxt = lines[lines.Length - 1];
            preTxt = preTxt.Replace("\n", "");
            var numSpaces = tabLen - preTxt.Length % tabLen;
            if (numSpaces == 0)
                numSpaces = tabLen;
            outTxt.Add(val + new string(' ', numSpaces));
        }
        return String.Join("", outTxt);
    }

(顺便说一下,这种方法在 CPU 效率方面也很高,因为它不会重新复制大字符串。)

0
你可以使用replace函数:
char tabs = '\u0009';
String newLine = withTabs.Replace(tabs.ToString(), "    ");

听起来他希望结果仍然能够在制表位上对齐。 - Joel Coehoorn
由于没有考虑到未对齐制表位的制表符(请参见@HappyTown的答案),因此它无法正常工作。假设制表符宽度为4,“aa\tb”将变成“aa____b”,而不是“aa__b”。 - Sprotty

-1
你想把制表符转换为N个空格吗?一个快速而简单的选项是:
output = input.Replace("\t", "".PadRight(N, (char)" "));

显然,N必须在程序中的某个地方定义,可以是用户输入,也可以是其他地方。


-1
Regex.Replace(input, "\t", "    ");

由于没有考虑到未对齐制表符(请参见@HappyTown的答案),因此无法正常工作。 - Sprotty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接