用制表符替换空格缩进

I am looking to replace 4 spaces at the start of a line to tabs, but nothing further when there is text present.

My initial regex of / {4}+/ or /[ ]{4}+/ for the sake of readability clearly worked but obviously any instance found with four spaces would be replaced.

$string  = '        this is some text -->    <-- are these tabs or spaces?';
$string .= "
    and this is another line singly indented";
// I wrote 4 spaces, a tab, then 4 spaces here but unfortunately it will not display
$string .= "
    \t    and this is third line with tabs and spaces";

$pattern = '/[ ]{4}+/';
$replace = "\t";

$new_str = preg_replace( $pattern , $replace , $string );

echo '<pre>'. $new_str .'</pre>';

This was an example of what I had originally, using the regex given the expression works perfectly with regards to the conversion but for the fact that the 4 spaces between the ----><---- are replaced by a tab. I am really looking to have text after indentation unaltered.

My best effort so far has been (^) start of line ([ ]{4}+) the pattern (.*?[;\s]*) anything up til the first non space \s

$pattern = '/^[ ]{4}+.*?[;\s]*/m';

which... almost works but for the fact that the indentation is now lost, can anybody help me understand what I am missing here?

[edit]

For clarity what I am trying to do is change the the start of text indentation from spaces to tabs, I really don't understand why this is confusing to anybody.

To be as clear as possible (using the value of $string above):

First line has 8 spaces at the start, some text with 4 spaces in the middle.
I am looking for 2 tabs at the start and no change to spaces in the text.

Second line has 4 spaces at the start.
I am looking to have only 1 tab at the start of the line.

Third line has 4 spaces, 1 tab and 4 spaces.
I am looking to have 3 tabs at the start of the line.

If you're not a regular expression guru, this will probably make most sense to you and be easier to adapt to similar use cases (this is not the most efficient code, but it's the most "readable" imho):

// replace all regex matches with the result of applying
// a given anonymous function to a $matches array
function tabs2spaces($s_with_spaces) {
    // before anything else, replace existing tabs with 4 spaces
    // to permit homogenous translation
    $s_with_spaces = str_replace("\t", '    ', $s_with_spaces);
    return preg_replace_callback(
        '/^([ ]+)/m',
        function ($ms) {
            // $ms[0] - is full match
            // $ms[1] - is first (...) group fron regex

            // ...here you can add extra logic to handle
            // leading spaces not multiple of 4

            return str_repeat("\t", floor(strlen($ms[1]) / 4));
        },
        $s_with_spaces
    );
}

// example (using dots to make spaces visible for explaining)
$s_with_spaces = <<<EOS
no indent
....4 spaces indent
........8 spaces indent
EOS;
$s_with_spaces = str_replace('.', ' ');
$s_with_tabs = tabs2spaces($s_with_spaces);

If you want a performant but hard to understand or tweak one-liner instead, the solutions in the comments from the regex-gurus above should work :)


P.S. In general preg_replace_callback (and its equivalent in Javascript) is a great "swiss army knife" of structured text processing. I have, shamefully, even writtent parsers to mini-languages using it ;)

The way I would do it is this.

$str = "...";
$pattern = "'/^[ ]{4}+/'";
$replace = "\t"; 

$multiStr = explode("
", $str);
$out = "";
foreach ($multiStr as &$line) {
    $line = str_replace("\t", "    ",$line);
    $out .= preg_replace( $pattern , $replace , $line )
}

$results = implode("
", $out);

Please re-evaluate the code thoroughly as I have done this on a quick and intuitive way.

As I can't run a PHP server to test it :( but should help you resolved this problem.